import copy
import pytest

from app.services.wine_pairing.config import WinePairingServiceConfig
from app.services.wine_pairing.models.requests import (
    WinePairingRequest,
    WinePairingFood,
    WineCardItem,
)
from app.services.wine_pairing.service import WinePairingService
from app.services.wine_pairing import service as service_module


class DeterministicRng:
    def randint(self, a, b):
        return a  # deterministic seed

    def shuffle(self, seq):
        return seq

    def sample(self, population, k):
        return list(population[:k])


@pytest.mark.anyio("asyncio")
async def test_match_wines_includes_user_prompt(monkeypatch):
    captured_prompt = {}

    async def fake_json_completion(*, api_key, model, prompt, operation_id, service, **kwargs):
        captured_prompt["value"] = prompt
        fake_response = {
            "language": "en",
            "results": [
                {
                    "food": "Seared Salmon",
                    "matches": [
                        {
                            "wine": "Sunset Rosé",
                            "reason": "Bright acidity lifts the salmon.",
                            "vivino_rating": "Unknown",
                            "price": "Unknown",
                        }
                    ],
                    "suggestions": [],
                    "notes": "Focus on refreshing rosé picks.",
                }
            ],
        }
        fake_metrics = {
            "input_tokens": 1,
            "output_tokens": 1,
            "cost": 0.0,
            "model": model,
        }
        return fake_response, fake_metrics

    monkeypatch.setattr(service_module.openai_client, "json_completion", fake_json_completion)

    config = WinePairingServiceConfig(
        enabled=True,
        openai_api_key="test-key",
        wine_pairing_model="gpt-test",
        vivino_lookup_model="gpt-test",
        vivino_lookup_enabled=False,
    )
    service = WinePairingService(config)

    request = WinePairingRequest(
        language="en",
        foods=[WinePairingFood(name="Seared Salmon")],
        wines=[WineCardItem(name="Sunset Rosé", price="$40")],
        user_prompt="add 3 more bottles rose wine",
    )

    await service.match_wines(request)

    assert "add 3 more bottles rose wine" in captured_prompt["value"]
    assert "Additional diner instructions" in captured_prompt["value"]


@pytest.mark.anyio("asyncio")
async def test_match_wines_holds_extra_matches_with_user_prompt(monkeypatch):
    template_response = {
        "language": "en",
        "results": [
            {
                "food": "Steak",
                "matches": [
                    {"wine": "Wine A", "reason": "R1", "vivino_rating": "Unknown", "price": "Unknown"},
                    {"wine": "Wine B", "reason": "R2", "vivino_rating": "Unknown", "price": "Unknown"},
                    {"wine": "Wine C", "reason": "R3", "vivino_rating": "Unknown", "price": "Unknown"},
                    {"wine": "Wine D", "reason": "R4", "vivino_rating": "Unknown", "price": "Unknown"},
                ],
                "suggestions": [],
                "notes": "Standard notes.",
            }
        ],
    }

    async def fake_json_completion(**kwargs):
        return copy.deepcopy(template_response), {
            "input_tokens": 1,
            "output_tokens": 1,
            "cost": 0.0,
            "model": kwargs["model"],
        }

    monkeypatch.setattr(service_module.openai_client, "json_completion", fake_json_completion)

    config = WinePairingServiceConfig(
        enabled=True,
        openai_api_key="test-key",
        wine_pairing_model="gpt-test",
        vivino_lookup_model="gpt-test",
        vivino_lookup_enabled=False,
    )
    service = WinePairingService(config)
    service._rng = DeterministicRng()

    with_prompt = WinePairingRequest(
        language="en",
        foods=[WinePairingFood(name="Steak")],
        wines=[WineCardItem(name="Wine A")],
        user_prompt="add 3 more wine suggestions",
    )
    without_prompt = WinePairingRequest(
        language="en",
        foods=[WinePairingFood(name="Steak")],
        wines=[WineCardItem(name="Wine A")],
    )

    result_with_prompt = await service.match_wines(with_prompt)
    result_without_prompt = await service.match_wines(without_prompt)

    matches_with_prompt = result_with_prompt["results"][0]["matches"]
    matches_without_prompt = result_without_prompt["results"][0]["matches"]

    assert len(matches_with_prompt) == 4
    assert len(matches_without_prompt) == 2


@pytest.mark.anyio("asyncio")
async def test_match_wines_matches_html_encoded_wine_names_for_price_lookup(monkeypatch):
    async def fake_json_completion(**kwargs):
        return {
            "language": "bg",
            "results": [
                {
                    "food": "Салата Over The Top",
                    "matches": [
                        {
                            "wine": "Riesling & Varna Misket",
                            "reason": "R1",
                            "vivino_rating": "Unknown",
                            "price": "Unknown",
                        }
                    ],
                    "suggestions": [],
                    "notes": "",
                }
            ],
        }, {
            "input_tokens": 1,
            "output_tokens": 1,
            "cost": 0.0,
            "model": kwargs["model"],
        }

    monkeypatch.setattr(service_module.openai_client, "json_completion", fake_json_completion)

    config = WinePairingServiceConfig(
        enabled=True,
        openai_api_key="test-key",
        wine_pairing_model="gpt-test",
        vivino_lookup_model="gpt-test",
        vivino_lookup_enabled=False,
    )
    service = WinePairingService(config)
    service._rng = DeterministicRng()

    request = WinePairingRequest(
        language="bg",
        foods=[WinePairingFood(name="Салата Over The Top")],
        wines=[WineCardItem(name="Riesling &amp; Varna Misket", price="€ 9")],
        user_prompt="recommend 1 wine",
    )

    result = await service.match_wines(request)

    assert result["results"][0]["matches"][0]["price"] == "€ 9"
