"""Self-contained HTML trace viewer.

Renders an execution trace as a single HTML file that can be opened
in any browser.  Embeds Mermaid JS from CDN for DAG and timeline
rendering.

Usage::

    from core.observability.html_viewer import render_trace_html, save_trace_html

    html = render_trace_html(pathway, trace)
    save_trace_html(pathway, trace, "trace.html")
"""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from core.observability.trace import ExecutionTrace
    from core.pathways.pathway import Pathway


def _build_span_details(trace: ExecutionTrace) -> str:
    """Build HTML for the collapsible span detail section."""
    from core.observability.trace import _format_gantt_duration

    parts: list[str] = []
    for span in trace.spans:
        status_cls = {"success": "ok", "failure": "fail"}.get(span.status, "skip")
        dur = _format_gantt_duration(span.duration_ms)
        parts.append(
            f'<details class="span-detail {status_cls}">'
            f"<summary><strong>{span.node_id}</strong> "
            f'<span class="tag">{span.node_type}</span> '
            f'<span class="dur">{dur}</span> '
            f'<span class="status-badge {status_cls}">{span.status}</span>'
            f"</summary>"
            f"<div class='span-body'>"
        )

        # Tool calls
        if span.tool_calls:
            parts.append("<h4>Tool Calls</h4><ul>")
            for tc in span.tool_calls:
                tc_dur = _format_gantt_duration(tc.duration_ms)
                icon = "&#10003;" if tc.success else "&#10007;"
                err = f' <span class="err">{tc.error}</span>' if tc.error else ""
                parts.append(f"<li>{icon} <code>{tc.tool_name}</code> {tc_dur}{err}</li>")
            parts.append("</ul>")

        # LLM calls
        if span.llm_calls:
            parts.append("<h4>LLM Calls</h4><ul>")
            for lc in span.llm_calls:
                lc_dur = _format_gantt_duration(lc.duration_ms)
                parts.append(
                    f"<li><code>{lc.model}</code> {lc.tokens_in}+{lc.tokens_out} tok, {lc_dur}</li>"
                )
            parts.append("</ul>")

        # Decisions
        decisions = getattr(span, "decisions", [])
        if decisions:
            parts.append("<h4>Decisions</h4><ul>")
            for d in decisions:
                chosen = ", ".join(d.tools_chosen) if d.tools_chosen else "(none)"
                reasoning = d.reasoning or ""
                parts.append(
                    f"<li>Round {d.round}: chose <code>{chosen}</code>"
                    f"<br><small>{reasoning[:200]}</small></li>"
                )
            parts.append("</ul>")

        parts.append("</div></details>")

    return "\n".join(parts)


