"""Tests for the OpenAI-based extractor with mocked API responses."""

from __future__ import annotations

import json
from datetime import datetime
from unittest.mock import MagicMock, patch

import pytest

from agent_samochodowy.config import Settings
from agent_samochodowy.extraction.openai_extractor import extract_with_openai
from agent_samochodowy.models import Post


def _post(text: str, group: str = "TIR części zamienne") -> Post:
    return Post(
        post_id="test_001",
        group=group,
        author_name="Test User",
        author_profile_url="https://fb.com/test",
        text=text,
        timestamp=datetime(2025, 1, 15),
        post_url="https://fb.com/posts/001",
    )


def _settings(**overrides) -> Settings:
    defaults = {
        "llm_api_key": "sk-test-fake-key",
        "llm_provider": "openai",
        "llm_model": "gpt-4o-mini",
    }
    defaults.update(overrides)
    return Settings(**defaults)


def _mock_openai_response(data: dict) -> MagicMock:
    """Build a mock ChatCompletion response with a function call."""
    tool_call = MagicMock()
    tool_call.function.name = "extract_part_query"
    tool_call.function.arguments = json.dumps(data)

    message = MagicMock()
    message.tool_calls = [tool_call]

    choice = MagicMock()
    choice.message = message

    response = MagicMock()
    response.choices = [choice]
    return response


class TestOpenAIExtractor:
    @patch("agent_samochodowy.extraction.openai_extractor.openai.OpenAI")
    def test_extracts_part_request(self, mock_openai_cls):
        mock_client = MagicMock()
        mock_openai_cls.return_value = mock_client
        mock_client.chat.completions.create.return_value = _mock_openai_response({
            "is_part_request": True,
            "brand": "VOLVO",
            "model": "FH13",
            "year": None,
            "category": "pompa wody",
            "engine_code": None,
            "oe_numbers": ["20744939"],
            "reason": "Użytkownik szuka pompy wody do Volvo FH13 z numerem OE",
        })

        result = extract_with_openai(
            _post("Szukam pompy wody do Volvo FH13, numer 20744939. Pilne!"),
            _settings(),
        )

        assert result.is_part_request is True
        assert result.brand == "VOLVO"
        assert result.model == "FH13"
        assert result.category == "pompa wody"
        assert "20744939" in result.oe_numbers
        assert result.reason

    @patch("agent_samochodowy.extraction.openai_extractor.openai.OpenAI")
    def test_rejects_selling_post(self, mock_openai_cls):
        mock_client = MagicMock()
        mock_openai_cls.return_value = mock_client
        mock_client.chat.completions.create.return_value = _mock_openai_response({
            "is_part_request": False,
            "brand": "MAN",
            "model": "TGA",
            "year": None,
            "category": "klocki hamulcowe",
            "engine_code": None,
            "oe_numbers": [],
            "reason": "Post sprzedażowy, nie zapytanie kupujące",
        })

        result = extract_with_openai(
            _post("Sprzedam klocki hamulcowe MAN TGA, nowe, 200 zł"),
            _settings(),
        )

        assert result.is_part_request is False

    @patch("agent_samochodowy.extraction.openai_extractor.openai.OpenAI")
    def test_handles_engine_code(self, mock_openai_cls):
        mock_client = MagicMock()
        mock_openai_cls.return_value = mock_client
        mock_client.chat.completions.create.return_value = _mock_openai_response({
            "is_part_request": True,
            "brand": "OPEL",
            "model": "Astra F",
            "year": None,
            "category": "alternator",
            "engine_code": "C18NZ",
            "oe_numbers": [],
            "reason": "Zapytanie o alternator z kodem silnika",
        })

        result = extract_with_openai(
            _post("Kupię alternator do Opla Astry F, silnik C18NZ"),
            _settings(),
        )

        assert result.is_part_request is True
        assert result.engine_code == "C18NZ"
        assert result.brand == "OPEL"

    @patch("agent_samochodowy.extraction.openai_extractor.openai.OpenAI")
    def test_oe_normalization(self, mock_openai_cls):
        """OE numbers should be stripped of spaces/dashes and uppercased."""
        mock_client = MagicMock()
        mock_openai_cls.return_value = mock_client
        mock_client.chat.completions.create.return_value = _mock_openai_response({
            "is_part_request": True,
            "brand": None,
            "model": None,
            "year": None,
            "category": None,
            "engine_code": None,
            "oe_numbers": ["51.06500-6408", "A 000 180 27 01"],
            "reason": "OE numbers in text",
        })

        result = extract_with_openai(
            _post("Szukam 51.06500-6408 albo A 000 180 27 01"),
            _settings(),
        )

        assert "51065006408" in result.oe_numbers
        assert "A0001802701" in result.oe_numbers

    @patch("agent_samochodowy.extraction.openai_extractor.openai.OpenAI")
    def test_no_tool_call_returns_false(self, mock_openai_cls):
        """When OpenAI returns no function call, treat as not a request."""
        mock_client = MagicMock()
        mock_openai_cls.return_value = mock_client

        message = MagicMock()
        message.tool_calls = None
        choice = MagicMock()
        choice.message = message
        response = MagicMock()
        response.choices = [choice]
        mock_client.chat.completions.create.return_value = response

        result = extract_with_openai(_post("Random text"), _settings())
        assert result.is_part_request is False

    @patch("agent_samochodowy.extraction.openai_extractor.openai.OpenAI")
    def test_passes_correct_model(self, mock_openai_cls):
        """Verify the model parameter is passed through from settings."""
        mock_client = MagicMock()
        mock_openai_cls.return_value = mock_client
        mock_client.chat.completions.create.return_value = _mock_openai_response({
            "is_part_request": False,
            "brand": None, "model": None, "year": None,
            "category": None, "engine_code": None,
            "oe_numbers": [], "reason": "test",
        })

        extract_with_openai(_post("test"), _settings(llm_model="gpt-4o-mini"))

        call_kwargs = mock_client.chat.completions.create.call_args.kwargs
        assert call_kwargs["model"] == "gpt-4o-mini"


class TestProviderDispatch:
    """Test that extraction/__init__.py dispatches correctly."""

    @patch("agent_samochodowy.extraction.openai_extractor.extract_with_openai")
    def test_openai_dispatch(self, mock_extract):
        from agent_samochodowy.extraction import _extract_with_llm

        mock_extract.return_value = MagicMock(is_part_request=False)
        settings = _settings(llm_provider="openai")
        _extract_with_llm(_post("test"), settings)
        mock_extract.assert_called_once()

    @patch("agent_samochodowy.extraction.llm_extractor.extract_with_llm")
    def test_anthropic_dispatch(self, mock_extract):
        from agent_samochodowy.extraction import _extract_with_llm

        mock_extract.return_value = MagicMock(is_part_request=False)
        settings = _settings(llm_provider="anthropic")
        _extract_with_llm(_post("test"), settings)
        mock_extract.assert_called_once()
