#!/usr/bin/env python3
"""
export_session.py — Convert a Claude Code session JSONL to readable markdown.

Called automatically by the Claude Code Stop hook after each turn, or manually.
Output is written to attic/CLAUDE/session-logs/ and named by date + session-id prefix,
so repeated calls during the same session overwrite the same file.

Usage (manual):
    export_session.py                        # export most recent session
    export_session.py <path/to/session.jsonl>

Stop hook (stdin receives event JSON from Claude Code):
    {"type":"stop","session_id":"...","transcript_path":"..."}
"""

import json
import os
import sys
from datetime import datetime
from pathlib import Path


# ── Text extraction ───────────────────────────────────────────────────────────

def extract_text(content) -> str:
    """Extract readable text from a message content field (str or list of blocks)."""
    if isinstance(content, str):
        return content
    if not isinstance(content, list):
        return ''
    parts = []
    for block in content:
        if not isinstance(block, dict):
            continue
        bt = block.get('type', '')
        if bt == 'text':
            parts.append(block.get('text', ''))
        # thinking blocks: omit by default (too verbose, internal reasoning)
    return '\n'.join(parts)


def summarise_tool(block: dict) -> str:
    name = block.get('name', '?')
    inp = block.get('input', {})
    if name == 'Bash':
        cmd = inp.get('command', '').strip()
        desc = inp.get('description', '')
        short = cmd[:160].replace('\n', ' ')
        suffix = '…' if len(cmd) > 160 else ''
        detail = f'`{short}{suffix}`'
        if desc:
            detail += f'  _{desc}_'
        return f'- **Bash** {detail}'
    if name in ('Read', 'Write', 'Edit'):
        path = inp.get('file_path', inp.get('path', '?'))
        return f'- **{name}** `{path}`'
    if name == 'Grep':
        pat = inp.get('pattern', '?')[:80]
        loc = inp.get('path', '')
        return f'- **Grep** `{pat}`' + (f' in `{loc}`' if loc else '')
    if name == 'Glob':
        pat = inp.get('pattern', '?')
        return f'- **Glob** `{pat}`'
    if name == 'Agent':
        desc = inp.get('description', inp.get('prompt', '')[:60])
        return f'- **Agent** _{desc}_'
    # Generic fallback
    keys = [f'{k}={str(v)[:40]}' for k, v in list(inp.items())[:3]]
    return f'- **{name}** {", ".join(keys)}'


# ── Conversion ────────────────────────────────────────────────────────────────

def convert(jsonl_path: Path) -> tuple[str, str]:
    """Return (markdown_text, session_id)."""
    entries = []
    with open(jsonl_path, encoding='utf-8', errors='replace') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                entries.append(json.loads(line))
            except json.JSONDecodeError:
                pass

    session_id = ''
    start_ts = None
    turns = []

    for e in entries:
        t = e.get('type', '')
        if t == 'permission-mode':
            session_id = e.get('sessionId', session_id)
        elif t in ('user', 'assistant'):
            ts_ms = e.get('timestamp')
            if ts_ms and start_ts is None:
                start_ts = ts_ms
            try:
                if isinstance(ts_ms, (int, float)):
                    time_str = datetime.fromtimestamp(ts_ms / 1000).strftime('%H:%M:%S')
                elif isinstance(ts_ms, str):
                    # ISO format: "2026-04-16T01:59:43.686Z"
                    time_str = ts_ms[11:19]
                else:
                    time_str = ''
            except Exception:
                time_str = ''

            msg = e.get('message', {})
            content = msg.get('content', '')
            role = t  # 'user' or 'assistant'

            if role == 'user':
                text = extract_text(content).strip()
                if text:
                    turns.append(('user', time_str, text, []))
            else:
                blocks = content if isinstance(content, list) else []
                text_parts = []
                tool_lines = []
                for block in blocks:
                    if not isinstance(block, dict):
                        continue
                    bt = block.get('type', '')
                    if bt == 'text':
                        text_parts.append(block.get('text', ''))
                    elif bt == 'tool_use':
                        tool_lines.append(summarise_tool(block))
                text = '\n'.join(text_parts).strip()
                turns.append(('assistant', time_str, text, tool_lines))

    # ── Render markdown ───────────────────────────────────────────────────────
    if start_ts:
        try:
            if isinstance(start_ts, (int, float)):
                date_str = datetime.fromtimestamp(start_ts / 1000).strftime('%Y-%m-%d %H:%M')
            else:
                date_str = str(start_ts)[:16].replace('T', ' ')
        except Exception:
            date_str = datetime.now().strftime('%Y-%m-%d')
    else:
        date_str = datetime.now().strftime('%Y-%m-%d')
    lines = [
        f'# Claude session — {date_str}',
        f'',
        f'Session: `{session_id}`  ',
        f'Source: `{jsonl_path}`',
        f'Exported: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}',
        f'',
        '---',
        '',
    ]

    for role, ts, text, tools in turns:
        if role == 'user':
            lines.append(f'## You [{ts}]')
            lines.append('')
            lines.append(text)
            lines.append('')
        else:
            lines.append(f'## Claude [{ts}]')
            lines.append('')
            if text:
                lines.append(text)
                lines.append('')
            if tools:
                lines.append('**Tools:**')
                lines.extend(tools)
                lines.append('')
        lines.append('---')
        lines.append('')

    return '\n'.join(lines), session_id


