# (c) cavaliba.com - tests / api / cmt

import json

from django.test import TestCase
from django.urls import reverse

import app_home.cache as cache
from app_cmt.models import CMTRaw
from app_data.data import Instance
from tests import helper


def add_agent(keyname="agent1", mode="DEFAULT", conf=""):
    """Create a _cmtagent test instance."""
    helper.add_instance(
        classname="_cmtagent",
        keyname=keyname,
        fields={"displayname": keyname, "mode": mode, "conf": conf, "is_enabled": True},
    )


def add_check(displayname="check1", module="HTTP", conf=""):
    """Create a _cmtcheck test instance and return its (engine-generated UUID)
    keyname - _cmtcheck uses keyname_mode: uuid, so any keyname passed to
    load_broker is ignored; look the real one up by displayname instead."""
    helper.add_instance(
        classname="_cmtcheck",
        keyname=None,
        fields={"displayname": displayname, "module": module, "conf": conf, "is_enabled": True},
    )
    for instance in Instance.list_classname(classname="_cmtcheck"):
        if instance.displayname == displayname:
            return instance.keyname
    raise AssertionError(f"_cmtcheck '{displayname}' not found after creation")


class APICmtAuthTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = helper.add_apikey(
            keyname="cmtkey", acl="p_cmt_api_worklist\np_cmt_api_result"
        )
        self.key_value = self.header["X-Cavaliba-Key"]
        add_agent(keyname="agent1")

    def _header(self, identity=None):
        if identity is None:
            return {"X-Cavaliba-Key": self.key_value}
        return {"X-Cavaliba-Key": f"{self.key_value} {identity}"}

    def test_no_auth_header(self):
        response = self.client.get(reverse("api:api_cmt"))
        self.assertEqual(response.status_code, 401)

    def test_missing_permission(self):
        header = helper.add_apikey(keyname="cmtkey_noperm", acl="")
        response = self.client.get(
            reverse("api:api_cmt"),
            headers={"X-Cavaliba-Key": f"{header['X-Cavaliba-Key']} agent1"},
        )
        self.assertEqual(response.status_code, 401)

    def test_unknown_agent_identity(self):
        response = self.client.get(
            reverse("api:api_cmt"), headers=self._header(identity="no_such_agent")
        )
        self.assertEqual(response.status_code, 400)

    def test_no_identity_token(self):
        # 2-field header still parses fine (backward compatible), but has no
        # agent identity to resolve, so /api/cmt/ rejects it with 400.
        response = self.client.get(reverse("api:api_cmt"), headers=self._header())
        self.assertEqual(response.status_code, 400)


class APICmtGetTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = helper.add_apikey(
            keyname="cmtkey", acl="p_cmt_api_worklist\np_cmt_api_result"
        )
        self.key_value = self.header["X-Cavaliba-Key"]

    def _header(self, identity):
        return {"X-Cavaliba-Key": f"{self.key_value} {identity}"}

    def test_get_default_mode_aggregates_enabled_checks(self):
        add_agent(keyname="agent_default", mode="DEFAULT", conf="")
        checkname = add_check(displayname="c1", module="HTTP", conf="target: http://example.org")

        response = self.client.get(reverse("api:api_cmt"), headers=self._header("agent_default"))
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("checks", data)
        self.assertIn(checkname, data["checks"])
        self.assertEqual(data["checks"][checkname]["module"], "HTTP")
        self.assertEqual(data["checks"][checkname]["target"], "http://example.org")

    def test_get_conf_mode_returns_agent_conf_verbatim(self):
        add_agent(keyname="agent_conf", mode="CONF", conf="global:\n  interval: 60\nchecks: {}\n")

        response = self.client.get(reverse("api:api_cmt"), headers=self._header("agent_conf"))
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data["global"]["interval"], 60)

    def test_get_conf_mode_invalid_yaml_returns_400(self):
        add_agent(keyname="agent_bad", mode="CONF", conf="global: [unclosed")

        response = self.client.get(reverse("api:api_cmt"), headers=self._header("agent_bad"))
        self.assertEqual(response.status_code, 400)

    def test_wrong_method_returns_405(self):
        add_agent(keyname="agent_put")
        response = self.client.put(reverse("api:api_cmt"), headers=self._header("agent_put"))
        self.assertEqual(response.status_code, 405)


class APICmtPostTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = helper.add_apikey(
            keyname="cmtkey", acl="p_cmt_api_worklist\np_cmt_api_result"
        )
        self.key_value = self.header["X-Cavaliba-Key"]
        add_agent(keyname="agent1")
        self.checkname = add_check(displayname="c1", module="HTTP", conf="")

    def _header(self, identity="agent1"):
        return {"X-Cavaliba-Key": f"{self.key_value} {identity}"}

    def test_post_happy_path_creates_cmtraw(self):
        body = {
            "results": [
                {"checkname": self.checkname, "status": True, "usec": 12345, "error": ""},
            ]
        }
        response = self.client.post(
            reverse("api:api_cmt"),
            data=json.dumps(body),
            content_type="application/json",
            headers=self._header(),
        )
        self.assertEqual(response.status_code, 200)
        self.assertEqual(CMTRaw.objects.filter(checkname=self.checkname).count(), 1)
        raw = CMTRaw.objects.get(checkname=self.checkname)
        self.assertTrue(raw.status)
        self.assertEqual(raw.usec, 12345)

    def test_post_unknown_checkname_returns_400(self):
        body = {"results": [{"checkname": "no_such_check", "status": True, "usec": 1}]}
        response = self.client.post(
            reverse("api:api_cmt"),
            data=json.dumps(body),
            content_type="application/json",
            headers=self._header(),
        )
        self.assertEqual(response.status_code, 400)
        self.assertEqual(CMTRaw.objects.count(), 0)

    def test_post_invalid_json_returns_400(self):
        response = self.client.post(
            reverse("api:api_cmt"),
            data="not json",
            content_type="application/json",
            headers=self._header(),
        )
        self.assertEqual(response.status_code, 400)
