"""
Tests for Themis REST API views and serializers.
"""
from django.contrib.auth import get_user_model
from django.urls import reverse
from rest_framework import status
from rest_framework.test import APITestCase

from themis.encryption import decrypt_value, encrypt_value
from themis.models import UserAPIKey, UserProfile

User = get_user_model()


class UserProfileAPITest(APITestCase):
    """Tests for the UserProfile API viewset."""

    def setUp(self):
        self.user = User.objects.create_user(username="apiuser", password="pass123")
        self.admin = User.objects.create_superuser(
            username="admin", password="admin123"
        )
        self.other_user = User.objects.create_user(
            username="otherapi", password="pass123"
        )
        self.url = reverse("themis-api:profile-list")

    def test_list_own_profile(self):
        """Regular user only sees their own profile."""
        self.client.force_authenticate(user=self.user)
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]["username"], "apiuser")

    def test_admin_sees_all_profiles(self):
        """Admin sees all profiles."""
        self.client.force_authenticate(user=self.admin)
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 3)  # apiuser, admin, otherapi

    def test_unauthenticated_denied(self):
        """Unauthenticated requests are denied with 401 + Bearer challenge."""
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)

    def test_retrieve_own_profile(self):
        """User can retrieve their own profile."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:profile-detail", kwargs={"pk": self.user.profile.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data["username"], "apiuser")
        self.assertEqual(response.data["home_timezone"], "UTC")

    def test_update_own_profile(self):
        """User can update their own profile."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:profile-detail", kwargs={"pk": self.user.profile.pk})
        response = self.client.patch(
            url,
            data={
                "home_timezone": "America/Toronto",
                "date_format": "DD/MM/YYYY",
                "theme_mode": "dark",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.user.profile.refresh_from_db()
        self.assertEqual(self.user.profile.home_timezone, "America/Toronto")
        self.assertEqual(self.user.profile.date_format, "DD/MM/YYYY")

    def test_profile_serializer_read_only_fields(self):
        """Read-only fields are present but not writable."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:profile-detail", kwargs={"pk": self.user.profile.pk})
        response = self.client.get(url)
        self.assertIn("effective_timezone", response.data)
        self.assertIn("is_traveling", response.data)
        self.assertIn("created_at", response.data)
        self.assertIn("updated_at", response.data)

    def test_profile_includes_user_info(self):
        """Profile serializer includes username and email."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:profile-detail", kwargs={"pk": self.user.profile.pk})
        response = self.client.get(url)
        self.assertIn("username", response.data)
        self.assertIn("email", response.data)


class UserAPIKeyAPITest(APITestCase):
    """Tests for the UserAPIKey API viewset."""

    def setUp(self):
        self.user = User.objects.create_user(username="keyapiuser", password="pass123")
        self.other_user = User.objects.create_user(
            username="otherkeyapi", password="pass123"
        )
        self.url = reverse("themis-api:apikey-list")

        self.key = UserAPIKey.objects.create(
            user=self.user,
            service_name="OpenAI",
            key_type="api",
            label="Work",
            encrypted_value=encrypt_value("sk-test-key-12345678"),
            instructions="Get from dashboard",
        )
        self.other_key = UserAPIKey.objects.create(
            user=self.other_user,
            service_name="Secret",
            key_type="api",
            encrypted_value=encrypt_value("sk-other-secret"),
        )

    def test_list_own_keys(self):
        """User only sees their own keys."""
        self.client.force_authenticate(user=self.user)
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(len(response.data), 1)
        self.assertEqual(response.data[0]["service_name"], "OpenAI")

    def test_list_shows_masked_value(self):
        """List includes masked_value, not the actual encrypted value."""
        self.client.force_authenticate(user=self.user)
        response = self.client.get(self.url)
        self.assertIn("masked_value", response.data[0])
        self.assertTrue(response.data[0]["masked_value"].endswith("5678"))
        self.assertNotIn("encrypted_value", response.data[0])

    def test_list_shows_display_name(self):
        """List includes display_name."""
        self.client.force_authenticate(user=self.user)
        response = self.client.get(self.url)
        self.assertEqual(response.data[0]["display_name"], "Work")

    def test_create_key(self):
        """POST creates a new key with encrypted value."""
        self.client.force_authenticate(user=self.user)
        response = self.client.post(
            self.url,
            data={
                "service_name": "Anthropic",
                "key_type": "token",
                "label": "Personal",
                "key_value": "sk-ant-abc123",
                "instructions": "From console",
            },
        )
        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
        self.assertEqual(UserAPIKey.objects.filter(user=self.user).count(), 2)
        new_key = UserAPIKey.objects.filter(
            user=self.user, service_name="Anthropic"
        ).first()
        self.assertEqual(decrypt_value(new_key.encrypted_value), "sk-ant-abc123")

    def test_retrieve_own_key(self):
        """User can retrieve their own key details."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:apikey-detail", kwargs={"pk": self.key.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.assertEqual(response.data["service_name"], "OpenAI")

    def test_cannot_retrieve_other_users_key(self):
        """Cannot retrieve another user's key."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:apikey-detail", kwargs={"pk": self.other_key.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)

    def test_update_key_metadata(self):
        """PATCH updates key metadata."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:apikey-detail", kwargs={"pk": self.key.pk})
        response = self.client.patch(
            url,
            data={
                "service_name": "Updated OpenAI",
                "is_active": False,
            },
        )
        self.assertEqual(response.status_code, status.HTTP_200_OK)
        self.key.refresh_from_db()
        self.assertEqual(self.key.service_name, "Updated OpenAI")
        self.assertFalse(self.key.is_active)

    def test_delete_own_key(self):
        """User can delete their own key."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:apikey-detail", kwargs={"pk": self.key.pk})
        response = self.client.delete(url)
        self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
        self.assertFalse(UserAPIKey.objects.filter(pk=self.key.pk).exists())

    def test_cannot_delete_other_users_key(self):
        """Cannot delete another user's key."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:apikey-detail", kwargs={"pk": self.other_key.pk})
        response = self.client.delete(url)
        self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
        self.assertTrue(UserAPIKey.objects.filter(pk=self.other_key.pk).exists())

    def test_unauthenticated_denied(self):
        """Unauthenticated requests are denied with 401 + Bearer challenge."""
        response = self.client.get(self.url)
        self.assertEqual(response.status_code, status.HTTP_401_UNAUTHORIZED)


class IsOwnerOrAdminPermissionTest(APITestCase):
    """Tests for the IsOwnerOrAdmin permission class."""

    def setUp(self):
        self.user = User.objects.create_user(username="permuser", password="pass123")
        self.admin = User.objects.create_superuser(
            username="permadmin", password="admin123"
        )
        self.key = UserAPIKey.objects.create(
            user=self.user,
            service_name="PermTest",
            key_type="api",
            encrypted_value=encrypt_value("sk-perm-test"),
        )

    def test_owner_can_access(self):
        """Owner can access their own object."""
        self.client.force_authenticate(user=self.user)
        url = reverse("themis-api:apikey-detail", kwargs={"pk": self.key.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

    def test_admin_can_access_own_keys(self):
        """Admin can access keys (queryset is user-scoped for privacy)."""
        # Note: UserAPIKeyViewSet queryset filters by request.user for privacy.
        # Admin sees their own keys, not other users', even though IsOwnerOrAdmin
        # would permit object-level access.
        admin_key = UserAPIKey.objects.create(
            user=self.admin,
            service_name="AdminKey",
            key_type="api",
            encrypted_value=encrypt_value("sk-admin-test"),
        )
        self.client.force_authenticate(user=self.admin)
        url = reverse("themis-api:apikey-detail", kwargs={"pk": admin_key.pk})
        response = self.client.get(url)
        self.assertEqual(response.status_code, status.HTTP_200_OK)
