import asyncio

import pytest

from app.llm import LLMError, LLMService
from app.planner import RecommendationService
from app.schemas import ModelRecommendationSet, PlanRecommendationRequest


def custom_request() -> PlanRecommendationRequest:
    return PlanRecommendationRequest.model_validate(
        {
            "mode": "custom",
            "preferences": {
                "destination": "深圳市光明区",
                "themes": ["朋友"],
                "pace": "moderate",
                "interests": ["自然风光"],
                "transport": "public_transport",
            },
            "durationMinutes": 120,
            "selectedPoiIds": ["poi_a", "poi_b", "poi_c"],
            "candidates": [
                {
                    "id": poi_id,
                    "name": poi_id,
                    "categoryCode": "urban_park",
                    "tagCodes": ["walking"],
                    "summary": "test",
                    "recommendationIndex": 4,
                }
                for poi_id in ["poi_a", "poi_b", "poi_c", "poi_d"]
            ],
        }
    )


class PartialCustomLLM:
    mode = "mock"

    async def recommend(self, _request):
        return ModelRecommendationSet.model_validate(
            {
                "recommendations": [
                    {"poiId": "poi_c", "reason": "先参观 C", "order": 1},
                    {"poiId": "poi_d", "reason": "模型多选了 D", "order": 2},
                ]
            }
        )


class UnknownPoiLLM:
    mode = "mock"

    async def recommend(self, _request):
        return ModelRecommendationSet.model_validate(
            {
                "recommendations": [
                    {"poiId": "poi_unknown", "reason": "unknown", "order": 1}
                ]
            }
        )


def test_custom_selection_is_owned_by_user_not_model():
    result = asyncio.run(RecommendationService(PartialCustomLLM()).create(custom_request()))
    ids = [item.poi_id for item in result.recommendations]
    assert ids == ["poi_c", "poi_a", "poi_b"]
    assert set(ids) == {"poi_a", "poi_b", "poi_c"}


def test_model_output_with_unknown_id_is_rejected():
    with pytest.raises(LLMError, match="白名单外"):
        asyncio.run(RecommendationService(UnknownPoiLLM()).create(custom_request()))


def test_model_health_states_distinguish_mode_configuration_and_readiness(monkeypatch):
    monkeypatch.delenv("OPENAI_API_KEY", raising=False)
    monkeypatch.delenv("OPENAI_MODEL", raising=False)
    monkeypatch.setenv("LLM_MODE", "mock")
    mock = LLMService()
    assert (mock.mode, mock.configured, mock.ready) == ("mock", False, True)

    monkeypatch.setenv("LLM_MODE", "real")
    unconfigured = LLMService()
    assert (unconfigured.mode, unconfigured.configured, unconfigured.ready) == (
        "real",
        False,
        False,
    )

    monkeypatch.setenv("OPENAI_API_KEY", "test-only-placeholder")
    monkeypatch.setenv("OPENAI_MODEL", "test-only-model")
    configured = LLMService()
    assert (configured.mode, configured.configured, configured.ready) == (
        "real",
        True,
        True,
    )
