import json

from django.urls import reverse

from konova.settings import DEFAULT_GROUP
from konova.tests.test_views import BaseTestCase
from konova.utils.user_checks import is_default_group_only


class BaseAPIV1TestCase(BaseTestCase):
    @classmethod
    def setUpTestData(cls):
        super().setUpTestData()

    def setUp(self) -> None:
        super().setUp()
        self.superuser.get_API_token()
        self.superuser.api_token.is_active = True
        self.superuser.api_token.save()
        default_group = self.groups.get(name=DEFAULT_GROUP)
        self.superuser.groups.add(default_group)

        self.header_data = {
            "HTTP_ksptoken": self.superuser.api_token.token,
            "HTTP_kspuser": self.superuser.username,
        }


class APIV1SharingTestCase(BaseAPIV1TestCase):

    @classmethod
    def setUpTestData(cls):
        super().setUpTestData()

    def _run_share_request(self, url, user_list: list):
        data = {
            "users": user_list
        }
        data = json.dumps(data)
        response = self.client.put(
            url,
            data,
            **self.header_data
        )
        return response

    def _test_api_sharing(self, obj, url):
        """ Generic test for testing sharing of a ShareableObjectMixin object

        Args:
            obj (ShareableObjectMixin): The object
            url (str): The url to be used for a request

        Returns:

        """
        self.assertEqual(obj.users.count(), 0)
        user_list = [
            self.superuser.username,
            self.user.username,
        ]

        response = self._run_share_request(url, user_list)

        # Must fail, since performing user has no access on requested object
        self.assertEqual(response.status_code, 500)
        self.assertTrue(len(json.loads(response.content.decode("utf-8")).get("errors", [])) > 0)

        # Add performing user to shared access users and rerun the request
        obj.users.add(self.superuser)
        response = self._run_share_request(url, user_list)

        shared_users = obj.shared_users
        self.assertEqual(response.status_code, 200)
        self.assertEqual(shared_users.count(), 2)
        self.assertIn(self.superuser, shared_users)
        self.assertIn(self.user, shared_users)

    def test_api_token_invalid(self):
        """ Tests that a request with an invalid token won't be successfull

        Returns:

        """
        share_url = reverse("api:v1:intervention-share", args=(self.intervention.id,))
        # Expect the first request to work properly
        self.intervention.users.add(self.superuser)
        response = self._run_share_request(share_url, [self.superuser.username])
        self.assertEqual(response.status_code, 200)

        # Change the token
        self.header_data["HTTP_ksptoken"] = f"{self.superuser.api_token.token}__X"

        # Expect the request to fail now
        response = self._run_share_request(share_url, [self.superuser.username])
        self.assertEqual(response.status_code, 403)

    def test_api_intervention_sharing(self):
        """ Tests proper sharing of intervention

        Returns:

        """
        share_url = reverse("api:v1:intervention-share", args=(self.intervention.id,))
        self._test_api_sharing(self.intervention, share_url)

    def test_api_eco_account_sharing(self):
        """ Tests proper sharing of eco account

        Returns:

        """
        share_url = reverse("api:v1:ecoaccount-share", args=(self.eco_account.id,))
        self._test_api_sharing(self.eco_account, share_url)

    def test_api_ema_sharing(self):
        """ Tests proper sharing of ema

        Returns:

        """
        share_url = reverse("api:v1:ema-share", args=(self.ema.id,))
        self._test_api_sharing(self.ema, share_url)

    def test_api_sharing_as_default_group_only(self):
        """ Tests that sharing using the API as an only default group user works as expected.

        Expected:
            Default only user can only add new users, having shared access. Removing them from the list of users
            having shared access is only possible if the user has further rights, e.g. being part of a registration
            or conservation office group.

        Returns:

        """
        share_url = reverse("api:v1:intervention-share", args=(self.intervention.id,))

        # Give the user only default group rights
        default_group = self.groups.get(name=DEFAULT_GROUP)
        self.superuser.groups.set([default_group])
        self.assertTrue(is_default_group_only(self.superuser))

        # Add only him as shared_users an object
        self.intervention.users.set([self.superuser])
        self.assertEqual(self.intervention.users.count(), 1)

        # Try to add another user via API -> must work!
        response = self._run_share_request(share_url, [self.superuser.username, self.user.username])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.intervention.users.count(), 2)

        # Now try to remove the user again -> expect no changes at all to the shared user list
        response = self._run_share_request(share_url, [self.superuser.username])
        self.assertEqual(response.status_code, 200)
        self.assertEqual(self.intervention.users.count(), 2)