"""
Tests for Themis models: UserProfile and UserAPIKey.
"""
from django.contrib.auth import get_user_model
from django.test import TestCase
from django.urls import reverse

from themis.encryption import encrypt_value
from themis.models import DAISYUI_THEMES, TIMEZONE_CHOICES, UserAPIKey, UserProfile

User = get_user_model()


class UserProfileModelTest(TestCase):
    """Tests for the UserProfile model."""

    def setUp(self):
        self.user = User.objects.create_user(
            username="testuser",
            password="testpass123",
        )
        # Signal auto-creates the profile
        self.profile = self.user.profile

    def test_profile_auto_created(self):
        """UserProfile is created automatically via signal when User is created."""
        self.assertIsNotNone(self.profile)
        self.assertIsInstance(self.profile, UserProfile)

    def test_str(self):
        """__str__ returns 'username's Profile'."""
        self.assertEqual(str(self.profile), "testuser's Profile")

    def test_get_absolute_url(self):
        """get_absolute_url returns profile settings URL."""
        self.assertEqual(
            self.profile.get_absolute_url(),
            reverse("themis:profile-settings"),
        )

    def test_default_values(self):
        """Profile has expected default values."""
        self.assertEqual(self.profile.home_timezone, "UTC")
        self.assertEqual(self.profile.current_timezone, "")
        self.assertEqual(self.profile.date_format, "YYYY-MM-DD")
        self.assertEqual(self.profile.time_format, "24-hour")
        self.assertEqual(self.profile.thousand_separator, "comma")
        self.assertEqual(self.profile.week_start, "monday")
        self.assertEqual(self.profile.theme_mode, "auto")
        self.assertEqual(self.profile.theme_name, "corporate")
        self.assertEqual(self.profile.dark_theme_name, "business")

    def test_effective_timezone_home(self):
        """effective_timezone returns home_timezone when current_timezone is blank."""
        self.profile.home_timezone = "America/Toronto"
        self.profile.current_timezone = ""
        self.profile.save()
        self.assertEqual(self.profile.effective_timezone, "America/Toronto")

    def test_effective_timezone_traveling(self):
        """effective_timezone returns current_timezone when set."""
        self.profile.home_timezone = "America/Toronto"
        self.profile.current_timezone = "Europe/London"
        self.profile.save()
        self.assertEqual(self.profile.effective_timezone, "Europe/London")

    def test_is_traveling_false_when_no_current(self):
        """is_traveling is False when current_timezone is blank."""
        self.profile.current_timezone = ""
        self.assertFalse(self.profile.is_traveling)

    def test_is_traveling_false_when_same(self):
        """is_traveling is False when current_timezone equals home_timezone."""
        self.profile.home_timezone = "America/Toronto"
        self.profile.current_timezone = "America/Toronto"
        self.assertFalse(self.profile.is_traveling)

    def test_is_traveling_true(self):
        """is_traveling is True when current_timezone differs from home."""
        self.profile.home_timezone = "America/Toronto"
        self.profile.current_timezone = "Asia/Tokyo"
        self.assertTrue(self.profile.is_traveling)

    def test_timestamps(self):
        """created_at and updated_at are set automatically."""
        self.assertIsNotNone(self.profile.created_at)
        self.assertIsNotNone(self.profile.updated_at)

    def test_meta(self):
        """Meta verbose names are correct."""
        self.assertEqual(UserProfile._meta.verbose_name, "User Profile")
        self.assertEqual(UserProfile._meta.verbose_name_plural, "User Profiles")

    def test_timezone_choices_populated(self):
        """TIMEZONE_CHOICES is a non-empty list of tuples."""
        self.assertGreater(len(TIMEZONE_CHOICES), 0)
        self.assertIn(("UTC", "UTC"), TIMEZONE_CHOICES)

    def test_daisyui_themes_populated(self):
        """DAISYUI_THEMES contains the expected themes."""
        theme_keys = [t[0] for t in DAISYUI_THEMES]
        self.assertIn("light", theme_keys)
        self.assertIn("dark", theme_keys)
        self.assertIn("corporate", theme_keys)
        self.assertIn("business", theme_keys)
        self.assertEqual(len(DAISYUI_THEMES), 32)


