# (c) cavaliba.com - tests / log2 - ECS JSON and text formatters

import datetime
import json
import logging
import sys

from django.test import SimpleTestCase

from app_home.logformat import EcsJsonFormatter, TextFormatter

EVENT = {
    "id": 7,
    "created": datetime.datetime(2026, 10, 18, 14, 2, 11, 482000, tzinfo=datetime.UTC),
    "level": "INFO",
    "action": "instance.update",
    "outcome": "success",
    "reason": "",
    "domain": "data",
    "channel": "web",
    "actor": "alice",
    "actor_effective": "bob",
    "client_ip": "10.0.0.7",
    "request_id": "rid1",
    "target_type": "server",
    "target_id": "web01",
    "count": 3,
    "duration_ms": 42,
    "message": "",
    "extra": {"k": "v"},
}


def _record(name, level=logging.INFO, msg="hello", **attrs):
    record = logging.LogRecord(name, level, __file__, 10, msg, (), None, func="fn")
    for key, value in attrs.items():
        setattr(record, key, value)
    return record


class TestLog2Formatters(SimpleTestCase):
    def test_json_event_is_ecs(self):
        doc = json.loads(EcsJsonFormatter().format(_record("cavaliba.event", event=EVENT)))
        self.assertEqual(doc["@timestamp"], "2026-10-18T14:02:11.482Z")
        self.assertEqual(doc["log.level"], "info")
        self.assertEqual(doc["event.kind"], "event")
        self.assertEqual(doc["event.action"], "instance.update")
        self.assertEqual(doc["event.outcome"], "success")
        self.assertEqual(doc["event.module"], "data")
        self.assertEqual(doc["event.id"], 7)
        self.assertEqual(doc["user.name"], "alice")
        self.assertEqual(doc["user.effective.name"], "bob")
        self.assertEqual(doc["client.ip"], "10.0.0.7")
        self.assertEqual(doc["trace.id"], "rid1")
        self.assertEqual(doc["event.duration"], 42_000_000)
        self.assertEqual(doc["labels.target_id"], "web01")
        self.assertEqual(doc["labels.count"], 3)
        self.assertEqual(doc["labels.extra.k"], "v")
        self.assertEqual(doc["labels.stream"], "event")
        self.assertNotIn("event.reason", doc)

    def test_json_non_event_records_by_stream(self):
        debug = json.loads(EcsJsonFormatter().format(
            _record("cavaliba.debug.app_data.loader", logging.DEBUG, ambient={"actor": "alice"})))  # fmt: skip
        self.assertEqual(debug["labels.stream"], "debug")
        self.assertEqual(debug["user.name"], "alice")
        self.assertEqual(debug["log.origin.function"], "fn")
        self.assertNotIn("event.kind", debug)
        system = json.loads(EcsJsonFormatter().format(_record("django.request", logging.WARNING)))
        self.assertEqual(system["labels.stream"], "system")
        self.assertEqual(system["log.logger"], "django.request")
        self.assertEqual(system["message"], "hello")

    def test_json_exception(self):
        try:
            raise ValueError("boom")
        except ValueError:
            record = _record("django.request", logging.ERROR, exc_info=sys.exc_info())
        doc = json.loads(EcsJsonFormatter().format(record))
        self.assertEqual(doc["error.type"], "ValueError")
        self.assertIn("boom", doc["error.stack_trace"])

    def test_text_event_line(self):
        line = TextFormatter().format(_record("cavaliba.event", event=EVENT))
        self.assertIn("INFO event web alice(bob) instance.update success server:web01", line)
        self.assertIn("count=3", line)
        self.assertIn("42ms", line)

    def test_text_message_capped(self):
        event = {**EVENT, "message": "x" * 500}
        line = TextFormatter().format(_record("cavaliba.event", event=event))
        self.assertIn("x" * 197 + "...", line)
        self.assertNotIn("x" * 201, line)

    def test_text_non_event_line(self):
        line = TextFormatter().format(
            _record("cavaliba.debug.mod", logging.DEBUG, ambient={"channel": "cli"}))  # fmt: skip
        self.assertIn("DEBUG debug cli cavaliba.debug.mod:10 hello", line)

    def test_json_nested_extra_values_become_strings(self):
        event = {
            **EVENT,
            "extra": {"job": {"a": 1}, "sent": ["a", "b"], "rows": [{"x": 1}], "n": 3},
        }
        doc = json.loads(EcsJsonFormatter().format(_record("cavaliba.event", event=event)))
        self.assertEqual(doc["labels.extra.job"], '{"a": 1}')
        self.assertEqual(doc["labels.extra.sent"], ["a", "b"])
        self.assertEqual(doc["labels.extra.rows"], '[{"x": 1}]')
        self.assertEqual(doc["labels.extra.n"], 3)
