"""HTML evidence report generator.

Produces a self-contained HTML file that an auditor can open
in any browser. No external dependencies, no JavaScript frameworks —
just HTML + inline CSS.

The report contains:
  1. Executive summary (period, record count, chain status)
  2. SOC2 control mapping with evidence points and gaps
  3. Identity coverage breakdown
  4. Operation timeline (last N records)
  5. Chain verification result
"""

from __future__ import annotations

from datetime import UTC, datetime
from html import escape
from typing import TYPE_CHECKING

from attestmcp.soc2 import SOC2Report, map_controls

if TYPE_CHECKING:
    from attestmcp.models import AuditRecord, ChainVerification
    from attestmcp.soc2 import ControlEvidence
    from attestmcp.store import SQLiteAuditStore


def generate_report(
    store: SQLiteAuditStore,
    *,
    since: datetime | None = None,
    until: datetime | None = None,
    max_timeline_records: int = 100,
) -> str:
    """Generate an HTML evidence report from the audit store.

    Args:
        store: The audit store to report on.
        since: Start of the evidence period.
        until: End of the evidence period.
        max_timeline_records: Max records to show in the timeline table.

    Returns:
        Complete self-contained HTML string.
    """
    records = store.query(since=since, until=until, limit=10000)
    chain = store.verify_chain(since=since, until=until)
    soc2 = map_controls(
        records,
        chain,
        period_start=since,
        period_end=until,
    )

    return _render_html(soc2, records[:max_timeline_records], chain)


def _render_html(
    soc2: SOC2Report,
    timeline_records: list[AuditRecord],
    chain: ChainVerification,
) -> str:
    """Render the complete HTML report."""
    period_str = _format_period(soc2.period_start, soc2.period_end)
    chain_status = "INTACT" if chain.valid else "BROKEN"
    chain_class = "pass" if chain.valid else "fail"

    controls_html = "\n".join(
        _render_control(c) for c in soc2.controls
    )
    timeline_html = _render_timeline(timeline_records)
    stats = soc2.summary_stats

    return f"""<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>AttestMCP Evidence Report — {escape(period_str)}</title>
<style>
{_CSS}
</style>
</head>
<body>
<div class="container">

<header>
  <div class="logo">AttestMCP</div>
  <h1>Compliance Evidence Report</h1>
  <p class="subtitle">AI Agent Memory Audit Trail</p>
  <p class="period">{escape(period_str)}</p>
  <p class="generated">Generated: {datetime.now(UTC).strftime("%Y-%m-%d %H:%M:%S UTC")}</p>
</header>

<section class="summary">
  <h2>Executive Summary</h2>
  <div class="stats-grid">
    <div class="stat">
      <span class="stat-value">{stats['total']}</span>
      <span class="stat-label">Total Operations</span>
    </div>
    <div class="stat">
      <span class="stat-value {chain_class}">{chain_status}</span>
      <span class="stat-label">Chain Integrity</span>
    </div>
    <div class="stat">
      <span class="stat-value">{chain.records_checked}</span>
      <span class="stat-label">Records Verified</span>
    </div>
    <div class="stat">
      <span class="stat-value">{stats['with_identity']}</span>
      <span class="stat-label">With Identity</span>
    </div>
    <div class="stat">
      <span class="stat-value">{stats['unique_users']}</span>
      <span class="stat-label">Unique Users</span>
    </div>
    <div class="stat">
      <span class="stat-value">{stats['unique_agents']}</span>
      <span class="stat-label">Unique Agents</span>
    </div>
    <div class="stat">
      <span class="stat-value">{stats['tools_call']}</span>
      <span class="stat-label">Tool Invocations</span>
    </div>
    <div class="stat">
      <span class="stat-value">{stats['avg_duration_ms']}ms</span>
      <span class="stat-label">Avg Latency</span>
    </div>
  </div>
</section>

<section class="controls">
  <h2>SOC 2 Trust Services Criteria Mapping</h2>
  {controls_html}
</section>

<section class="chain-detail">
  <h2>Chain Verification Detail</h2>
  <table class="detail-table">
    <tr><th>Status</th><td class="{chain_class}">{chain_status}</td></tr>
    <tr><th>Records Checked</th><td>{chain.records_checked}</td></tr>
    <tr><th>First Record</th><td><code>{escape(chain.first_record_id or 'N/A')}</code></td></tr>
    <tr><th>Last Record</th><td><code>{escape(chain.last_record_id or 'N/A')}</code></td></tr>
    <tr><th>Verified At</th><td>{chain.verified_at.strftime("%Y-%m-%d %H:%M:%S UTC")}</td></tr>
    {_chain_break_rows(chain)}
  </table>
  <p class="chain-explanation">
    Each audit record contains a <code>chain_hash</code> computed as
    <code>SHA-256(previous_chain_hash + canonical_json(record))</code>.
    Any modification, deletion, insertion, or reordering of records
    produces a hash mismatch detectable by verification.
  </p>
</section>

<section class="timeline">
  <h2>Operation Timeline (Last {len(timeline_records)} Records)</h2>
  {timeline_html}
</section>

<footer>
  <p>This report was generated by <strong>AttestMCP</strong> —
  continuous compliance evidence for AI agent memory.</p>
  <p>Report covers {stats['total']} audit records.
  Chain hash algorithm: SHA-256. Canonical serialization:
  sorted keys, no whitespace, UTC ISO8601, Unicode NFC.</p>
</footer>

</div>
</body>
</html>"""


