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

import json

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

import app_home.cache as cache
from app_data.revision import revision_add_raw
from tests import helper


class APIAssetTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = helper.add_apikey_admin()
        helper.add_schema(classname="assettest")
        helper.add_instance(classname="assettest", keyname="obj01")
        for i in range(5):
            revision_add_raw(classname="assettest", keyname="obj01", action="edit")

    def test_api_asset(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "assettest", "key": "obj01"}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual("obj01", data["keyname"])


class APIAssetRevisionTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = helper.add_apikey_admin()
        helper.add_schema(classname="revtest")
        helper.add_instance(classname="revtest", keyname="obj01")
        for i in range(5):
            revision_add_raw(classname="revtest", keyname="obj01", action="edit")

    def test_api_asset_no_rev_param(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "revtest", "key": "obj01"}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertNotIn("_revision", data)

    def test_api_asset_rev_zero(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "revtest", "key": "obj01"}) + "?rev=0",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertNotIn("_revision", data)

    def test_api_asset_rev_positive(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "revtest", "key": "obj01"}) + "?rev=3",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("_revision", data)
        self.assertEqual(len(data["_revision"]), 3)

    def test_api_asset_rev_entry_fields(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "revtest", "key": "obj01"}) + "?rev=1",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        entry = data["_revision"][0]
        self.assertIn("login", entry)
        self.assertIn("date", entry)
        self.assertIn("action", entry)

    def test_api_asset_rev_capped_at_available(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "revtest", "key": "obj01"}) + "?rev=100",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(len(data["_revision"]), 5)


class APIAssetFieldTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = helper.add_apikey_admin()
        helper.add_schema(
            classname="fieldtest",
            field_definition={
                "keep_me": {"dataformat": "string", "displayname": "Keep Me"},
                "drop_me": {"dataformat": "string", "displayname": "Drop Me"},
            },
        )
        helper.add_instance(
            classname="fieldtest",
            keyname="obj01",
            fields={"keep_me": "keepvalue", "drop_me": "dropvalue"},
        )
        helper.add_instance(
            classname="fieldtest",
            keyname="obj02",
            fields={"keep_me": "keepvalue2", "drop_me": "dropvalue2"},
        )

    def test_no_field_param_returns_everything(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "fieldtest", "key": "obj01"}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data["keep_me"], "keepvalue")
        self.assertEqual(data["drop_me"], "dropvalue")

    def test_field_whitelist(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "fieldtest", "key": "obj01"})
            + "?field=keep_me",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("keep_me", data)
        self.assertNotIn("drop_me", data)

    def test_field_blacklist(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "fieldtest", "key": "obj01"})
            + "?field=!drop_me",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertIn("keep_me", data)
        self.assertNotIn("drop_me", data)

    def test_field_applies_in_list_mode(self):
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "fieldtest"}) + "?field=keep_me",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertTrue(len(data) > 0)
        for entry in data:
            self.assertIn("keep_me", entry)
            self.assertNotIn("drop_me", entry)

    def test_field_ignored_with_expand(self):
        # o=yaml here (not the json default) since the ui_detail shape includes
        # raw datetime values that json.dumps can't serialize - a pre-existing,
        # unrelated bug in the expand=true path, not exercised by this test
        response = self.client.get(
            reverse("api:api_asset", kwargs={"skey": "fieldtest", "key": "obj01"})
            + "?field=keep_me&expand=true&o=yaml",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = yaml.safe_load(response.content)[0]
        # expand uses the ui_detail shape - not filtered, drop_me still present
        self.assertIn("drop_me", data)
