# (c) cavaliba.com - test_data_refs.py

import json

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

import app_home.cache as cache
from app_data.data import Instance
from tests import helper

# ---------------------------------------------------------------------------
# Unit tests: get_dict_for_export refs option
# ---------------------------------------------------------------------------


class TestGetDictForExportRefs(TestCase):
    def setUp(self):
        cache.clear()

        # schema with a user field and a schema field
        helper.add_schema(
            classname="refstest",
            field_definition={
                "owner": {
                    "dataformat": "user",
                    "displayname": "Owner",
                    "cardinal_min": 0,
                    "cardinal_max": 1,
                },
                "linked": {
                    "dataformat": "schema",
                    "dataformat_ext": "refsother",
                    "displayname": "Linked",
                    "cardinal_min": 0,
                    "cardinal_max": 1,
                },
            },
        )
        helper.add_schema(classname="refsother")

        helper.add_instance(
            classname="refsother", keyname="other01", fields={"displayname": "Other 01"}
        )

        helper.add_instance(
            classname="refstest",
            keyname="obj01",
            fields={
                "owner": "testuser01",
                "linked": "other01",
            },
        )

        # user referenced by obj01, with extra fields for refs expansion
        user = helper.add_user(login="testuser01", displayname="Test User 01")
        user.set_field_value_single("email", "testuser01@example.com")
        user.set_field_value_single("mobile", "+33600000001")
        user.set_field_value_single("external_id", "EXT001")
        user.update()

    def _export(self, refs=None):
        instance = Instance.from_keyname("refstest", "obj01")
        return instance.get_dict_for_export(refs=refs)

    def test_refs_none_no_refs_key(self):
        data = self._export(refs=None)
        self.assertNotIn("_refs", data)

    def test_refs_empty_list_no_refs_key(self):
        data = self._export(refs=[])
        self.assertNotIn("_refs", data)

    def test_refs_unknown_type_no_refs_key(self):
        data = self._export(refs=["nonexistent"])
        self.assertNotIn("_refs", data)

    def test_refs_user_present(self):
        data = self._export(refs=["user"])
        self.assertIn("_refs", data)
        self.assertIn("user", data["_refs"])

    def test_refs_user_contains_expected_login(self):
        data = self._export(refs=["user"])
        logins = [u["keyname"] for u in data["_refs"]["user"]]
        self.assertIn("testuser01", logins)

    def test_refs_user_entry_fields(self):
        data = self._export(refs=["user"])
        entry = data["_refs"]["user"][0]
        self.assertIn("keyname", entry)
        self.assertIn("displayname", entry)

    def test_refs_user_extra_fields(self):
        data = self._export(refs=["user"])
        entry = next(u for u in data["_refs"]["user"] if u["keyname"] == "testuser01")
        self.assertEqual(entry.get("email"), "testuser01@example.com")
        self.assertEqual(entry.get("mobile"), "+33600000001")
        self.assertEqual(entry.get("external_id"), "EXT001")

    def test_refs_schema_present(self):
        data = self._export(refs=["refsother"])
        self.assertIn("_refs", data)
        self.assertIn("refsother", data["_refs"])

    def test_refs_schema_contains_expected_keyname(self):
        data = self._export(refs=["refsother"])
        keynames = [i["keyname"] for i in data["_refs"]["refsother"]]
        self.assertIn("other01", keynames)

    def test_refs_schema_entry_fields(self):
        data = self._export(refs=["refsother"])
        entry = data["_refs"]["refsother"][0]
        self.assertIn("keyname", entry)
        self.assertIn("classname", entry)
        self.assertIn("displayname", entry)

    def test_refs_multiple_types(self):
        data = self._export(refs=["user", "refsother"])
        self.assertIn("_refs", data)
        self.assertIn("user", data["_refs"])
        self.assertIn("refsother", data["_refs"])

    def test_refs_star_expands_to_schemas(self):
        data = self._export(refs=["*"])
        self.assertIn("_refs", data)
        self.assertIn("refsother", data["_refs"])

    def test_refs_star_with_user(self):
        data = self._export(refs=["*", "user"])
        self.assertIn("_refs", data)
        self.assertIn("user", data["_refs"])
        self.assertIn("refsother", data["_refs"])


# ---------------------------------------------------------------------------
# API tests: ?refs= parameter on /api/assets/
# ---------------------------------------------------------------------------


class APIAssetRefsTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = helper.add_apikey_admin()

        helper.add_schema(
            classname="refstest",
            field_definition={
                "owner": {
                    "dataformat": "user",
                    "displayname": "Owner",
                    "cardinal_min": 0,
                    "cardinal_max": 1,
                },
                "linked": {
                    "dataformat": "schema",
                    "dataformat_ext": "refsother",
                    "displayname": "Linked",
                    "cardinal_min": 0,
                    "cardinal_max": 1,
                },
            },
        )
        helper.add_schema(classname="refsother")

        helper.add_instance(
            classname="refsother", keyname="other01", fields={"displayname": "Other 01"}
        )

        helper.add_instance(
            classname="refstest",
            keyname="obj01",
            fields={
                "owner": "testuser01",
                "linked": "other01",
            },
        )

        helper.add_user(login="testuser01", displayname="Test User 01")

        self.url = reverse("api:api_asset", kwargs={"skey": "refstest", "key": "obj01"})

    def test_api_no_refs_param(self):
        response = self.client.get(self.url, headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertNotIn("_refs", data)

    def test_api_refs_user(self):
        response = self.client.get(self.url + "?refs=user", headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("_refs", data)
        self.assertIn("user", data["_refs"])

    def test_api_refs_user_login(self):
        response = self.client.get(self.url + "?refs=user", headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        logins = [u["keyname"] for u in data["_refs"]["user"]]
        self.assertIn("testuser01", logins)

    def test_api_refs_schema(self):
        response = self.client.get(self.url + "?refs=refsother", headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("_refs", data)
        self.assertIn("refsother", data["_refs"])

    def test_api_refs_multiple_types(self):
        response = self.client.get(self.url + "?refs=user,refsother", headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("_refs", data)
        self.assertIn("user", data["_refs"])
        self.assertIn("refsother", data["_refs"])

    def test_api_refs_star(self):
        response = self.client.get(self.url + "?refs=*", headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("_refs", data)
        self.assertIn("refsother", data["_refs"])

    def test_api_refs_star_with_user(self):
        response = self.client.get(self.url + "?refs=*,user", headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("_refs", data)
        self.assertIn("user", data["_refs"])
        self.assertIn("refsother", data["_refs"])

    def test_api_refs_unknown_no_refs_key(self):
        response = self.client.get(self.url + "?refs=nonexistent", headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertNotIn("_refs", data)