def _render_control(control: ControlEvidence) -> str:
    """Render a single SOC2 control card."""
    status_class = "pass" if control.satisfied else "fail"
    status_text = "SATISFIED" if control.satisfied else "GAPS FOUND"

    evidence_items = "\n".join(
        f'    <li class="evidence">{escape(e)}</li>'
        for e in control.evidence_points
    )
    gap_items = "\n".join(
        f'    <li class="gap">{escape(g)}</li>'
        for g in control.gaps
    )

    evidence_section = (
        f'  <ul class="evidence-list">\n{evidence_items}\n  </ul>'
        if evidence_items
        else ""
    )
    gap_section = (
        f'  <h4>Gaps</h4>\n  <ul class="gap-list">\n{gap_items}\n  </ul>'
        if gap_items
        else ""
    )

    return f"""
<div class="control-card">
  <div class="control-header">
    <span class="control-id">{escape(control.control_id)}</span>
    <span class="control-name">{escape(control.control_name)}</span>
    <span class="control-status {status_class}">{status_text}</span>
  </div>
  <p class="control-desc">{escape(control.description)}</p>
  {evidence_section}
  {gap_section}
</div>"""


def _render_timeline(records: list[AuditRecord]) -> str:
    """Render the operations timeline table."""
    if not records:
        return "<p>No records in this period.</p>"

    rows = "\n".join(_render_timeline_row(r) for r in records)
    return f"""
<table class="timeline-table">
  <thead>
    <tr>
      <th>Timestamp</th>
      <th>Method</th>
      <th>Tool</th>
      <th>Identity</th>
      <th>Trust</th>
      <th>Policy</th>
      <th>Duration</th>
      <th>Chain Hash (first 12)</th>
    </tr>
  </thead>
  <tbody>
    {rows}
  </tbody>
</table>"""


def _render_timeline_row(record: AuditRecord) -> str:
    """Render a single timeline table row."""
    ts = record.timestamp.strftime("%Y-%m-%d %H:%M:%S")
    method = escape(record.method)
    tool = escape(record.tool_name or "—")
    identity = escape(
        record.agent_identity.user
        or record.agent_identity.agent_name
        or "anonymous"
    )
    trust = record.agent_identity.trust_level.value
    trust_class = {
        "verified": "trust-verified",
        "claimed": "trust-claimed",
        "anonymous": "trust-anon",
    }.get(trust, "")
    policy = record.policy_decision.value
    policy_class = "policy-deny" if policy == "deny" else ""
    duration = f"{record.duration_ms}ms"
    chain_prefix = record.chain_hash[:12] if record.chain_hash else "—"
    error_class = ' class="error-row"' if record.error else ""

    return f"""    <tr{error_class}>
      <td>{ts}</td>
      <td><code>{method}</code></td>
      <td>{tool}</td>
      <td>{identity}</td>
      <td class="{trust_class}">{trust}</td>
      <td class="{policy_class}">{policy}</td>
      <td>{duration}</td>
      <td><code>{chain_prefix}</code></td>
    </tr>"""


def _chain_break_rows(chain: ChainVerification) -> str:
    """Render additional rows if the chain is broken."""
    if chain.valid:
        return ""
    break_id = escape(chain.break_at_record_id or "unknown")
    exp_hash = escape(chain.expected_hash or "unknown")
    act_hash = escape(chain.actual_hash or "unknown")
    return f"""
    <tr class="fail"><th>Break At Record</th>
      <td><code>{break_id}</code></td></tr>
    <tr class="fail"><th>Expected Hash</th>
      <td><code>{exp_hash}</code></td></tr>
    <tr class="fail"><th>Actual Hash</th>
      <td><code>{act_hash}</code></td></tr>"""


def _format_period(
    start: datetime | None, end: datetime | None
) -> str:
    """Format the evidence period for display."""
    if start and end:
        return (
            f"{start.strftime('%Y-%m-%d %H:%M')} — "
            f"{end.strftime('%Y-%m-%d %H:%M UTC')}"
        )
    if start:
        return f"Since {start.strftime('%Y-%m-%d %H:%M UTC')}"
    if end:
        return f"Until {end.strftime('%Y-%m-%d %H:%M UTC')}"
    return "All Time"


