# (c) cavaliba.com - tests / log2 - retained events: outcome, level, target, truncation

import logging

from django.test import TestCase

import app_home.cache as cache
from app_data.data import Instance
from app_home.log import WARNING, log2
from app_home.models import CavalibaEvent
from tests import helper


class TestLog2Events(TestCase):
    def setUp(self):
        cache.clear()
        log2.reset(channel="web", origin="unittest", domain="data", actor="alice")

    def _last(self):
        return CavalibaEvent.objects.order_by("-id").first()

    def test_methods_set_outcome_and_default_level(self):
        expected = {
            "access": ("success", "DEBUG"),
            "ok": ("success", "INFO"),
            "fail": ("failure", "ERROR"),
            "deny": ("denied", "WARNING"),
        }
        for method, (outcome, level) in expected.items():
            getattr(log2, method)(f"test.{method}")
            event = self._last()
            self.assertEqual(event.action, f"test.{method}")
            self.assertEqual(event.outcome, outcome)
            self.assertEqual(event.level, level)
            self.assertEqual(event.severity, logging.getLevelName(level))

    def test_level_override_accepts_int_and_name(self):
        log2.fail("test.int", level=WARNING)
        self.assertEqual(self._last().level, "WARNING")
        log2.fail("test.name", level="info")
        self.assertEqual(self._last().level, "INFO")

    def test_ambient_and_call_fields_stored(self):
        log2.ok("test.fields", reason="r1", count=3, message="hello", extra={"k": "v"},
                duration_ms=12, category="data")  # fmt: skip
        event = self._last()
        self.assertEqual(event.channel, "web")
        self.assertEqual(event.origin, "unittest")
        self.assertEqual(event.domain, "data")
        self.assertEqual(event.actor, "alice")
        self.assertEqual(event.reason, "r1")
        self.assertEqual(event.count, 3)
        self.assertEqual(event.message, "hello")
        self.assertEqual(event.extra, {"k": "v"})
        self.assertEqual(event.duration_ms, 12)
        self.assertEqual(event.category, "data")
        self.assertTrue(event.request_id)

    def test_domain_argument_overrides_ambient(self):
        log2.ok("test.domain", domain="cmt")
        self.assertEqual(self._last().domain, "cmt")

    def test_target_from_tuple(self):
        log2.ok("test.target", target=("server", "web01"))
        event = self._last()
        self.assertEqual((event.target_type, event.target_id), ("server", "web01"))

    def test_target_from_instance(self):
        helper.add_schema(classname="logtarget")
        helper.add_instance(classname="logtarget", keyname="obj01")
        instance = Instance.from_keyname(classname="logtarget", keyname="obj01")
        log2.ok("test.target", target=instance)
        event = self._last()
        self.assertEqual((event.target_type, event.target_id), ("logtarget", "obj01"))

    def test_strings_truncated_to_column_size(self):
        log2.fail("test.long", message="x" * 3000, reason="y" * 100)
        event = self._last()
        self.assertEqual(len(event.message), 2000)
        self.assertEqual(len(event.reason), 64)

    def test_channel_defaults_to_system_outside_entry_points(self):
        log2.reset()
        log2.ok("test.system")
        self.assertEqual(self._last().channel, "system")

    def test_returns_the_event_with_its_id(self):
        event = log2.ok("test.return")
        self.assertEqual(event["id"], self._last().id)

    def test_extra_made_json_safe(self):
        import uuid

        handle = uuid.uuid4()
        log2.ok("test.extra.safe", extra={"handle": handle})
        self.assertEqual(self._last().extra, {"handle": str(handle)})

    def test_extra_capped(self):
        big = {"mode": "smtp", "recipients": 900, "sent": ["x" * 40] * 900}
        log2.ok("test.extra.big", extra=big)
        extra = self._last().extra
        self.assertTrue(extra["truncated"])
        self.assertEqual((extra["mode"], extra["recipients"]), ("smtp", 900))
        self.assertNotIn("sent", extra)
        self.assertGreater(extra["size"], 12000)
