#!/usr/bin/env -S uv run --script
# /// script
# requires-python = ">=3.12"
# dependencies = ["rich>=14.0.0", "typer>=0.24.0"]
# ///
#MISE description="List or export Claude Code session transcripts"
#USAGE arg "[session_id]" help="Short session UUID prefix to convert (omit to list sessions)"
#USAGE flag "--view" help="Open transcript in browser (temp dir, cleaned up on exit)"
"""List recent Claude Code sessions or convert one to a zipped HTML transcript.

Without arguments, prints a table of sessions with short IDs.
With a session ID prefix, converts that session to HTML and zips it into ./transcripts/.
"""

import datetime
import subprocess
import tempfile
import webbrowser
import zipfile
from pathlib import Path
from typing import Annotated, Optional

import typer
from rich.console import Console
from rich.table import Table

console = Console(stderr=True)

PROJECT_SLUG = "-Users-eblume-code-personal-mercury-interview-project"
SESSIONS_DIR = Path.home() / ".claude" / "projects" / PROJECT_SLUG
OUTPUT_DIR = Path("transcripts")


def get_sessions() -> list[tuple[str, Path, float, int]]:
    """Return (uuid, path, mtime, size_kb) for all JSONL session files, newest first."""
    sessions = []
    for p in SESSIONS_DIR.glob("*.jsonl"):
        uuid = p.stem
        stat = p.stat()
        sessions.append((uuid, p, stat.st_mtime, stat.st_size // 1024))
    sessions.sort(key=lambda s: s[2], reverse=True)
    return sessions


def list_sessions(limit: int) -> None:
    """Print a table of recent sessions."""
    sessions = get_sessions()
    if not sessions:
        console.print("[yellow]No sessions found.[/]")
        raise typer.Exit(0)

    table = Table(title=f"Recent sessions (showing {min(limit, len(sessions))} of {len(sessions)})")
    table.add_column("Short ID", style="bold cyan")
    table.add_column("Date", style="green")
    table.add_column("Size")
    table.add_column("First line")

    for uuid, path, mtime, size_kb in sessions[:limit]:
        short_id = uuid[:8]
        date_str = datetime.datetime.fromtimestamp(mtime).strftime("%Y-%m-%d %H:%M")

        # Read first human message for context
        first_line = ""
        try:
            import json

            with open(path) as f:
                for line in f:
                    obj = json.loads(line)
                    if obj.get("type") == "user":
                        msg = obj.get("message", {})
                        if isinstance(msg, dict):
                            content = msg.get("content", "")
                            if isinstance(content, str):
                                first_line = content[:60]
                                break
                            elif isinstance(content, list):
                                for block in content:
                                    if isinstance(block, dict) and block.get("type") == "text":
                                        first_line = block["text"][:60]
                                        break
                                if first_line:
                                    break
        except Exception:
            first_line = "(unreadable)"

        if len(first_line) >= 60:
            first_line += "..."

        table.add_row(short_id, date_str, f"{size_kb} KB", first_line)

    console.print(table)
    console.print("\n[dim]Usage:[/] mise run transcript <short-id>")


def resolve_session(prefix: str) -> Path:
    """Find the JSONL file matching a short ID prefix."""
    matches = [p for p in SESSIONS_DIR.glob("*.jsonl") if p.stem.startswith(prefix)]
    if len(matches) == 0:
        console.print(f"[red]No session found matching '{prefix}'[/]")
        raise typer.Exit(1)
    if len(matches) > 1:
        console.print(f"[red]Ambiguous prefix '{prefix}' — matches {len(matches)} sessions:[/]")
        for m in matches:
            console.print(f"  {m.stem}")
        raise typer.Exit(1)
    return matches[0]


def _convert_to_dir(session_path: Path, outdir: Path) -> None:
    """Run claude-code-transcripts json to convert a session into outdir."""
    result = subprocess.run(
        [
            "uvx", "claude-code-transcripts", "json",
            str(session_path),
            "-o", str(outdir),
            "--json",
        ],
        capture_output=True,
        text=True,
    )

    if result.returncode != 0:
        console.print(f"[red]Conversion failed:[/]\n{result.stderr}")
        raise typer.Exit(1)

    if result.stdout.strip():
        console.print(result.stdout.strip())
    if result.stderr.strip():
        console.print(result.stderr.strip())


def convert_session(session_path: Path) -> None:
    """Convert a session JSONL to HTML and zip it into ./transcripts/."""
    short_id = session_path.stem[:8]

    with tempfile.TemporaryDirectory(prefix="transcript-") as tmpdir:
        outdir = Path(tmpdir) / "html"

        console.print(f"[blue]Converting session {short_id}...[/]")
        _convert_to_dir(session_path, outdir)

        # Create output dir and zip
        OUTPUT_DIR.mkdir(exist_ok=True)
        zip_name = f"transcript-{short_id}.zip"
        zip_path = OUTPUT_DIR / zip_name

        with zipfile.ZipFile(zip_path, "w", zipfile.ZIP_DEFLATED) as zf:
            for file in sorted(outdir.rglob("*")):
                if file.is_file():
                    arcname = file.relative_to(outdir)
                    zf.write(file, arcname)

        size_kb = zip_path.stat().st_size // 1024
        console.print(f"\n[green bold]Done:[/] {zip_path} ({size_kb} KB)")


def view_session(session_path: Path) -> None:
    """Export session to zip, then unzip to a tempdir and open in browser."""
    convert_session(session_path)

    short_id = session_path.stem[:8]
    zip_path = OUTPUT_DIR / f"transcript-{short_id}.zip"

    tmpdir = tempfile.mkdtemp(prefix="transcript-view-")
    with zipfile.ZipFile(zip_path, "r") as zf:
        zf.extractall(tmpdir)

    index = Path(tmpdir) / "index.html"
    console.print(f"[blue]Opening in browser...[/]")
    webbrowser.open(index.as_uri())
    console.print(f"[green]Viewing from:[/] {tmpdir}")


app = typer.Typer(help="List or export Claude Code session transcripts.")


@app.command()
def main(
    session_id: Annotated[
        Optional[str],
        typer.Argument(help="Short session UUID prefix to convert (omit to list)"),
    ] = None,
    limit: Annotated[
        int,
        typer.Option("--limit", "-n", help="Max sessions to show in list mode"),
    ] = 10,
    view: Annotated[
        bool,
        typer.Option("--view", help="Open transcript in browser (temp dir, cleaned up on exit)"),
    ] = False,
) -> None:
    """List sessions or convert one to a zipped HTML transcript."""
    if session_id is None:
        list_sessions(limit)
    elif view:
        session_path = resolve_session(session_id)
        view_session(session_path)
    else:
        session_path = resolve_session(session_id)
        convert_session(session_path)


if __name__ == "__main__":
    app()
