import json

from ..exceptions import LLMGenerationError
from ..prompts import itinerary_system_prompt
from ..providers.factory import get_provider, get_provider_starting_with
from ..schemas import GenerateItineraryRequest


def generate_itinerary(payload: GenerateItineraryRequest) -> dict:
    system_prompt = itinerary_system_prompt(
        trip_spec=payload.trip_spec.model_dump() if payload.trip_spec else None,
        preferences=payload.preferences,
        answered_questions=payload.answered_questions,
        num_options=payload.num_options,
    )
    user_content = {
        "trip_brief": payload.trip_brief,
        "flight_candidates": payload.flight_candidates,
        "stay_candidates": payload.stay_candidates,
        "event_candidates": payload.event_candidates,
    }
    messages = [{"role": "user", "content": json.dumps(user_content)}]

    # When the caller requests a specific provider, still use the fallback chain so
    # rate limits don't crash the request — the requested provider is just promoted to
    # the front with the specific model variant honoured.
    # get_provider() is used when no preference is set (uses LLM_PROVIDER default).
    provider = (
        get_provider_starting_with(payload.provider, model_override=payload.model)
        if payload.provider
        else get_provider()
    )

    try:
        result = provider.complete_json(system_prompt, messages)
    except Exception as exc:
        raise LLMGenerationError("Failed to generate itinerary", str(exc)) from exc

    # Inject provider metadata so the caller can surface fallback info to the user.
    result["provider_used"] = provider.last_used
    result["skipped_providers"] = provider.skipped

    return result
