"""Backtest run artifact endpoints."""
from __future__ import annotations

import json
from pathlib import Path
from typing import Any

from fastapi import APIRouter, HTTPException, Query

from apps.web.dependencies import get_runs_dir

router = APIRouter(prefix="/runs", tags=["runs"])


def _run_path(run_id: str, runs_dir: Path) -> Path:
    """Find a run directory by run_id."""
    # run_id IS the directory name
    p = runs_dir / run_id
    if p.is_dir():
        return p
    # Fallback: search nested (some runs may be nested)
    for d in runs_dir.iterdir():
        if d.is_dir() and d.name == run_id:
            return d
    raise FileNotFoundError(f"Run not found: {run_id}")


def _parquet_to_records(path: Path) -> list[dict[str, Any]]:
    """Read a Parquet file and return list of dicts."""
    import pyarrow.parquet as pq
    table = pq.read_table(path)
    return table.to_pydict()


def _parquet_to_records_list(path: Path) -> list[dict[str, Any]]:
    """Read a Parquet file and convert to list of row dicts."""
    import pyarrow.parquet as pq
    table = pq.read_table(path)
    return [
        {col: (table.column(col)[i].as_py()) for col in table.column_names}
        for i in range(len(table))
    ]


@router.get("")
def list_runs(
    experiment: str | None = Query(None, description="Filter by experiment name"),
    limit: int = Query(50, le=200),
    offset: int = 0,
) -> dict[str, Any]:
    """List available backtest runs."""
    runs_dir = get_runs_dir()
    if not runs_dir.exists():
        return {"runs": [], "total": 0}

    runs = []
    for d in sorted(runs_dir.iterdir(), key=lambda p: p.stat().st_mtime, reverse=True):
        if not d.is_dir():
            continue
        metadata_file = d / "metadata.json"
        if not metadata_file.exists():
            continue
        try:
            meta = json.loads(metadata_file.read_text())
        except Exception:
            continue
        if experiment and meta.get("experiment_name") != experiment:
            continue
        runs.append({
            "run_id": d.name,
            "experiment_name": meta.get("experiment_name"),
            "split_name": meta.get("split_name"),
            "started_at": meta.get("started_at"),
            "finished_at": meta.get("finished_at"),
            "duration_seconds": meta.get("duration_seconds"),
        })

    total = len(runs)
    return {"runs": runs[offset : offset + limit], "total": total}


@router.get("/{run_id}/metadata")
def get_run_metadata(run_id: str) -> dict[str, Any]:
    """Get run metadata."""
    runs_dir = get_runs_dir()
    try:
        run_path = _run_path(run_id, runs_dir)
    except FileNotFoundError:
        raise HTTPException(status_code=404, detail=f"Run not found: {run_id}")

    metadata_file = run_path / "metadata.json"
    if not metadata_file.exists():
        raise HTTPException(status_code=404, detail="metadata.json not found")
    return json.loads(metadata_file.read_text())


@router.get("/{run_id}/metrics")
def get_run_metrics(run_id: str) -> dict[str, Any]:
    """Get metrics summary for a run."""
    runs_dir = get_runs_dir()
    try:
        run_path = _run_path(run_id, runs_dir)
    except FileNotFoundError:
        raise HTTPException(status_code=404, detail=f"Run not found: {run_id}")

    metrics_file = run_path / "metrics" / "metrics_summary.json"
    if not metrics_file.exists():
        raise HTTPException(status_code=404, detail="metrics_summary.json not found")
    return json.loads(metrics_file.read_text())


@router.get("/{run_id}/equity-curve")
def get_equity_curve(run_id: str) -> dict[str, Any]:
    """Get equity curve data for a run (from Parquet)."""
    runs_dir = get_runs_dir()
    try:
        run_path = _run_path(run_id, runs_dir)
    except FileNotFoundError:
        raise HTTPException(status_code=404, detail=f"Run not found: {run_id}")

    parquet_file = run_path / "artifacts" / "equity_curve.parquet"
    csv_file = run_path / "artifacts" / "equity_curve.csv"

    if parquet_file.exists():
        try:
            records = _parquet_to_records_list(parquet_file)
            return {"data": records, "format": "parquet"}
        except Exception:
            pass

    if csv_file.exists():
        import csv
        with open(csv_file) as f:
            reader = csv.DictReader(f)
            records = list(reader)
        return {"data": records, "format": "csv"}

    raise HTTPException(status_code=404, detail="Equity curve data not found")


@router.get("/{run_id}/trade-blotter")
def get_trade_blotter(
    run_id: str,
    limit: int = Query(200, le=2000),
    offset: int = 0,
) -> dict[str, Any]:
    """Get trade blotter for a run (paginated)."""
    runs_dir = get_runs_dir()
    try:
        run_path = _run_path(run_id, runs_dir)
    except FileNotFoundError:
        raise HTTPException(status_code=404, detail=f"Run not found: {run_id}")

    parquet_file = run_path / "artifacts" / "trade_blotter.parquet"
    csv_file = run_path / "artifacts" / "trade_blotter.csv"

    if parquet_file.exists():
        try:
            records = _parquet_to_records_list(parquet_file)
            total = len(records)
            return {"trades": records[offset : offset + limit], "total": total}
        except Exception:
            pass

    if csv_file.exists():
        import csv
        with open(csv_file) as f:
            reader = csv.DictReader(f)
            records = list(reader)
        total = len(records)
        return {"trades": records[offset : offset + limit], "total": total}

    raise HTTPException(status_code=404, detail="Trade blotter not found")


@router.get("/{run_id}/per-engine-metrics")
def get_per_engine_metrics(run_id: str) -> dict[str, Any]:
    """Get per-engine metrics breakdown."""
    runs_dir = get_runs_dir()
    try:
        run_path = _run_path(run_id, runs_dir)
    except FileNotFoundError:
        raise HTTPException(status_code=404, detail=f"Run not found: {run_id}")

    pem_file = run_path / "artifacts" / "per_engine_metrics.json"
    if not pem_file.exists():
        return {"engines": {}}
    return json.loads(pem_file.read_text())
