from ipaddress import ip_network
from unittest.mock import patch

from django.conf import settings
from django.test import RequestFactory, TestCase, override_settings
from rest_framework.test import APIClient

from chat.api.permissions import InternalServiceAllowedIP
from chat.knowledge.asf_faq_ptbr import try_asf_canned_answer
from chat.services.chat_service import ChatService


@override_settings(
    INTERNAL_API_KEY="test-internal-key",
    OPENAI_API_KEY="test-openai-key",
    OPENAI_MODEL="gpt-4.1-mini",
    CHAT_SYSTEM_PROMPT="test prompt",
    MAX_QUESTION_LENGTH=100,
)
class ChatMessageApiTests(TestCase):
    def setUp(self):
        self.client = APIClient()
        self.url = "/api/v1/chat/messages/"

    @patch("chat.api.views.ChatService.send_message")
    def test_returns_chat_response_for_authenticated_request(self, mock_send_message):
        mock_send_message.return_value = type(
            "ChatResponse",
            (),
            {
                "request_id": "req-123",
                "answer_text": "Resposta profissional",
                "conversation_id": "conv-123",
                "model": settings.OPENAI_MODEL,
                "status": "ok",
                "error": None,
                "usage": {"input_tokens": 120, "output_tokens": 30},
            },
        )()

        response = self.client.post(
            self.url,
            {
                "request_id": "req-123",
                "source": "aluguesemfiador",
                "context": {"modulo": "formfianca", "registro_id": 123},
                "conversation": {"conversation_id": "conv-123", "message": "Quais documentos preciso?"},
                "case_data": {"documentos_pendentes": ["RG", "Comprovante de renda"]},
            },
            format="json",
            HTTP_AUTHORIZATION="Bearer test-internal-key",
        )

        self.assertEqual(response.status_code, 200)
        self.assertEqual(
            response.json(),
            {
                "request_id": "req-123",
                "conversation_id": "conv-123",
                "answer": {"text": "Resposta profissional", "role": "assistant"},
                "status": "ok",
                "error": None,
                "usage": {"input_tokens": 120, "output_tokens": 30},
                "model": settings.OPENAI_MODEL,
            },
        )

    def test_rejects_request_without_api_key(self):
        response = self.client.post(
            self.url,
            {"message": "Teste"},
            format="json",
        )

        self.assertEqual(response.status_code, 401)
        self.assertEqual(response.json()["error"]["code"], "authentication_failed")

    def test_validates_message_length(self):
        response = self.client.post(
            self.url,
            {"conversation": {"message": "x" * 101}},
            format="json",
            HTTP_X_API_KEY="test-internal-key",
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["error"]["code"], "bad_request")

    def test_requires_message_in_legacy_or_conversation_format(self):
        response = self.client.post(
            self.url,
            {"context": {"modulo": "formfianca"}},
            format="json",
            HTTP_X_API_KEY="test-internal-key",
        )

        self.assertEqual(response.status_code, 400)
        self.assertEqual(response.json()["error"]["code"], "bad_request")

    def test_healthcheck_is_public(self):
        response = self.client.get("/api/v1/health/")

        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json(), {"status": "ok"})

    @patch("chat.api.views.ChatService.send_message")
    def test_rejects_when_client_ip_not_in_allowlist(self, mock_send_message):
        mock_send_message.return_value = type(
            "ChatResponse",
            (),
            {
                "request_id": "req-123",
                "answer_text": "ok",
                "conversation_id": "conv-123",
                "model": settings.OPENAI_MODEL,
                "status": "ok",
                "error": None,
                "usage": None,
            },
        )()

        with override_settings(
            INTERNAL_API_ALLOWED_NETWORKS=[ip_network("127.0.0.0/8")],
        ):
            self.client.defaults["REMOTE_ADDR"] = "10.0.0.2"
            response = self.client.post(
                self.url,
                {"message": "Teste"},
                format="json",
                HTTP_AUTHORIZATION="Bearer test-internal-key",
            )

        self.assertEqual(response.status_code, 403)
        self.assertEqual(response.json()["error"]["code"], "permission_denied")
        self.client.defaults.pop("REMOTE_ADDR", None)


class AsfFaqCannedTests(TestCase):
    def test_matches_company_question(self):
        answer = try_asf_canned_answer("O que é o ASF Alugue Sem Fiador?")
        self.assertIsNotNone(answer)
        self.assertIn("corretora", answer.lower())

    def test_matches_contact(self):
        answer = try_asf_canned_answer("Como entrar em contato com vocês?")
        self.assertIsNotNone(answer)
        self.assertIn("0800", answer)

    def test_skips_cpf_lookup(self):
        answer = try_asf_canned_answer("Tem seguro vigente para o CPF 123.456.789-00?")
        self.assertIsNone(answer)

    def test_chat_service_returns_canned_faq(self):
        service = ChatService(openai_service=object())
        result = service.send_message(message="O que é o sistema SOU ASF?")
        self.assertEqual(result.model, "canned-faq")
        self.assertIn("SOU ASF", result.answer_text)
        self.assertEqual(result.usage["input_tokens"], 0)


class InternalServiceAllowedIPTests(TestCase):
    def setUp(self):
        self.factory = RequestFactory()
        self.permission = InternalServiceAllowedIP()

    @override_settings(INTERNAL_API_ALLOWED_NETWORKS=[])
    def test_empty_allowlist_allows_any_ip(self):
        request = self.factory.post("/api/v1/chat/messages/")
        request.META["REMOTE_ADDR"] = "203.0.113.99"
        self.assertTrue(self.permission.has_permission(request, None))

    @override_settings(INTERNAL_API_ALLOWED_NETWORKS=[ip_network("10.0.0.0/8")])
    def test_allowlist_accepts_matching_ip(self):
        request = self.factory.post("/api/v1/chat/messages/")
        request.META["REMOTE_ADDR"] = "10.1.2.3"
        self.assertTrue(self.permission.has_permission(request, None))

    @override_settings(INTERNAL_API_ALLOWED_NETWORKS=[ip_network("10.0.0.0/8")])
    def test_allowlist_rejects_other_ip(self):
        request = self.factory.post("/api/v1/chat/messages/")
        request.META["REMOTE_ADDR"] = "192.168.1.1"
        self.assertFalse(self.permission.has_permission(request, None))