_CSS = """
:root {
  --green: #059669;
  --red: #dc2626;
  --amber: #d97706;
  --blue: #2563eb;
  --gray-50: #f9fafb;
  --gray-100: #f3f4f6;
  --gray-200: #e5e7eb;
  --gray-500: #6b7280;
  --gray-700: #374151;
  --gray-900: #111827;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--gray-900);
  background: var(--gray-50);
  line-height: 1.6;
}

.container { max-width: 960px; margin: 0 auto; padding: 2rem; }

header {
  text-align: center;
  margin-bottom: 3rem;
  padding-bottom: 2rem;
  border-bottom: 2px solid var(--gray-200);
}
.logo {
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--blue);
  margin-bottom: 0.5rem;
}
h1 { font-size: 1.75rem; font-weight: 700; margin-bottom: 0.25rem; }
.subtitle { color: var(--gray-500); font-size: 1rem; }
.period { font-weight: 600; margin-top: 0.75rem; }
.generated { color: var(--gray-500); font-size: 0.875rem; }

h2 {
  font-size: 1.25rem;
  font-weight: 700;
  margin-bottom: 1rem;
  padding-bottom: 0.5rem;
  border-bottom: 1px solid var(--gray-200);
}

section { margin-bottom: 2.5rem; }

/* Stats grid */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 1rem;
}
.stat {
  background: white;
  padding: 1rem;
  border-radius: 8px;
  text-align: center;
  border: 1px solid var(--gray-200);
}
.stat-value {
  display: block;
  font-size: 1.5rem;
  font-weight: 700;
}
.stat-label {
  display: block;
  font-size: 0.75rem;
  color: var(--gray-500);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  margin-top: 0.25rem;
}

/* Control cards */
.control-card {
  background: white;
  border: 1px solid var(--gray-200);
  border-radius: 8px;
  padding: 1.25rem;
  margin-bottom: 1rem;
}
.control-header {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
}
.control-id {
  font-weight: 700;
  font-size: 0.875rem;
  background: var(--gray-100);
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  font-family: monospace;
}
.control-name { font-weight: 600; flex-grow: 1; }
.control-status {
  font-size: 0.75rem;
  font-weight: 700;
  padding: 0.15rem 0.5rem;
  border-radius: 4px;
  text-transform: uppercase;
}
.control-desc {
  color: var(--gray-700);
  font-size: 0.875rem;
  margin-bottom: 0.75rem;
}
.evidence-list, .gap-list {
  list-style: none;
  padding: 0;
  font-size: 0.875rem;
}
.evidence-list li, .gap-list li {
  padding: 0.25rem 0 0.25rem 1.25rem;
  position: relative;
}
.evidence::before {
  content: '✓';
  position: absolute;
  left: 0;
  color: var(--green);
  font-weight: 700;
}
.gap::before {
  content: '⚠';
  position: absolute;
  left: 0;
}
h4 {
  font-size: 0.875rem;
  color: var(--amber);
  margin: 0.5rem 0 0.25rem;
}

/* Status colors */
.pass { color: var(--green); }
.fail { color: var(--red); }
.control-status.pass { background: #d1fae5; color: var(--green); }
.control-status.fail { background: #fee2e2; color: var(--red); }

/* Detail table */
.detail-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}
.detail-table th {
  text-align: left;
  padding: 0.5rem 1rem;
  background: var(--gray-100);
  width: 200px;
  border: 1px solid var(--gray-200);
}
.detail-table td {
  padding: 0.5rem 1rem;
  border: 1px solid var(--gray-200);
}
.chain-explanation {
  font-size: 0.8rem;
  color: var(--gray-500);
  margin-top: 1rem;
}

/* Timeline table */
.timeline-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.8rem;
}
.timeline-table th {
  text-align: left;
  padding: 0.4rem 0.5rem;
  background: var(--gray-100);
  border: 1px solid var(--gray-200);
  font-weight: 600;
}
.timeline-table td {
  padding: 0.4rem 0.5rem;
  border: 1px solid var(--gray-200);
}
.timeline-table tbody tr:hover { background: #f0f9ff; }
.error-row { background: #fef2f2; }
.trust-verified { color: var(--green); font-weight: 600; }
.trust-claimed { color: var(--amber); }
.trust-anon { color: var(--gray-500); }
.policy-deny { color: var(--red); font-weight: 600; }

code {
  font-family: 'SF Mono', SFMono-Regular, Menlo, monospace;
  font-size: 0.85em;
}

footer {
  text-align: center;
  color: var(--gray-500);
  font-size: 0.8rem;
  padding-top: 2rem;
  border-top: 1px solid var(--gray-200);
}
footer p { margin-bottom: 0.25rem; }

@media (max-width: 768px) {
  .stats-grid { grid-template-columns: repeat(2, 1fr); }
  .timeline-table { font-size: 0.7rem; }
}

@media print {
  body { background: white; }
  .container { max-width: none; padding: 1rem; }
  .control-card { break-inside: avoid; }
}
"""
