"""CLI handler for bandito diagnostics."""

from __future__ import annotations

import json as json_mod

from .diagnostics_impl import AttentionItem, DiagnosticsResult


_STATUS_MARKS = {
    "ok": "\u2713",
    "info": "i",
    "warning": "~",
    "critical": "\u2717",
}


def run_diagnostics_cmd(
    project: str,
    *,
    tag: str | None = None,
    days: int = 14,
    all_traces: bool = False,
    json_output: bool = False,
) -> None:
    from datetime import datetime, timedelta, timezone

    from .diagnostics_impl import run_diagnostics
    from .retrieval import get_retrieval
    from ..storage import get_store

    try:
        r = get_retrieval(project or "default")
    except Exception as e:
        print(f"\n  Could not load traces: {e}\n")
        return

    if project is None:
        store = get_store()
        projects = store.list_projects()
        if not projects:
            if json_output:
                print(json_mod.dumps({"error": "no_traces"}))
            else:
                print("\n  No traces found. Instrument your app with bandito.trace() first.\n")
            return
        if len(projects) == 1:
            project = projects[0]
            r = get_retrieval(project)
        else:
            if json_output:
                print(json_mod.dumps({"error": "multiple_projects", "projects": projects[:20]}))
            else:
                print(f"\n  {len(projects)} projects found. Pick one with --project:\n")
                for p in projects[:5]:
                    print(f"    bandito diagnostics --project {p}")
                print()
            return

    scoped = r.with_time_window(days=days) if not all_traces else r
    traces = scoped.traces(tag=tag)

    if not traces:
        if json_output:
            print(json_mod.dumps({"error": "no_traces", "project": project}))
        else:
            print(f"\n  No traces for project '{project}'.\n")
        return

    evaluations = scoped.evals()

    result = run_diagnostics(project, traces, evaluations)

    if json_output:
        print(json_mod.dumps(result.to_dict(), indent=2, default=str))
        return

    window = f"last {days} days" if not all_traces else "all time"
    _print_diagnostics(result, window=window)


def _print_diagnostics(result: DiagnosticsResult, window: str = "last 14 days") -> None:
    print(f"\n  System Health: {result.project}\n")
    print(f"  {result.trace_count} traces \u00b7 {result.span_count} spans \u00b7 {window}")
    print()

    # -- Reliability --
    print(f"  \u2500\u2500 Reliability {'─' * 43}")
    print()
    print(f"    Success rate:    {result.success_rate:.1%}")
    if result.error_count:
        print(f"    Errors:          {result.error_count} span errors")
    if result.judge_pass_rate is not None:
        print(f"    Judge pass rate: {result.judge_pass_rate:.0%}  ({result.judge_eval_count} scored)")
    if result.human_pass_rate is not None:
        print(f"    Human pass rate: {result.human_pass_rate:.0%}  ({result.human_eval_count} graded)")
    if result.judge_pass_rate is None and result.human_pass_rate is None:
        print(f"    No evaluations yet")
    print()

    # -- Performance --
    print(f"  \u2500\u2500 Performance {'─' * 43}")
    print()
    if result.latency_avg is not None:
        print(f"    Avg:   {_lat(result.latency_avg)}")
        print(f"    p50:   {_lat(result.latency_p50)}")
        print(f"    p95:   {_lat(result.latency_p95)}")
        print(f"    p99:   {_lat(result.latency_p99)}")
    else:
        print(f"    No latency data")
    print()

    # -- Cost --
    print(f"  \u2500\u2500 Cost {'─' * 50}")
    print()
    if result.cost_avg is not None:
        print(f"    Avg per trace:  {_cost(result.cost_avg)}")
        print(f"    Total:          {_cost(result.cost_total)}")
        print(f"    p95:            {_cost(result.cost_p95)}")
    else:
        print(f"    No cost data")
    print()

    # -- Models --
    if result.stats.models:
        print(f"  \u2500\u2500 Models {'─' * 48}")
        print()
        for m in result.stats.models:
            provider = f" ({m.provider})" if m.provider else ""
            parts = [f"{m.count} calls"]
            if m.has_cost:
                parts.append(_cost(m.total_cost))
            parts.append(f"{_lat(m.avg_latency)} avg")
            print(f"    {m.model}{provider}  {' \u00b7 '.join(parts)}")
        print()

    # -- Span Kinds --
    if result.stats.span_kinds:
        print(f"  \u2500\u2500 Span Kinds {'─' * 44}")
        print()
        for sk in result.stats.span_kinds:
            parts = [f"{sk.count} spans"]
            if sk.pct_of_total > 0:
                parts.append(f"{sk.pct_of_total:.0f}% of latency")
            if sk.error_count:
                parts.append(f"{sk.error_count} errors")
            print(f"    {sk.kind:<14s} {' \u00b7 '.join(parts)}")
        print()

    # -- Segments --
    if result.segments:
        print(f"  \u2500\u2500 Segments {'─' * 46}")
        print()
        for seg in result.segments:
            parts = []
            if seg.stats.latency.count > 0:
                parts.append(f"p95: {_lat(seg.stats.latency.p95)}")
            if seg.stats.cost.count > 0:
                parts.append(f"avg cost: {_cost(seg.stats.cost.mean)}")
            if seg.stats.judge_avg_grade is not None:
                parts.append(f"judge: {seg.stats.judge_avg_grade:.0%}")
            if seg.stats.error_count > 0 and seg.stats.span_count > 0:
                rate = seg.stats.error_count / seg.stats.span_count
                if rate > 0.01:
                    parts.append(f"error rate: {rate:.0%}")
            summary = " \u00b7 ".join(parts) if parts else "no data"
            print(f"    {seg.key} ({seg.trace_count} traces)")
            print(f"      {summary}")
        print()

    # -- Attention --
    if result.attention:
        print(f"  \u2500\u2500 Attention {'─' * 45}")
        print()
        for item in result.attention:
            mark = _STATUS_MARKS.get(item.status, "?")
            print(f"  {mark} {item.label}")
            print(f"    {item.detail}")
            for e in item.evidence:
                print(f"    \u00b7 {e}")
        print()

    # -- Next steps --
    if result.human_eval_count == 0 and result.judge_eval_count == 0:
        print(f"  Grade traces to build quality signal:")
        print(f"    bandito tui --project {result.project}")
        print()
    elif result.judge_eval_count == 0:
        print(f"  Scale grading with the judge:")
        print(f"    bandito judge calibrate --project {result.project}")
        print()


def _lat(v: float | None) -> str:
    if v is None:
        return "-"
    if v >= 60:
        return f"{v / 60:.1f}m"
    return f"{v:.2f}s"


def _cost(v: float | None) -> str:
    if v is None:
        return "-"
    if v >= 1.0:
        return f"${v:,.2f}"
    return f"${v:.4f}"