# ── Entry point ───────────────────────────────────────────────────────────────

def find_latest_jsonl(project_dir: Path) -> Path | None:
    slug = str(project_dir).lstrip('/').replace('/', '-')
    storage = Path.home() / '.claude' / 'projects' / slug
    if not storage.exists():
        # Try with leading dash removed (Claude Code convention)
        storage = Path.home() / '.claude' / 'projects' / f'-{slug.lstrip("-")}'
    if not storage.exists():
        return None
    candidates = [p for p in storage.glob('*.jsonl') if p.stat().st_size > 0]
    if not candidates:
        return None
    return max(candidates, key=lambda p: p.stat().st_mtime)


def main():
    project_dir = Path(os.environ.get('CLAUDE_PROJECT_DIR',
                                       os.environ.get('PWD', '/home/elphel/git/imagej-elphel')))
    log_dir = project_dir / 'attic' / 'CLAUDE' / 'session-logs'
    log_dir.mkdir(parents=True, exist_ok=True)

    jsonl_path = None

    # 1. Explicit path argument
    if len(sys.argv) > 1:
        jsonl_path = Path(sys.argv[1])

    # 2. Stop-hook stdin event
    if jsonl_path is None and not sys.stdin.isatty():
        try:
            data = json.load(sys.stdin)
            tp = data.get('transcript_path') or data.get('transcriptPath')
            if tp:
                jsonl_path = Path(tp)
            elif 'session_id' in data or 'sessionId' in data:
                sid = data.get('session_id') or data.get('sessionId')
                # Locate by session id
                projects_root = Path.home() / '.claude' / 'projects'
                for p in projects_root.rglob(f'{sid}.jsonl'):
                    jsonl_path = p
                    break
        except Exception:
            pass

    # 3. Most recent JSONL for the current project
    if jsonl_path is None or not jsonl_path.exists():
        jsonl_path = find_latest_jsonl(project_dir)

    if jsonl_path is None or not jsonl_path.exists():
        print('export_session: no session JSONL found', file=sys.stderr)
        sys.exit(1)

    try:
        md, session_id = convert(jsonl_path)
    except Exception as exc:
        print(f'export_session: conversion failed: {exc}', file=sys.stderr)
        sys.exit(1)

    # Name by date + first 8 chars of session id so same-session calls overwrite
    date_str = datetime.now().strftime('%Y%m%d')
    sid_short = (session_id or jsonl_path.stem)[:8]
    out_path = log_dir / f'claude_{date_str}_{sid_short}.md'

    out_path.write_text(md, encoding='utf-8')
    print(f'export_session: {len(md)} chars → {out_path}', file=sys.stderr)


if __name__ == '__main__':
    main()
