#!/usr/bin/env python3
"""
export_codex_session.py — Convert a Codex session JSONL to readable markdown.

Codex stores sessions automatically at ~/.codex/sessions/YYYY/MM/DD/rollout-*.jsonl

Usage (manual):
    export_codex_session.py                        # export most recent session
    export_codex_session.py <path/to/session.jsonl>
    export_codex_session.py --all                  # export all sessions not yet exported

Output goes to attic/CLAUDE/session-logs/ as  codex_YYYYMMDD_<session-id-prefix>.md
Same session = same filename, so re-running overwrites cleanly.
"""

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


CODEX_SESSIONS_ROOT = Path.home() / '.codex' / 'sessions'


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

def convert(jsonl_path: Path) -> tuple[str, str, str]:
    """Return (markdown_text, session_id, date_str)."""
    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 = ''
    cwd = ''
    model = ''
    start_ts = None
    turns = []          # list of (role, time_str, text, metadata_dict)
    current_turn_meta = {}

    for e in entries:
        etype = e.get('type', '')
        ts_raw = e.get('timestamp', '')
        try:
            time_str = ts_raw[11:19]   # "HH:MM:SS" from ISO timestamp
        except Exception:
            time_str = ''
        if ts_raw and start_ts is None:
            start_ts = ts_raw

        if etype == 'session_meta':
            p = e.get('payload', {})
            session_id = p.get('id', session_id)
            cwd = p.get('cwd', cwd)

        elif etype == 'turn_context':
            p = e.get('payload', {})
            current_turn_meta = {
                'model': p.get('model', ''),
                'effort': p.get('effort', ''),
            }

        elif etype == 'event_msg':
            p = e.get('payload', {})
            msg_type = p.get('type', '')

            if msg_type == 'user_message':
                text = p.get('message', '').strip()
                if text:
                    turns.append(('user', time_str, text, dict(current_turn_meta)))

            elif msg_type == 'agent_message':
                text = p.get('message', '').strip()
                if text:
                    turns.append(('assistant', time_str, text, dict(current_turn_meta)))

            elif msg_type == 'task_complete':
                # summarise any completion metadata
                pass   # nothing useful to show currently

    # ── Render markdown ───────────────────────────────────────────────────────
    if start_ts:
        date_str = start_ts[:10]
        dt_display = start_ts[:16].replace('T', ' ')
    else:
        date_str = datetime.now().strftime('%Y-%m-%d')
        dt_display = date_str

    lines = [
        f'# Codex session — {dt_display}',
        f'',
        f'Session: `{session_id}`  ',
        f'Working dir: `{cwd}`  ',
        f'Source: `{jsonl_path}`',
        f'Exported: {datetime.now().strftime("%Y-%m-%d %H:%M:%S")}',
        f'',
        '---',
        '',
    ]

    for role, ts, text, meta in turns:
        if role == 'user':
            lines.append(f'## You [{ts}]')
            lines.append('')
            lines.append(text)
            lines.append('')
        else:
            model_tag = f'  _{meta.get("model", "")}  effort={meta.get("effort", "")}_' if meta.get('model') else ''
            lines.append(f'## Codex [{ts}]{model_tag}')
            lines.append('')
            lines.append(text)
            lines.append('')
        lines.append('---')
        lines.append('')

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


def find_all_sessions() -> list[Path]:
    return sorted(CODEX_SESSIONS_ROOT.rglob('*.jsonl'), key=lambda p: p.stat().st_mtime)


def export_one(jsonl_path: Path, log_dir: Path) -> Path:
    md, session_id, date_str = convert(jsonl_path)
    date_compact = date_str.replace('-', '')
    sid_short = (session_id or jsonl_path.stem)[:8]
    out_path = log_dir / f'codex_{date_compact}_{sid_short}.md'
    out_path.write_text(md, encoding='utf-8')
    return out_path


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

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)

    args = sys.argv[1:]

    if args and args[0] == '--all':
        sessions = find_all_sessions()
        exported = 0
        for p in sessions:
            try:
                out = export_one(p, log_dir)
                print(f'  → {out.name}', file=sys.stderr)
                exported += 1
            except Exception as exc:
                print(f'  ✗ {p.name}: {exc}', file=sys.stderr)
        print(f'export_codex_session: {exported}/{len(sessions)} exported → {log_dir}', file=sys.stderr)
        return

    if args and not args[0].startswith('-'):
        jsonl_path = Path(args[0])
    else:
        sessions = find_all_sessions()
        if not sessions:
            print('export_codex_session: no Codex session files found', file=sys.stderr)
            sys.exit(1)
        jsonl_path = sessions[-1]

    if not jsonl_path.exists():
        print(f'export_codex_session: not found: {jsonl_path}', file=sys.stderr)
        sys.exit(1)

    try:
        out_path = export_one(jsonl_path, log_dir)
        size = out_path.stat().st_size
        print(f'export_codex_session: {size} bytes → {out_path}', file=sys.stderr)
    except Exception as exc:
        print(f'export_codex_session: failed: {exc}', file=sys.stderr)
        sys.exit(1)


if __name__ == '__main__':
    main()
