# (c) cavaliba.com - tests / views - log browser: list, detail, export, purge

import json

from django.core.cache import cache as cache_django
from django.test import TestCase, override_settings
from django.urls import reverse

import app_home.cache as cache
from app_home.log import log2
from app_home.models import CavalibaEvent
from tests import helper


@override_settings(CAVALIBA_AUTH_MODE="unittest")
class TestViewsLog(TestCase):
    def setUp(self):
        cache.clear()
        cache_django.clear()
        CavalibaEvent.objects.all().delete()
        log2.reset(channel="web", actor="seed")
        log2.access("seed.read", target=("server", "web01"))
        log2.fail("seed.fail", reason="not_found", target=("server", "web02"), extra={"k": "v"})

    def _login(self, admin=False):
        perms = ["p_log_view", "p_admintools"] + (["p_log_admin"] if admin else [])
        helper.add_user_with_perm(login="unittest", perms=perms)

    def _body(self, response):
        return b"".join(response.streaming_content).decode()

    def test_requires_p_log_view(self):
        helper.add_user_with_perm(login="unittest", perms=[])
        response = self.client.get(reverse("app_home:log"))
        self.assertEqual(response.status_code, 302)

    def test_default_list_is_info_and_above(self):
        self._login()
        response = self.client.get(reverse("app_home:log"))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "seed.fail")
        self.assertNotContains(response, "seed.read")

    def test_filters_and_legacy_level_url(self):
        self._login()
        response = self.client.get(reverse("app_home:log") + "?level=&target=server:web01")
        self.assertContains(response, "seed.read")
        self.assertNotContains(response, "seed.fail")
        response = self.client.get(reverse("app_home:log", args=["error"]))
        self.assertContains(response, "seed.fail")

    def test_invalid_page_size_redirects(self):
        self._login()
        response = self.client.get(reverse("app_home:log") + "?size=7")
        self.assertEqual(response.status_code, 302)

    def test_viewing_is_logged_as_access(self):
        self._login()
        self.client.get(reverse("app_home:log"))
        self.assertTrue(CavalibaEvent.objects.filter(action="log.list", actor="unittest").exists())

    def test_detail_partial(self):
        self._login()
        event = CavalibaEvent.objects.get(action="seed.fail")
        response = self.client.get(reverse("app_home:log_event", args=[event.id]))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, "not_found")
        self.assertContains(response, "&quot;k&quot;: &quot;v&quot;")
        self.assertContains(response, f"request_id={event.request_id}")

    def test_detail_unknown_id_is_404(self):
        self._login()
        response = self.client.get(reverse("app_home:log_event", args=[999999]))
        self.assertEqual(response.status_code, 404)

    def test_export_csv_and_json(self):
        self._login()
        url = reverse("app_home:log_export", args=["csv"]) + "?level=&target=server:"
        lines = self._body(self.client.get(url)).strip().splitlines()
        self.assertTrue(lines[0].startswith("id,created,level"))
        self.assertEqual(len(lines), 3)
        url = reverse("app_home:log_export", args=["json"]) + "?level=&target=server:"
        rows = json.loads(self._body(self.client.get(url)))
        self.assertEqual({r["action"] for r in rows}, {"seed.read", "seed.fail"})
        self.assertTrue(CavalibaEvent.objects.filter(action="log.export").exists())

    def test_export_unknown_format_is_404(self):
        self._login()
        response = self.client.get(reverse("app_home:log_export", args=["xml"]))
        self.assertEqual(response.status_code, 404)

    def test_purge_refused_without_p_log_admin(self):
        self._login()
        response = self.client.post(reverse("app_home:log"), {"purge": "all"})
        self.assertEqual(response.status_code, 302)
        self.assertTrue(CavalibaEvent.objects.filter(action="seed.fail").exists())
        denied = CavalibaEvent.objects.get(action="log.purge", outcome="denied")
        self.assertEqual(denied.reason, "permission_denied")

    def test_purge_all_with_p_log_admin(self):
        self._login(admin=True)
        self.client.post(reverse("app_home:log"), {"purge": "all"})
        self.assertFalse(CavalibaEvent.objects.filter(action__startswith="seed.").exists())

    def test_admintools_purge_requires_p_log_admin(self):
        self._login()
        self.client.post(reverse("app_home:admintools_logpurge"))
        self.assertTrue(CavalibaEvent.objects.filter(action="log.purge", outcome="denied").exists())

    def test_csv_export_neutralises_formulas(self):
        self._login()
        log2.fail("seed.formula", message='=HYPERLINK("http://x","y")', target=("server", "@web03"))
        url = reverse("app_home:log_export", args=["csv"]) + "?level=&action=seed.formula"
        body = self._body(self.client.get(url))
        self.assertIn("\"'=HYPERLINK(", body)
        self.assertIn("'@web03", body)
        self.assertNotIn(",=HYPERLINK", body)
