"""HTML routes for trace views."""

from __future__ import annotations

from typing import Any

from fastapi import APIRouter, Query, Request
from fastapi.responses import HTMLResponse

router = APIRouter(tags=["traces"])


def _build_span_tree(flat_spans: list[dict[str, Any]]) -> list[dict[str, Any]]:
    """Build a hierarchical tree from flat spans using parent_span_id.

    Returns root spans, each with a ``children`` list nested recursively.
    Children are sorted by sequence_number then started_at so the
    chronological call order is preserved.
    """
    span_map: dict[str, dict[str, Any]] = {}
    roots: list[dict[str, Any]] = []

    # First pass: index all spans and initialise children list
    for span in flat_spans:
        span["children"] = []
        span_map[span["span_id"]] = span

    # Second pass: link children to parents
    for span in flat_spans:
        parent_id = span.get("parent_span_id")
        if parent_id and parent_id in span_map:
            span_map[parent_id]["children"].append(span)
        else:
            roots.append(span)

    # Sort children recursively by sequence_number then started_at
    def _sort(node: dict[str, Any]) -> None:
        node["children"].sort(
            key=lambda s: (
                s.get("sequence_number") or 0,
                s.get("started_at") or "",
            )
        )
        for child in node["children"]:
            _sort(child)

    roots.sort(
        key=lambda s: (
            s.get("sequence_number") or 0,
            s.get("started_at") or "",
        )
    )
    for root in roots:
        _sort(root)

    return roots


def _compute_waterfall(
    root_spans: list[dict[str, Any]],
    trace_started_at: Any,
    trace_duration_ms: int | None,
) -> None:
    """Add ``waterfall_left_pct`` and ``waterfall_width_pct`` to each span.

    These are used by the template to render a mini timeline bar.
    """
    if not trace_duration_ms or trace_duration_ms <= 0 or not trace_started_at:
        return

    total_ms = float(trace_duration_ms)

    def _visit(span: dict[str, Any]) -> None:
        started = span.get("started_at")
        dur = span.get("duration_ms")
        if started and trace_started_at:
            try:
                offset_ms = (started - trace_started_at).total_seconds() * 1000
                left_pct = max(0.0, min(offset_ms / total_ms * 100, 100.0))
                width_pct = max(
                    0.5, min((dur or 0) / total_ms * 100, 100.0 - left_pct)
                )
            except (TypeError, AttributeError):
                left_pct = 0.0
                width_pct = 0.0
            span["waterfall_left_pct"] = round(left_pct, 2)
            span["waterfall_width_pct"] = round(width_pct, 2)
        for child in span.get("children", []):
            _visit(child)

    for root in root_spans:
        _visit(root)


@router.get("/", response_class=HTMLResponse)
async def dashboard_home(request: Request) -> HTMLResponse:
    """Dashboard overview page."""
    templates = request.app.state.templates
    repo = request.app.state.repo

    stats = await repo.get_overview_stats()
    traces, total = await repo.list_traces(page=1, per_page=10)
    models = await repo.get_model_breakdown()
    daily_usage = await repo.get_daily_usage(days=30)

    # Serialize for Chart.js (dates → strings, Decimals → floats)
    for row in daily_usage:
        if row.get("day"):
            row["day"] = row["day"].strftime("%b %d")
        for k, v in row.items():
            if hasattr(v, "as_tuple"):  # Decimal
                row[k] = float(v)

    return templates.TemplateResponse(
        "base.html",
        {
            "request": request,
            "page_title": "Overview",
            "stats": stats,
            "recent_traces": traces,
            "total_traces": total,
            "top_models": models[:5],
            "daily_usage": daily_usage,
        },
    )


@router.get("/traces", response_class=HTMLResponse)
async def trace_list(
    request: Request,
    page: int = Query(1, ge=1),
    per_page: int = Query(50, ge=1, le=200),
    status: str | None = None,
    search: str | None = None,
    model: str | None = None,
) -> HTMLResponse:
    """Trace list page."""
    templates = request.app.state.templates
    repo = request.app.state.repo

    traces, total = await repo.list_traces(
        page=page,
        per_page=per_page,
        status=status,
        search=search,
        model=model,
    )

    return templates.TemplateResponse(
        "traces/list.html",
        {
            "request": request,
            "page_title": "Traces",
            "traces": traces,
            "total": total,
            "page": page,
            "per_page": per_page,
            "pages": (total + per_page - 1) // per_page if per_page else 1,
            "filters": {"status": status, "search": search, "model": model},
        },
    )


@router.get("/traces/{trace_id}", response_class=HTMLResponse)
async def trace_detail(request: Request, trace_id: str) -> HTMLResponse:
    """Trace detail page with chain view."""
    templates = request.app.state.templates
    repo = request.app.state.repo

    trace = await repo.get_trace(trace_id)
    chain = await repo.get_trace_chain(trace_id)
    tool_calls = await repo.get_tool_calls_for_trace(trace_id)

    # Build proper hierarchical tree from flat spans
    root_spans = _build_span_tree(chain)

    # Compute waterfall timing data
    if trace:
        _compute_waterfall(
            root_spans,
            trace.get("created_at"),
            trace.get("total_duration_ms"),
        )

    return templates.TemplateResponse(
        "traces/detail.html",
        {
            "request": request,
            "page_title": f"Trace {trace_id[:8]}",
            "trace": trace,
            "root_spans": root_spans,
            "tool_calls": tool_calls,
        },
    )
