#!/usr/bin/env python3
from __future__ import annotations

import argparse
import html
import json
import sqlite3
from datetime import datetime, timezone
from pathlib import Path
from typing import Any

SKIP_TABLES = {
    "memories_fts",
    "memories_fts_data",
    "memories_fts_idx",
    "memories_fts_docsize",
    "memories_fts_config",
    "memories_fts_content",
}


def to_text(value: Any) -> str:
    if value is None:
        return ""
    if isinstance(value, bytes):
        return value.decode("utf-8", errors="replace")
    if isinstance(value, (dict, list)):
        return json.dumps(value, ensure_ascii=False, indent=2)
    return str(value)


def detect_json(value: Any) -> Any:
    if not isinstance(value, str):
        return value
    stripped = value.strip()
    if not stripped:
        return value
    if stripped[:1] not in "[{":
        return value
    try:
        return json.loads(stripped)
    except Exception:
        return value


def render_table(name: str, columns: list[str], rows: list[sqlite3.Row]) -> str:
    header = "".join(f"<th>{html.escape(col)}</th>" for col in columns)
    body_rows = []
    for row in rows:
        cells = []
        for col in columns:
            value = detect_json(row[col])
            text = to_text(value)
            if len(text) > 2000:
                text = text[:2000] + "\n… [truncated in HTML cell]"
            cells.append(f"<td><pre>{html.escape(text)}</pre></td>")
        body_rows.append("<tr>" + "".join(cells) + "</tr>")
    if not body_rows:
        body_rows.append(f'<tr><td colspan="{len(columns)}"><em>empty</em></td></tr>')
    return f"""
<section class=\"table-section\">
  <h2 id=\"table-{html.escape(name)}\">{html.escape(name)}</h2>
  <div class=\"table-wrap\">
    <table>
      <thead><tr>{header}</tr></thead>
      <tbody>
        {''.join(body_rows)}
      </tbody>
    </table>
  </div>
</section>
"""


def main() -> int:
    parser = argparse.ArgumentParser()
    parser.add_argument("--db", required=True)
    parser.add_argument("--out", required=True)
    args = parser.parse_args()

    db_path = Path(args.db)
    out_path = Path(args.out)
    out_path.parent.mkdir(parents=True, exist_ok=True)

    conn = sqlite3.connect(str(db_path))
    conn.row_factory = sqlite3.Row

    tables = [
        row["name"]
        for row in conn.execute(
            "select name from sqlite_master where type='table' and name not like 'sqlite_%' order by name"
        )
        if row["name"] not in SKIP_TABLES
    ]

    counts: list[tuple[str, int]] = []
    sections: list[str] = []
    toc_items: list[str] = []

    for table in tables:
        columns = [r[1] for r in conn.execute(f"pragma table_info({table})")]
        rows = list(conn.execute(f"select * from {table} order by 1"))
        counts.append((table, len(rows)))
        toc_items.append(f'<li><a href="#table-{html.escape(table)}">{html.escape(table)}</a> <span class="count">({len(rows)})</span></li>')
        sections.append(render_table(table, columns, rows))

    generated_at = datetime.now(timezone.utc).isoformat()
    total_rows = sum(count for _, count in counts)
    schema_summary = "".join(
        f"<tr><td>{html.escape(name)}</td><td>{count}</td></tr>" for name, count in counts
    )

    html_doc = f"""<!doctype html>
<html>
<head>
  <meta charset=\"utf-8\">
  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">
  <title>Agents Database Snapshot</title>
  <style>
    body {{ font-family: system-ui, -apple-system, Segoe UI, Roboto, sans-serif; margin: 0; padding: 24px; color: #111; background: #fafafa; }}
    h1, h2 {{ line-height: 1.2; }}
    .meta, .summary {{ background: white; border: 1px solid #ddd; border-radius: 10px; padding: 16px; margin-bottom: 20px; }}
    .grid {{ display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }}
    .card {{ background: #f6f8fa; border-radius: 8px; padding: 12px; border: 1px solid #e5e7eb; }}
    .table-wrap {{ overflow: auto; background: white; border: 1px solid #ddd; border-radius: 10px; }}
    table {{ border-collapse: collapse; width: 100%; min-width: 900px; }}
    th, td {{ border: 1px solid #e5e7eb; padding: 8px; vertical-align: top; text-align: left; }}
    th {{ position: sticky; top: 0; background: #f3f4f6; z-index: 1; }}
    pre {{ margin: 0; white-space: pre-wrap; word-break: break-word; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }}
    ul {{ margin: 0; padding-left: 20px; }}
    .count {{ color: #666; }}
    .muted {{ color: #555; }}
    a {{ color: #0b57d0; text-decoration: none; }}
    a:hover {{ text-decoration: underline; }}
  </style>
</head>
<body>
  <h1>Agents Database Snapshot</h1>
  <div class=\"meta\">
    <div class=\"grid\">
      <div class=\"card\"><strong>Generated at</strong><br>{html.escape(generated_at)}</div>
      <div class=\"card\"><strong>Database</strong><br>{html.escape(str(db_path))}</div>
      <div class=\"card\"><strong>Tables</strong><br>{len(tables)}</div>
      <div class=\"card\"><strong>Total rows</strong><br>{total_rows}</div>
    </div>
  </div>
  <div class=\"summary\">
    <h2>Table counts</h2>
    <table>
      <thead><tr><th>Table</th><th>Rows</th></tr></thead>
      <tbody>{schema_summary}</tbody>
    </table>
  </div>
  <div class=\"summary\">
    <h2>Contents</h2>
    <ul>{''.join(toc_items)}</ul>
    <p class=\"muted\">FTS internal tables omitted. JSON columns rendered pretty when possible. Long cells truncated in-table for browser sanity.</p>
  </div>
  {''.join(sections)}
</body>
</html>
"""
    out_path.write_text(html_doc, encoding="utf-8")
    print(out_path)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
