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

import json

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

import app_home.cache as cache
from app_data import crypto
from app_data.data import Instance
from app_data.models import DataInstance
from tests import helper


def add_apikey_keyname_rename(keyname="test_keyname", secret="keyname_secret"):
    hashed = crypto.hash_create(secret)
    instance = Instance(classname="_apikey", keyname=keyname)
    data = {
        "secret": hashed,
        "ip_filter": "*",
        "is_readonly": False,
        "is_enabled": True,
        "acl": "p_keyname_rename",
    }
    instance.merge_import(data)
    instance.save(action="create")
    return {"X-Cavaliba-Key": f"{keyname} {secret}"}


def add_apikey_readonly(keyname="test_readonly", secret="readonly_secret"):
    hashed = crypto.hash_create(secret)
    instance = Instance(classname="_apikey", keyname=keyname)
    data = {
        "secret": hashed,
        "ip_filter": "*",
        "is_readonly": True,
        "is_enabled": True,
        "acl": "p_keyname_rename",
    }
    instance.merge_import(data)
    instance.save(action="create")
    return {"X-Cavaliba-Key": f"{keyname} {secret}"}


class APIKeynameAuthTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = add_apikey_keyname_rename()
        helper.add_schema("tschema_b", displayname="Schema B")
        helper.add_instance("tschema_b", "b_old")

    def test_api_keyname_no_auth(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?s=tschema_b&old=b_old&new=b_new",
        )
        self.assertEqual(response.status_code, 401)

    def test_api_keyname_no_permission(self):
        header = helper.add_apikey(keyname="test_noperm", acl="")
        response = self.client.post(
            reverse("api:api_keyname") + "?s=tschema_b&old=b_old&new=b_new",
            headers=header,
        )
        self.assertEqual(response.status_code, 401)

    def test_api_keyname_readonly_key(self):
        header = add_apikey_readonly()
        response = self.client.post(
            reverse("api:api_keyname") + "?s=tschema_b&old=b_old&new=b_new",
            headers=header,
        )
        self.assertEqual(response.status_code, 401)

    def test_api_keyname_get_not_allowed(self):
        response = self.client.get(
            reverse("api:api_keyname") + "?s=tschema_b&old=b_old&new=b_new",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 405)


class APIKeynameInputTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = add_apikey_keyname_rename()
        helper.add_schema("tschema_b", displayname="Schema B")
        helper.add_instance("tschema_b", "b_old")

    def test_api_keyname_missing_s(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?old=b_old&new=b_new",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 400)

    def test_api_keyname_missing_old(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?s=tschema_b&new=b_new",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 400)

    def test_api_keyname_missing_new(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?s=tschema_b&old=b_old",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 400)

    def test_api_keyname_unknown_instance(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?s=tschema_b&old=missing&new=b_new&dryrun=false",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 400)


class APIKeynameLogicTest(TestCase):
    def setUp(self):
        cache.clear()
        self.header = add_apikey_keyname_rename()
        helper.add_schema("tschema_b", displayname="Schema B")
        helper.add_instance("tschema_b", "b_old")

    def test_api_keyname_default_dryrun_no_db_changes(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?s=tschema_b&old=b_old&new=b_new",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertTrue(data["dryrun"])
        self.assertFalse(data["target_renamed"])
        self.assertTrue(
            DataInstance.objects.filter(classname="tschema_b", keyname="b_old").exists()
        )

    def test_api_keyname_dryrun_false_renames(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?s=tschema_b&old=b_old&new=b_new&dryrun=false",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertTrue(data["target_renamed"])
        self.assertFalse(
            DataInstance.objects.filter(classname="tschema_b", keyname="b_old").exists()
        )
        self.assertTrue(
            DataInstance.objects.filter(classname="tschema_b", keyname="b_new").exists()
        )

    def test_api_keyname_rejects_user_without_force(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?s=user&old=admin&new=admin2&dryrun=false",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 400)

    def test_api_keyname_protected_keyname_rejected_even_with_force(self):
        response = self.client.post(
            reverse("api:api_keyname") + "?s=user&old=admin&new=admin2&dryrun=false&force=yes",
            headers=self.header,
        )
        self.assertEqual(response.status_code, 400)