def render_trace_html(
    pathway: Pathway,
    trace: ExecutionTrace,
    *,
    title: str | None = None,
) -> str:
    """Render an execution trace as a self-contained HTML document.

    Parameters
    ----------
    pathway
        The pathway that was executed.
    trace
        The execution trace to visualize.
    title
        Page title. Defaults to the pathway name or ID.

    Returns
    -------
    str
        A complete HTML document string.
    """
    from core.observability.trace import _format_gantt_duration, trace_to_mermaid
    from core.pathways.visualize import pathway_to_mermaid_with_trace

    page_title = title or getattr(pathway, "name", None) or pathway.id
    dag_mermaid = pathway_to_mermaid_with_trace(pathway, trace)
    gantt_mermaid = trace_to_mermaid(trace)
    total_dur = _format_gantt_duration(trace.total_duration_ms)
    span_details = _build_span_details(trace)

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{page_title} — AlbusOS Trace</title>
<script src="https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.min.js"></script>
<style>
  :root {{
    --bg: #0d1117; --fg: #c9d1d9; --card: #161b22; --border: #30363d;
    --ok: #3fb950; --fail: #f85149; --skip: #8b949e; --accent: #58a6ff;
  }}
  * {{ margin: 0; padding: 0; box-sizing: border-box; }}
  body {{ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
         background: var(--bg); color: var(--fg); padding: 24px; }}
  h1 {{ font-size: 1.5rem; margin-bottom: 4px; }}
  .subtitle {{ color: var(--skip); font-size: 0.85rem; margin-bottom: 20px; }}
  .stats {{ display: flex; gap: 24px; margin-bottom: 20px; flex-wrap: wrap; }}
  .stat {{ background: var(--card); border: 1px solid var(--border);
           border-radius: 8px; padding: 12px 18px; }}
  .stat .val {{ font-size: 1.3rem; font-weight: 600; color: var(--accent); }}
  .stat .lbl {{ font-size: 0.75rem; color: var(--skip); text-transform: uppercase; }}
  .panels {{ display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin-bottom: 20px; }}
  @media (max-width: 900px) {{ .panels {{ grid-template-columns: 1fr; }} }}
  .panel {{ background: var(--card); border: 1px solid var(--border);
            border-radius: 8px; padding: 16px; overflow-x: auto; }}
  .panel h2 {{ font-size: 1rem; margin-bottom: 12px; color: var(--accent); }}
  .mermaid {{ background: transparent; }}
  details.span-detail {{ background: var(--card); border: 1px solid var(--border);
    border-radius: 6px; margin-bottom: 6px; }}
  details.span-detail summary {{ padding: 10px 14px; cursor: pointer;
    font-size: 0.9rem; }}
  details.span-detail summary:hover {{ background: rgba(255,255,255,0.03); }}
  .span-body {{ padding: 8px 14px 14px; font-size: 0.85rem; }}
  .span-body h4 {{ margin: 8px 0 4px; font-size: 0.8rem; color: var(--accent); }}
  .span-body ul {{ margin-left: 16px; }}
  .span-body li {{ margin: 2px 0; }}
  .tag {{ background: var(--border); padding: 1px 6px; border-radius: 4px;
          font-size: 0.75rem; }}
  .dur {{ color: var(--skip); font-size: 0.8rem; }}
  .status-badge {{ padding: 1px 6px; border-radius: 4px; font-size: 0.7rem;
                   font-weight: 600; text-transform: uppercase; }}
  .status-badge.ok {{ background: rgba(63,185,80,0.15); color: var(--ok); }}
  .status-badge.fail {{ background: rgba(248,81,73,0.15); color: var(--fail); }}
  .status-badge.skip {{ background: rgba(139,148,158,0.15); color: var(--skip); }}
  .err {{ color: var(--fail); font-size: 0.8rem; }}
  code {{ background: var(--border); padding: 1px 4px; border-radius: 3px;
          font-size: 0.85em; }}
</style>
</head>
<body>
<h1>{page_title}</h1>
<div class="subtitle">Execution {trace.execution_id or "—"} &middot; Pathway {trace.pathway_id or "—"}</div>

<div class="stats">
  <div class="stat"><div class="val">{total_dur}</div><div class="lbl">Duration</div></div>
  <div class="stat"><div class="val">{trace.total_tokens:,}</div><div class="lbl">Tokens</div></div>
  <div class="stat"><div class="val">{len(trace.spans)}</div><div class="lbl">Nodes</div></div>
  <div class="stat"><div class="val">{sum(1 for s in trace.spans if s.status == "success")}</div><div class="lbl">Succeeded</div></div>
  <div class="stat"><div class="val">{sum(1 for s in trace.spans if s.status == "failure")}</div><div class="lbl">Failed</div></div>
</div>

<div class="panels">
  <div class="panel">
    <h2>Pathway DAG</h2>
    <pre class="mermaid">
{dag_mermaid}
    </pre>
  </div>
  <div class="panel">
    <h2>Execution Timeline</h2>
    <pre class="mermaid">
{gantt_mermaid}
    </pre>
  </div>
</div>

<h2 style="font-size:1rem; margin-bottom:10px; color:var(--accent);">Span Details</h2>
{span_details}

<script>
  mermaid.initialize({{
    startOnLoad: true,
    theme: 'dark',
    themeVariables: {{
      primaryColor: '#1f6feb',
      primaryTextColor: '#c9d1d9',
      lineColor: '#30363d',
      secondaryColor: '#161b22',
      tertiaryColor: '#0d1117',
    }},
  }});
</script>
</body>
</html>"""


def save_trace_html(
    pathway: Pathway,
    trace: ExecutionTrace,
    path: str | Path,
    *,
    title: str | None = None,
) -> Path:
    """Render and save an execution trace as an HTML file.

    Parameters
    ----------
    pathway
        The pathway that was executed.
    trace
        The execution trace to visualize.
    path
        File path to write. Parent directories are created if needed.
    title
        Page title.

    Returns
    -------
    Path
        The resolved path of the written file.
    """
    resolved = Path(path).resolve()
    resolved.parent.mkdir(parents=True, exist_ok=True)
    resolved.write_text(render_trace_html(pathway, trace, title=title))
    return resolved


__all__ = [
    "render_trace_html",
    "save_trace_html",
]
