"""Trace summary derivation — extract lightweight metadata from full traces.

Lives in the storage layer because summaries are a storage concern:
backends produce them, consumers read them.
"""

from __future__ import annotations

import json as _json
from datetime import datetime
from typing import Any


def summarize_trace(trace: dict[str, Any]) -> dict[str, Any]:
    """Derive a lightweight summary from a full trace dict.

    Extracts pre-computed stats from spans. No span content included.

    Note: summaries lose per-span detail. Model cost/latency breakdowns
    are unavailable — only per-trace totals and primary model are captured.
    """
    spans = trace.get("spans", [])

    total_cost = 0.0
    has_cost = False
    total_tokens = 0
    has_tokens = False
    error_count = 0
    kinds: set[str] = set()
    primary_model = None

    for span in spans:
        kind = span.get("kind", "custom")
        kinds.add(kind)

        cost = span.get("cost")
        if cost is not None:
            total_cost += cost
            has_cost = True

        inp_tok = span.get("input_tokens") or 0
        out_tok = span.get("output_tokens") or 0
        if inp_tok or out_tok:
            total_tokens += inp_tok + out_tok
            has_tokens = True

        if span.get("status") == "error":
            error_count += 1

        if primary_model is None and kind == "llm" and span.get("model"):
            primary_model = span["model"]

    # Latency
    latency = None
    started = trace.get("started_at", "")
    ended = trace.get("ended_at", "")
    if started and ended:
        try:
            s = datetime.fromisoformat(str(started).replace("Z", "+00:00"))
            e = datetime.fromisoformat(str(ended).replace("Z", "+00:00"))
            latency = (e - s).total_seconds()
        except (ValueError, TypeError):
            pass

    return {
        "trace_id": trace.get("trace_id", ""),
        "project": trace.get("project", ""),
        "started_at": started,
        "ended_at": ended or None,
        "status": trace.get("status", "ok"),
        "tags": trace.get("tags", []),
        "user_id": trace.get("user_id"),
        "session_id": trace.get("session_id"),
        "input_preview": content_preview(trace.get("input")),
        "output_preview": content_preview(trace.get("output")),
        "span_count": len(spans),
        "span_kinds": sorted(kinds),
        "primary_model": primary_model,
        "total_cost": total_cost if has_cost else None,
        "total_tokens": total_tokens if has_tokens else None,
        "latency": latency,
        "error_count": error_count,
    }


def content_preview(value: Any, max_len: int = 100) -> str:
    """Generate a short text preview of trace input/output."""
    if value is None:
        return ""
    if isinstance(value, str):
        text = value
    elif isinstance(value, (dict, list)):
        text = _json.dumps(value, ensure_ascii=False)
    else:
        text = str(value)
    text = text.replace("\n", " ").strip()
    if len(text) > max_len:
        text = text[:max_len] + "\u2026"
    return text
