# (c) cavaliba.com - test_data_refs.py

import json

import app_home.cache as cache
from app_data.data import Instance
from django.test import TestCase
from django.urls import reverse
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
        from app_user.models import SireneUser
        SireneUser.objects.get_or_create(login="testuser01", defaults={"displayname": "Test User 01"})

    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["login"] 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("login", entry)
        self.assertIn("displayname", entry)

    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_does_not_include_user(self):
        data = self._export(refs=["*"])
        self.assertNotIn("_user", data.get("_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):

    fixtures = ["test"]

    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",
        })

        from app_user.models import SireneUser
        SireneUser.objects.get_or_create(login="testuser01", defaults={"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)
        data = json.loads(response.content)
        logins = [u["login"] 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)
