"""Record one scenario through a locally constructed application."""

import argparse
import os
import sys
from importlib import import_module
from pathlib import Path
from typing import cast

import httpx
import httpx2
from fastapi import FastAPI
from fastapi.testclient import TestClient
from live import ScenarioResult, build_request
from recording import Httpx2RecordingTransport, HttpxRecordingTransport, write_cassettes
from scenario import SCENARIO_PATH, Scenario, load_scenarios
from wire import BACKEND_ROOT, validate_event

EVAL_ROOT = Path(__file__).parent


def main() -> int:
    """Record the selected scenario and persist raw plus safe cassettes."""
    arguments = _parse_arguments()
    scenarios = load_scenarios(arguments.scenarios)
    selected = next((item for item in scenarios if item.key == arguments.scenario), None)
    if selected is None:
        raise SystemExit(f"Unknown scenario: {arguments.scenario}")
    spotify_client_id = _required_environment("SPOTIFY_CLIENT_ID")
    spotify_refresh_token = _required_environment("SPOTIFY_SEED_REFRESH_TOKEN")
    anthropic_api_key = _required_environment("ANTHROPIC_API_KEY")

    spotify_transport = Httpx2RecordingTransport(httpx2.AsyncHTTPTransport())
    anthropic_transport = HttpxRecordingTransport(httpx.AsyncHTTPTransport())
    anthropic_http_client = httpx.AsyncClient(transport=anthropic_transport)
    app = _create_recording_app(
        spotify_client_id,
        spotify_refresh_token,
        anthropic_api_key,
        spotify_transport,
        anthropic_http_client,
    )
    try:
        with TestClient(app, base_url=arguments.base_url) as client:
            _record_selected(client, scenarios, selected, spotify_transport)
    finally:
        raw_root, redacted_root = write_cassettes(
            arguments.fixture_dir,
            selected.key,
            {
                "spotify": spotify_transport.interactions,
                "anthropic": anthropic_transport.interactions,
            },
        )
    print(f"Raw cassettes: {raw_root}")
    print(f"Redacted cassettes: {redacted_root}")
    return 0


def _record_selected(
    client: TestClient,
    scenarios: tuple[Scenario, ...],
    selected: Scenario,
    spotify_transport: Httpx2RecordingTransport,
) -> None:
    if selected.after is not None:
        parent_scenario = next(item for item in scenarios if item.key == selected.after)
        parent = _send_turn(client, parent_scenario, None)
        search_count = _search_count(spotify_transport)
        _send_turn(client, selected, parent)
        if _search_count(spotify_transport) != search_count:
            raise RuntimeError("Refinement issued new Spotify searches")
    else:
        _send_turn(client, selected, None)


def _send_turn(
    client: TestClient,
    scenario: Scenario,
    parent: ScenarioResult | None,
) -> ScenarioResult:
    response = client.post("/api/recommendations", json=build_request(scenario, parent))
    response.raise_for_status()
    result = ScenarioResult(scenario)
    for line in response.text.splitlines():
        event = validate_event(line)
        result.events.append(cast(dict[str, object], event.model_dump(mode="json")))
    if not result.events or result.events[-1].get("type") != "done":
        raise RuntimeError(f"Scenario {scenario.key} did not complete")
    return result


def _create_recording_app(
    spotify_client_id: str,
    spotify_refresh_token: str,
    anthropic_api_key: str,
    spotify_transport: Httpx2RecordingTransport,
    anthropic_http_client: httpx.AsyncClient,
) -> FastAPI:
    backend_path = str(BACKEND_ROOT)
    if backend_path not in sys.path:
        sys.path.insert(0, backend_path)
    config_module = import_module("app.config")
    main_module = import_module("app.main")
    application_settings = config_module.Settings(
        app_mode=config_module.AppMode.LIVE,
        spotify_client_id=spotify_client_id,
        spotify_seed_refresh_token=spotify_refresh_token,
        anthropic_api_key=anthropic_api_key,
    )
    return cast(
        FastAPI,
        main_module.create_app(
            application_settings=application_settings,
            http_transport=spotify_transport,
            anthropic_http_client=anthropic_http_client,
        ),
    )


def _search_count(transport: Httpx2RecordingTransport) -> int:
    return sum("/search" in interaction.url for interaction in transport.interactions)


def _required_environment(name: str) -> str:
    value = os.environ.get(name)
    if not value:
        raise SystemExit(f"{name} is required for recording")
    return value


def _parse_arguments() -> argparse.Namespace:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--scenario", required=True)
    parser.add_argument("--base-url", required=True)
    parser.add_argument("--scenarios", type=Path, default=SCENARIO_PATH)
    parser.add_argument("--fixture-dir", type=Path, default=EVAL_ROOT / "fixtures")
    return parser.parse_args()


if __name__ == "__main__":
    raise SystemExit(main())
