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

import json

import app_home.cache as cache
from app_data.tasks import create_datatask, finish_task
from django.test import TestCase
from django.urls import reverse
from tests import helper


def add_apikey_other(keyname="other_key", secret="other_secret"):
    from app_data import crypto
    from app_data.data import Instance
    hashed = crypto.hash_create(secret)
    instance = Instance(classname="_apikey", keyname=keyname)
    data = {
        "secret": hashed,
        "ip_filter": "*",
        "is_readonly": False,
        "is_enabled": True,
        "acl": "role:role_data_access",
    }
    instance.merge_import(data)
    instance.create()
    return {"X-Cavaliba-Key": f"{keyname} {secret}"}


class APITaskListTest(TestCase):

    fixtures = ["test"]

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

    def test_no_auth(self):
        response = self.client.get(reverse('api:api_task_list'))
        self.assertEqual(response.status_code, 401)

    def test_method_not_allowed(self):
        response = self.client.post(reverse('api:api_task_list'), headers=self.header)
        self.assertEqual(response.status_code, 405)

    def test_empty_list(self):
        response = self.client.get(reverse('api:api_task_list'), headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data["total"], 0)
        self.assertEqual(data["tasks"], [])

    def test_admin_sees_all(self):
        create_datatask("t1", owner_id="userA")
        create_datatask("t2", owner_id="userB")
        response = self.client.get(reverse('api:api_task_list'), headers=self.header)
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data["total"], 2)

    def test_state_filter(self):
        dt = create_datatask("t1", owner_id="userA")
        finish_task(dt.handle, state="DONE")
        create_datatask("t2", owner_id="userA")
        response = self.client.get(
            reverse('api:api_task_list') + '?state=DONE',
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data["total"], 1)
        self.assertEqual(data["tasks"][0]["state"], "DONE")


class APITaskDetailTest(TestCase):

    fixtures = ["test"]

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

    def test_no_auth(self):
        dt = create_datatask("t", owner_id="u1")
        response = self.client.get(
            reverse('api:api_task_detail', kwargs={'handle': dt.handle})
        )
        self.assertEqual(response.status_code, 401)

    def test_not_found(self):
        import uuid
        response = self.client.get(
            reverse('api:api_task_detail', kwargs={'handle': uuid.uuid4()}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 404)

    def test_returns_task(self):
        dt = create_datatask("mytask", owner_id="u1", params={"x": 1})
        response = self.client.get(
            reverse('api:api_task_detail', kwargs={'handle': dt.handle}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertEqual(data["name"], "mytask")
        self.assertEqual(data["state"], "QUEUED")
        self.assertEqual(data["params"]["x"], 1)

    def test_method_not_allowed(self):
        dt = create_datatask("t", owner_id="u1")
        response = self.client.post(
            reverse('api:api_task_detail', kwargs={'handle': dt.handle}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 405)

    def test_owner_cannot_see_other_task(self):
        other_header = add_apikey_other()
        dt = create_datatask("t", owner_id="admin_owner")
        response = self.client.get(
            reverse('api:api_task_detail', kwargs={'handle': dt.handle}),
            headers=other_header,
        )
        self.assertEqual(response.status_code, 401)

    def test_owner_sees_own_task(self):
        dt = create_datatask("t", owner_id="test", owner_type="api")
        response = self.client.get(
            reverse('api:api_task_detail', kwargs={'handle': dt.handle}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)


class APITaskStopTest(TestCase):

    fixtures = ["test"]

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

    def test_no_auth(self):
        dt = create_datatask("t", owner_id="u1")
        response = self.client.post(
            reverse('api:api_task_stop', kwargs={'handle': dt.handle})
        )
        self.assertEqual(response.status_code, 401)

    def test_method_not_allowed(self):
        dt = create_datatask("t", owner_id="u1")
        response = self.client.get(
            reverse('api:api_task_stop', kwargs={'handle': dt.handle}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 405)

    def test_not_found(self):
        import uuid
        response = self.client.post(
            reverse('api:api_task_stop', kwargs={'handle': uuid.uuid4()}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 404)

    def test_stop_queued_task(self):
        dt = create_datatask("t", owner_id="test", owner_type="api")
        response = self.client.post(
            reverse('api:api_task_stop', kwargs={'handle': dt.handle}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertTrue(data["aborted"])
        dt.refresh_from_db()
        self.assertEqual(dt.state, "ABORTED")

    def test_stop_done_task_returns_false(self):
        dt = create_datatask("t", owner_id="test", owner_type="api")
        finish_task(dt.handle, state="DONE")
        response = self.client.post(
            reverse('api:api_task_stop', kwargs={'handle': dt.handle}),
            headers=self.header,
        )
        self.assertEqual(response.status_code, 200)
        data = json.loads(response.content)
        self.assertFalse(data["aborted"])

    def test_other_owner_cannot_stop(self):
        other_header = add_apikey_other()
        dt = create_datatask("t", owner_id="admin_owner")
        response = self.client.post(
            reverse('api:api_task_stop', kwargs={'handle': dt.handle}),
            headers=other_header,
        )
        self.assertEqual(response.status_code, 401)
