"""Event summaries, replay HTML/text, filters, and time parsing for CLI commands."""

from __future__ import annotations

import html
import json
import re
from datetime import datetime
from typing import Any

import typer

REPLAY_HTML_CSS = """
body{background:#f8fafc;color:#0f172a;font-family:ui-sans-serif,system-ui,sans-serif;margin:0;padding:24px}
main{max-width:56rem;margin:0 auto}
.title{font-size:1.5rem;font-weight:600;margin:0 0 .5rem}
.sub{font-size:.875rem;color:#475569;margin:0 0 1rem}
.card{
  background:#fff;
  border:1px solid #e2e8f0;
  border-radius:.5rem;
  box-shadow:0 1px 2px rgba(15,23,42,.08);
  padding:1rem
}
.row{
  font-family:ui-monospace,SFMono-Regular,monospace;
  font-size:.875rem;
  white-space:pre-wrap;
  border-bottom:1px solid #e2e8f0;
  padding:.25rem 0
}
.foot{font-size:.75rem;color:#64748b;margin-top:1rem}
"""


def event_summary(events: list[dict[str, Any]]) -> dict[str, Any]:
    summary: dict[str, Any] = {
        "status": "unknown",
        "workflow_name": None,
        "workflow_version": None,
        "state_count": 0,
        "transition_count": 0,
        "llm_calls": 0,
        "tool_calls": 0,
        "approvals": 0,
        "last_ts": None,
        "tags": {},
        "run_metadata": {},
        "experiment": {},
    }
    for event in events:
        summary["last_ts"] = event.get("ts")
        typ = event.get("type")
        payload = event.get("payload") or {}
        if typ == "run_started":
            summary["workflow_name"] = payload.get("workflow_name")
            summary["workflow_version"] = payload.get("workflow_version")
            summary["tags"] = payload.get("tags") or {}
            summary["run_metadata"] = payload.get("run_metadata") or {}
            exp = payload.get("experiment")
            summary["experiment"] = exp if isinstance(exp, dict) else {}
        elif typ == "state_entered":
            summary["state_count"] += 1
        elif typ == "transition":
            summary["transition_count"] += 1
        elif typ == "llm_request":
            summary["llm_calls"] += 1
        elif typ == "tool_call":
            summary["tool_calls"] += 1
        elif typ == "approval_requested":
            summary["approvals"] += 1
        elif typ == "run_completed":
            summary["status"] = payload.get("status", summary["status"])
        elif typ == "run_paused":
            summary["status"] = "paused"
    return summary


def replay_timeline_lines(events: list[dict[str, Any]]) -> list[str]:
    lines: list[str] = []
    for e in events:
        typ = e.get("type")
        seq = e.get("seq")
        payload = e.get("payload") or {}
        line = f"{seq:04d}  {typ}"
        if typ in {
            "state_entered",
            "state_exited",
            "transition",
            "run_failed",
            "approval_requested",
            "structured_output",
            "tool_call",
            "tool_result",
        }:
            raw = json.dumps(payload, ensure_ascii=False, default=str)
            if len(raw) > 500:
                raw = raw[:497] + "..."
            line += f"  {raw}"
        lines.append(line)
    return lines


def replay_html(run_id: str, events: list[dict[str, Any]]) -> str:
    summary = event_summary(events)
    title = html.escape(f"replayt run {run_id}")
    rows = []
    pre = '<pre class="row">'
    for line in replay_timeline_lines(events):
        rows.append(f"{pre}{html.escape(line)}</pre>")
    body_rows = "\n".join(rows)
    return f"""<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1"/>
  <title>{title}</title>
  <style>{REPLAY_HTML_CSS}</style>
</head>
<body>
  <main>
    <h1 class="title">{title}</h1>
    <p class="sub">
      status={html.escape(str(summary.get("status")))}
      workflow={html.escape(str(summary.get("workflow_name")))}@{html.escape(str(summary.get("workflow_version")))}
    </p>
    <section class="card">
      {body_rows}
    </section>
    <p class="foot">Generated by replayt (no model calls; timeline from local event store).</p>
  </main>
</body>
</html>
"""


def parse_tag_filters(raw: list[str] | None) -> dict[str, str]:
    if not raw:
        return {}
    out: dict[str, str] = {}
    for t in raw:
        if "=" not in t:
            raise typer.BadParameter(f"Tag filter must be key=value, got: {t!r}")
        k, v = t.split("=", 1)
        out[k] = v
    return out


def tags_match(run_tags: dict[str, str], filters: dict[str, str]) -> bool:
    return all(run_tags.get(k) == v for k, v in filters.items())


def parse_meta_filters(raw: list[str] | None) -> dict[str, str]:
    if not raw:
        return {}
    out: dict[str, str] = {}
    for t in raw:
        if "=" not in t:
            raise typer.BadParameter(f"run-meta filter must be key=value, got: {t!r}")
        k, v = t.split("=", 1)
        out[k] = v
    return out


def run_meta_filters_match(run_meta: dict[str, Any], filters: dict[str, str]) -> bool:
    return all(k in run_meta and str(run_meta[k]) == v for k, v in filters.items())


def experiment_filters_match(run_exp: dict[str, Any], filters: dict[str, str]) -> bool:
    return all(k in run_exp and str(run_exp[k]) == v for k, v in filters.items())


def parse_iso_ts(ts: str | None) -> datetime | None:
    if not ts:
        return None
    try:
        s = str(ts)
        if s.endswith("Z"):
            s = s[:-1] + "+00:00"
        return datetime.fromisoformat(s)
    except ValueError:
        return None


def run_diff_data(events: list[dict[str, Any]]) -> dict[str, Any]:
    """Extract comparable data from a run's events."""
    states: list[str] = []
    outputs: list[dict[str, Any]] = []
    tool_calls: list[dict[str, Any]] = []
    status = "unknown"
    total_latency_ms = 0
    llm_count = 0
    for e in events:
        typ = e.get("type")
        payload = e.get("payload") or {}
        if typ == "state_entered":
            states.append(str(payload.get("state", "")))
        elif typ == "structured_output":
            outputs.append(
                {
                    "schema_name": str(payload.get("schema_name", "")),
                    "state": payload.get("state"),
                    "seq": e.get("seq"),
                    "data": payload.get("data"),
                }
            )
        elif typ == "tool_call":
            tool_calls.append({"tool": payload.get("name"), "args": payload.get("arguments")})
        elif typ == "llm_response":
            ms = payload.get("latency_ms")
            if isinstance(ms, int):
                total_latency_ms += ms
                llm_count += 1
        elif typ == "run_completed":
            status = payload.get("status", status)
        elif typ == "run_paused":
            status = "paused"
    return {
        "states_visited": states,
        "structured_outputs": outputs,
        "tool_calls": tool_calls,
        "status": status,
        "total_latency_ms": total_latency_ms,
        "llm_calls": llm_count,
    }


def parse_duration(value: str) -> int | None:
    """Parse a human duration like '90d', '24h', '30d' into seconds. Returns None on failure."""
    m = re.fullmatch(r"(\d+)\s*([dhms])", value.strip().lower())
    if not m:
        return None
    n = int(m.group(1))
    unit = m.group(2)
    multipliers = {"d": 86400, "h": 3600, "m": 60, "s": 1}
    return n * multipliers[unit]
