"""
Tests for LLM Manager admin configuration.

Covers system model admin actions, badges, and save_model validation.
"""

from decimal import Decimal

from django.contrib.admin.sites import AdminSite
from django.contrib.auth import get_user_model
from django.test import RequestFactory, TestCase

from llm_manager.admin import LLMModelAdmin
from llm_manager.models import LLMApi, LLMModel

User = get_user_model()


class AdminTestBase(TestCase):
    """Base class with common admin test setup."""

    def setUp(self):
        self.factory = RequestFactory()
        self.site = AdminSite()
        self.admin = LLMModelAdmin(LLMModel, self.site)
        self.user = User.objects.create_superuser(
            username="admin", password="admin123", email="admin@test.com"
        )
        self.api = LLMApi.objects.create(
            name="Test API",
            api_type="vllm",
            base_url="http://localhost:8000/v1",
        )

    def _make_request(self):
        request = self.factory.post("/admin/")
        request.user = self.user
        # Django admin uses _messages attribute
        from django.contrib.messages.storage.fallback import FallbackStorage

        setattr(request, "session", "session")
        setattr(request, "_messages", FallbackStorage(request))
        return request


class SystemVisionModelActionTests(AdminTestBase):
    """Tests for the set_as_system_vision_model admin action."""

    def test_set_vision_type_as_system_vision_model(self):
        """A 'vision' type model can be set as system vision model."""
        model = LLMModel.objects.create(
            api=self.api,
            name="qwen3-vl-72b",
            model_type="vision",
            context_window=8192,
        )
        request = self._make_request()
        queryset = LLMModel.objects.filter(pk=model.pk)

        self.admin.set_as_system_vision_model(request, queryset)

        model.refresh_from_db()
        self.assertTrue(model.is_system_vision_model)

    def test_set_chat_type_as_system_vision_model(self):
        """A 'chat' type model can be set as system vision model (vision-capable chat)."""
        model = LLMModel.objects.create(
            api=self.api,
            name="gpt-4o",
            model_type="chat",
            context_window=128000,
        )
        request = self._make_request()
        queryset = LLMModel.objects.filter(pk=model.pk)

        self.admin.set_as_system_vision_model(request, queryset)

        model.refresh_from_db()
        self.assertTrue(model.is_system_vision_model)

    def test_embedding_type_rejected_as_vision_model(self):
        """An 'embedding' type model cannot be set as system vision model."""
        model = LLMModel.objects.create(
            api=self.api,
            name="embed-model",
            model_type="embedding",
            context_window=8192,
        )
        request = self._make_request()
        queryset = LLMModel.objects.filter(pk=model.pk)

        self.admin.set_as_system_vision_model(request, queryset)

        model.refresh_from_db()
        self.assertFalse(model.is_system_vision_model)

    def test_reranker_type_rejected_as_vision_model(self):
        """A 'reranker' type model cannot be set as system vision model."""
        model = LLMModel.objects.create(
            api=self.api,
            name="reranker-model",
            model_type="reranker",
            context_window=8192,
        )
        request = self._make_request()
        queryset = LLMModel.objects.filter(pk=model.pk)

        self.admin.set_as_system_vision_model(request, queryset)

        model.refresh_from_db()
        self.assertFalse(model.is_system_vision_model)

    def test_inactive_model_rejected(self):
        """An inactive model cannot be set as system vision model."""
        model = LLMModel.objects.create(
            api=self.api,
            name="inactive-vision",
            model_type="vision",
            context_window=8192,
            is_active=False,
        )
        request = self._make_request()
        queryset = LLMModel.objects.filter(pk=model.pk)

        self.admin.set_as_system_vision_model(request, queryset)

        model.refresh_from_db()
        self.assertFalse(model.is_system_vision_model)

    def test_multiple_selection_rejected(self):
        """Selecting more than one model is rejected."""
        m1 = LLMModel.objects.create(
            api=self.api, name="v1", model_type="vision", context_window=8192
        )
        m2 = LLMModel.objects.create(
            api=self.api, name="v2", model_type="vision", context_window=8192
        )
        request = self._make_request()
        queryset = LLMModel.objects.filter(pk__in=[m1.pk, m2.pk])

        self.admin.set_as_system_vision_model(request, queryset)

        m1.refresh_from_db()
        m2.refresh_from_db()
        self.assertFalse(m1.is_system_vision_model)
        self.assertFalse(m2.is_system_vision_model)

    def test_replaces_previous_system_vision_model(self):
        """Setting a new system vision model clears the previous one."""
        old = LLMModel.objects.create(
            api=self.api,
            name="old-vision",
            model_type="vision",
            context_window=8192,
            is_system_vision_model=True,
        )
        new = LLMModel.objects.create(
            api=self.api,
            name="new-vision",
            model_type="vision",
            context_window=8192,
        )
        request = self._make_request()
        queryset = LLMModel.objects.filter(pk=new.pk)

        self.admin.set_as_system_vision_model(request, queryset)

        old.refresh_from_db()
        new.refresh_from_db()
        self.assertFalse(old.is_system_vision_model)
        self.assertTrue(new.is_system_vision_model)