class UserAPIKeyModelTest(TestCase):
    """Tests for the UserAPIKey model."""

    def setUp(self):
        self.user = User.objects.create_user(
            username="keyuser",
            password="testpass123",
        )
        self.key = UserAPIKey.objects.create(
            user=self.user,
            service_name="OpenAI",
            key_type="api",
            label="Work account",
            encrypted_value=encrypt_value("sk-test-key-1234567890"),
            instructions="Get your key from platform.openai.com",
            help_url="https://platform.openai.com/api-keys",
        )

    def test_str(self):
        """__str__ returns 'service (type) — username'."""
        self.assertEqual(str(self.key), "OpenAI (api) — keyuser")

    def test_get_absolute_url(self):
        """get_absolute_url returns key detail URL with UUID pk."""
        expected = reverse("themis:key-detail", kwargs={"pk": self.key.pk})
        self.assertEqual(self.key.get_absolute_url(), expected)

    def test_uuid_primary_key(self):
        """Primary key is a UUID."""
        import uuid

        self.assertIsInstance(self.key.pk, uuid.UUID)

    def test_masked_value(self):
        """masked_value shows stars with last 4 chars of decrypted value."""
        masked = self.key.masked_value
        self.assertTrue(masked.endswith("7890"))
        self.assertTrue(masked.startswith("*"))

    def test_masked_value_short_key(self):
        """masked_value returns **** for keys 4 chars or shorter."""
        short_key = UserAPIKey.objects.create(
            user=self.user,
            service_name="Short",
            key_type="api",
            encrypted_value=encrypt_value("ab"),
        )
        self.assertEqual(short_key.masked_value, "****")

    def test_masked_value_bad_encryption(self):
        """masked_value returns **** for invalid encrypted data."""
        bad_key = UserAPIKey.objects.create(
            user=self.user,
            service_name="Bad",
            key_type="api",
            encrypted_value="not-encrypted-at-all",
        )
        self.assertEqual(bad_key.masked_value, "****")

    def test_display_name_with_label(self):
        """display_name returns label when set."""
        self.assertEqual(self.key.display_name, "Work account")

    def test_display_name_without_label(self):
        """display_name returns service_name when label is blank."""
        self.key.label = ""
        self.assertEqual(self.key.display_name, "OpenAI")

    def test_default_values(self):
        """Verify default field values."""
        key = UserAPIKey.objects.create(
            user=self.user,
            service_name="Test",
            encrypted_value=encrypt_value("test"),
        )
        self.assertEqual(key.key_type, "api")
        self.assertEqual(key.label, "")
        self.assertEqual(key.instructions, "")
        self.assertEqual(key.help_url, "")
        self.assertTrue(key.is_active)
        self.assertIsNone(key.last_used_at)
        self.assertIsNone(key.expires_at)

    def test_timestamps(self):
        """created_at and updated_at are set automatically."""
        self.assertIsNotNone(self.key.created_at)
        self.assertIsNotNone(self.key.updated_at)

    def test_meta(self):
        """Meta verbose names are correct."""
        self.assertEqual(UserAPIKey._meta.verbose_name, "API Key")
        self.assertEqual(UserAPIKey._meta.verbose_name_plural, "API Keys")

    def test_cascade_delete(self):
        """Deleting user cascades to their API keys."""
        user_pk = self.user.pk
        self.user.delete()
        self.assertFalse(UserAPIKey.objects.filter(user_id=user_pk).exists())

    def test_key_type_choices(self):
        """KEY_TYPE_CHOICES contains expected types."""
        type_keys = [t[0] for t in UserAPIKey.KEY_TYPE_CHOICES]
        self.assertIn("api", type_keys)
        self.assertIn("dav", type_keys)
        self.assertIn("token", type_keys)
        self.assertIn("secret", type_keys)
        self.assertIn("other", type_keys)
