from datetime import timedelta

from django.test import TestCase, tag
from django.utils import timezone

from epdb.logic import PackageManager, UserManager
from epdb.models import APIToken


@tag("api", "auth")
class BearerTokenAuthTests(TestCase):
    @classmethod
    def setUpTestData(cls):
        cls.user = UserManager.create_user(
            "token-user",
            "token-user@envipath.com",
            "SuperSafe",
            set_setting=False,
            add_to_group=False,
            is_active=True,
        )

        default_pkg = cls.user.default_package
        cls.user.default_package = None
        cls.user.save()
        if default_pkg:
            default_pkg.delete()

        cls.unreviewed_package = PackageManager.create_package(
            cls.user, "Token Auth Package", "Package for token auth tests"
        )

    def _auth_header(self, raw_token):
        return {"HTTP_AUTHORIZATION": f"Bearer {raw_token}"}

    def test_valid_token_allows_access(self):
        _, raw_token = APIToken.create_token(self.user, name="Valid Token", expires_days=1)

        response = self.client.get("/api/v1/compounds/", **self._auth_header(raw_token))

        self.assertEqual(response.status_code, 200)

    def test_expired_token_rejected(self):
        token, raw_token = APIToken.create_token(self.user, name="Expired Token", expires_days=1)
        token.expires_at = timezone.now() - timedelta(days=1)
        token.save(update_fields=["expires_at"])

        response = self.client.get("/api/v1/compounds/", **self._auth_header(raw_token))

        self.assertEqual(response.status_code, 401)

    def test_inactive_token_rejected(self):
        token, raw_token = APIToken.create_token(self.user, name="Inactive Token", expires_days=1)
        token.is_active = False
        token.save(update_fields=["is_active"])

        response = self.client.get("/api/v1/compounds/", **self._auth_header(raw_token))

        self.assertEqual(response.status_code, 401)

    def test_invalid_token_rejected(self):
        response = self.client.get("/api/v1/compounds/", HTTP_AUTHORIZATION="Bearer invalid-token")

        self.assertEqual(response.status_code, 401)

    def test_no_token_rejected(self):
        self.client.logout()
        response = self.client.get("/api/v1/compounds/")

        self.assertEqual(response.status_code, 401)

    def test_bearer_populates_request_user_for_packages(self):
        response = self.client.get("/api/v1/packages/")
        self.assertEqual(response.status_code, 200)
        payload = response.json()
        uuids = {item["uuid"] for item in payload["items"]}
        self.assertNotIn(str(self.unreviewed_package.uuid), uuids)

        _, raw_token = APIToken.create_token(self.user, name="Package Token", expires_days=1)
        response = self.client.get("/api/v1/packages/", **self._auth_header(raw_token))

        self.assertEqual(response.status_code, 200)
        payload = response.json()
        uuids = {item["uuid"] for item in payload["items"]}
        self.assertIn(str(self.unreviewed_package.uuid), uuids)

    def test_session_auth_still_works_without_bearer(self):
        self.client.force_login(self.user)
        response = self.client.get("/api/v1/packages/")

        self.assertEqual(response.status_code, 200)
        payload = response.json()
        uuids = {item["uuid"] for item in payload["items"]}
        self.assertIn(str(self.unreviewed_package.uuid), uuids)
