import json
from types import SimpleNamespace
from unittest.mock import patch

from django.contrib.auth import get_user_model
from django.core.cache import cache
from django.test import RequestFactory, TestCase, override_settings

from ..constants import MODE_ADMIN, MODE_CUSTOMER
from ..views import _chat_response
from .utils import make_settings

User = get_user_model()


class AiAssistantThrottlingTests(TestCase):
    def setUp(self):
        cache.clear()
        self.factory = RequestFactory()
        self.site = SimpleNamespace(id=77)
        self.staff_user = User.objects.create_user(
            username="staff-throttle",
            password="test-pass",
            is_staff=True,
        )
        self.customer_settings = make_settings()

    def _request(self, payload, user=None, cookies=None):
        request = self.factory.post(
            "/ai-assistant/api/chat/",
            data=json.dumps(payload),
            content_type="application/json",
        )
        request.user = user or SimpleNamespace(is_authenticated=False, is_staff=False)
        request.COOKIES = cookies or {}
        return request

    def _call_chat(self, request, mode=MODE_CUSTOMER, require_staff=False):
        with patch(
            "ocyan.plugin.ai_assistant.views._current_site", return_value=self.site
        ), patch(
            "ocyan.plugin.ai_assistant.views._site_settings",
            return_value=self.customer_settings,
        ):
            return _chat_response(request, mode, require_staff=require_staff)

    @override_settings(
        AI_ASSISTANT_PUBLIC_THROTTLE_LIMIT=1,
        AI_ASSISTANT_PUBLIC_THROTTLE_WINDOW_SECONDS=60,
    )
    def test_customer_rate_limit_returns_429(self):
        request_one = self._request(
            {"message": "Hello one"},
            cookies={"mb_ai_vid": "visitor-1"},
        )
        request_one.META["REMOTE_ADDR"] = "203.0.113.10"
        request_two = self._request(
            {"message": "Hello two"},
            cookies={"mb_ai_vid": "visitor-1"},
        )
        request_two.META["REMOTE_ADDR"] = "203.0.113.10"

        with patch(
            "ocyan.plugin.ai_assistant.views.generate_reply",
            return_value=("Reply", {"provider": "fake"}),
        ):
            first = self._call_chat(request_one)
            second = self._call_chat(request_two)

        self.assertEqual(first.status_code, 200)
        self.assertEqual(second.status_code, 429)
        self.assertEqual(json.loads(second.content)["error"], "rate_limited")

    @override_settings(
        AI_ASSISTANT_ADMIN_THROTTLE_LIMIT=1,
        AI_ASSISTANT_ADMIN_THROTTLE_WINDOW_SECONDS=60,
    )
    def test_admin_rate_limit_returns_429(self):
        request_one = self._request(
            {"message": "Hello one"},
            user=self.staff_user,
        )
        request_one.META["REMOTE_ADDR"] = "203.0.113.11"
        request_two = self._request(
            {"message": "Hello two"},
            user=self.staff_user,
        )
        request_two.META["REMOTE_ADDR"] = "203.0.113.11"

        with patch(
            "ocyan.plugin.ai_assistant.views.generate_reply",
            return_value=("Reply", {"provider": "fake"}),
        ):
            first = self._call_chat(request_one, mode=MODE_ADMIN, require_staff=True)
            second = self._call_chat(request_two, mode=MODE_ADMIN, require_staff=True)

        self.assertEqual(first.status_code, 200)
        self.assertEqual(second.status_code, 429)
        payload = json.loads(second.content)
        self.assertEqual(payload["error"], "rate_limited")
        self.assertEqual(payload["retry_after"], 60)