class SystemEmbeddingModelActionTests(AdminTestBase):
    """Tests for the set_as_system_embedding_model admin action."""

    def test_set_embedding_model(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="embed",
            model_type="embedding",
            context_window=8192,
        )
        request = self._make_request()
        self.admin.set_as_system_embedding_model(request, LLMModel.objects.filter(pk=model.pk))

        model.refresh_from_db()
        self.assertTrue(model.is_system_embedding_model)

    def test_multimodal_embed_accepted(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="multimodal",
            model_type="multimodal_embed",
            context_window=8192,
        )
        request = self._make_request()
        self.admin.set_as_system_embedding_model(request, LLMModel.objects.filter(pk=model.pk))

        model.refresh_from_db()
        self.assertTrue(model.is_system_embedding_model)

    def test_chat_rejected_as_embedding(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="chat",
            model_type="chat",
            context_window=128000,
        )
        request = self._make_request()
        self.admin.set_as_system_embedding_model(request, LLMModel.objects.filter(pk=model.pk))

        model.refresh_from_db()
        self.assertFalse(model.is_system_embedding_model)


class SystemChatModelActionTests(AdminTestBase):
    """Tests for the set_as_system_chat_model admin action."""

    def test_set_chat_model(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="chat",
            model_type="chat",
            context_window=128000,
        )
        request = self._make_request()
        self.admin.set_as_system_chat_model(request, LLMModel.objects.filter(pk=model.pk))

        model.refresh_from_db()
        self.assertTrue(model.is_system_chat_model)

    def test_embedding_rejected_as_chat(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="embed",
            model_type="embedding",
            context_window=8192,
        )
        request = self._make_request()
        self.admin.set_as_system_chat_model(request, LLMModel.objects.filter(pk=model.pk))

        model.refresh_from_db()
        self.assertFalse(model.is_system_chat_model)


class SystemRerankerModelActionTests(AdminTestBase):
    """Tests for the set_as_system_reranker_model admin action."""

    def test_set_reranker_model(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="reranker",
            model_type="reranker",
            context_window=8192,
        )
        request = self._make_request()
        self.admin.set_as_system_reranker_model(request, LLMModel.objects.filter(pk=model.pk))

        model.refresh_from_db()
        self.assertTrue(model.is_system_reranker_model)


class BadgeDisplayTests(AdminTestBase):
    """Tests for system model badge display methods."""

    def test_vision_badge_for_vision_default(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="vision",
            model_type="vision",
            context_window=8192,
            is_system_vision_model=True,
        )
        badge = self.admin.system_vision_badge(model)
        self.assertIn("SYSTEM DEFAULT", badge)
        self.assertIn("6f42c1", badge)  # Purple color

    def test_vision_badge_for_chat_vision_default(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="chat-vision",
            model_type="chat",
            context_window=128000,
            is_system_vision_model=True,
        )
        badge = self.admin.system_vision_badge(model)
        self.assertIn("SYSTEM DEFAULT", badge)

    def test_vision_badge_empty_when_not_default(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="not-default",
            model_type="vision",
            context_window=8192,
            is_system_vision_model=False,
        )
        badge = self.admin.system_vision_badge(model)
        self.assertEqual(badge, "")

    def test_vision_badge_empty_for_wrong_type(self):
        """Even if is_system_vision_model is True, wrong model_type shows no badge."""
        model = LLMModel.objects.create(
            api=self.api,
            name="embed-mislabeled",
            model_type="embedding",
            context_window=8192,
            is_system_vision_model=True,
        )
        badge = self.admin.system_vision_badge(model)
        self.assertEqual(badge, "")

    def test_embedding_badge_shows(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="embed",
            model_type="embedding",
            context_window=8192,
            is_system_embedding_model=True,
        )
        badge = self.admin.system_embedding_badge(model)
        self.assertIn("SYSTEM DEFAULT", badge)

    def test_chat_badge_shows(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="chat",
            model_type="chat",
            context_window=128000,
            is_system_chat_model=True,
        )
        badge = self.admin.system_chat_badge(model)
        self.assertIn("SYSTEM DEFAULT", badge)

    def test_reranker_badge_shows(self):
        model = LLMModel.objects.create(
            api=self.api,
            name="reranker",
            model_type="reranker",
            context_window=8192,
            is_system_reranker_model=True,
        )
        badge = self.admin.system_reranker_badge(model)
        self.assertIn("SYSTEM DEFAULT", badge)


class AdminActionDescriptionTests(TestCase):
    """Tests that admin actions have proper short_description."""

    def setUp(self):
        self.admin = LLMModelAdmin(LLMModel, AdminSite())

    def test_vision_action_description(self):
        self.assertEqual(
            self.admin.set_as_system_vision_model.short_description,
            "Set as System Vision Model",
        )

    def test_embedding_action_description(self):
        self.assertEqual(
            self.admin.set_as_system_embedding_model.short_description,
            "Set as System Embedding Model",
        )

    def test_chat_action_description(self):
        self.assertEqual(
            self.admin.set_as_system_chat_model.short_description,
            "Set as System Chat Model",
        )

    def test_reranker_action_description(self):
        self.assertEqual(
            self.admin.set_as_system_reranker_model.short_description,
            "Set as System Reranker Model",
        )

    def test_vision_badge_description(self):
        self.assertEqual(
            self.admin.system_vision_badge.short_description,
            "Vision Default",
        )
