"""CLI tool for session-start hook injection.

Reads directly from SQLite. Outputs rich context for Claude at session start:
- Recent sessions grouped by session_id with summaries and facts
- Memory Bank files (5 in dependency order)

MBEL grammar and cortex instructions are in --append-system-prompt, not here.

Usage:
    neo-cortex-timeline --n 3
    neo-cortex-timeline --data-dir /path/to/data --mb /path/to/memory_bank
"""

from __future__ import annotations

import argparse
import json
import os
import sqlite3
import sys
from pathlib import Path


def _log(msg: str) -> None:
    """Diagnostic log to stderr (captured by hook script into log file)."""
    print(f"[timeline_cli] {msg}", file=sys.stderr)


# Activities worth showing at startup (skip greetings, idle chatter)
_NOISE_ACTIVITIES = {"discussion"}
_NOISE_TOPICS = {"greeting", "session start", "introduction"}


def _get_recent_sessions(conn: sqlite3.Connection, n_sessions: int = 3,
                         project: str | None = None) -> list[dict]:
    """Get last N sessions with their substantive memories (summary + facts)."""
    # Find last N distinct session_ids with real content
    sql = """
        SELECT DISTINCT session_id, MAX(timestamp) as last_ts
        FROM memories
        WHERE summary IS NOT NULL AND length(summary) > 20
          AND activity NOT IN ('discussion')
          AND topic NOT IN ('greeting', 'session start', 'introduction')
    """
    params: list = []
    if project:
        sql += " AND project = ?"
        params.append(project)
    sql += " GROUP BY session_id ORDER BY last_ts DESC LIMIT ?"
    params.append(n_sessions)

    sessions = conn.execute(sql, params).fetchall()
    if not sessions:
        return []

    result = []
    for sess in sessions:
        sid = sess["session_id"]
        # Get memories for this session, ordered by time
        mem_sql = """
            SELECT title, summary, project, topic, activity, facts, concepts, files_touched
            FROM memories
            WHERE session_id = ?
              AND summary IS NOT NULL AND length(summary) > 20
              AND activity NOT IN ('discussion')
              AND topic NOT IN ('greeting', 'session start', 'introduction')
            ORDER BY timestamp ASC
        """
        mems = conn.execute(mem_sql, (sid,)).fetchall()
        if not mems:
            continue

        # Collect unique projects in this session
        projects = list(dict.fromkeys(m["project"] for m in mems))

        # Build session summary
        entries = []
        for m in mems:
            entry = {"summary": m["summary"], "activity": m["activity"]}
            # Add facts if present
            if m["facts"]:
                try:
                    facts = json.loads(m["facts"])
                    if facts:
                        entry["facts"] = facts
                except (json.JSONDecodeError, TypeError):
                    pass
            # Add files if present
            if m["files_touched"]:
                try:
                    files = json.loads(m["files_touched"])
                    if files:
                        entry["files"] = files
                except (json.JSONDecodeError, TypeError):
                    pass
            entries.append(entry)

        result.append({
            "session_id": sid[:8],
            "projects": projects,
            "entries": entries,
        })

    return result


def _get_project_summary(conn: sqlite3.Connection) -> dict[str, dict]:
    """Get per-project stats: count, last activity, last topic."""
    rows = conn.execute("""
        SELECT project, COUNT(*) as cnt,
               MAX(timestamp) as last_ts,
               (SELECT activity FROM memories m2
                WHERE m2.project = m1.project ORDER BY timestamp DESC LIMIT 1) as last_activity
        FROM memories m1
        GROUP BY project
        ORDER BY last_ts DESC
    """).fetchall()
    return {
        r["project"]: {"count": r["cnt"], "last_activity": r["last_activity"]}
        for r in rows
    }


def main(argv: list[str] | None = None) -> None:
    parser = argparse.ArgumentParser(description="Cortex session-start context injection")
    parser.add_argument("--data-dir", help="Cortex data directory (has cortex_db/, memory_index.db)")
    parser.add_argument("--n", type=int, default=3, help="Number of recent sessions to show")
    parser.add_argument("--project", default=None, help="Filter by project")
    parser.add_argument("--mb", default=None, help="Memory Bank directory path")
    parser.add_argument("--mb-max", type=int, default=2000, help="Max chars per MB file")
    args = parser.parse_args(argv)

    _log(f"started, args={args}")

    # Discover data dir
    data_dir = args.data_dir or os.environ.get("CORTEX_DATA_DIR")
    if not data_dir:
        for candidate in [
            os.path.join(os.getcwd(), ".neo", "cortex", "data"),
            os.path.expanduser("~/neo-ram"),
        ]:
            if os.path.isdir(os.path.join(candidate, "cortex_db")):
                data_dir = candidate
                break

    if not data_dir:
        _log("no data dir found — exiting")
        return

    _log(f"data_dir={data_dir}")

    # Discover MB path — CWD (project root) first, then fallbacks
    mb_path = args.mb
    if not mb_path:
        for candidate in [
            os.path.join(os.getcwd(), "memory_bank"),         # project root (primary)
            os.path.join(data_dir, "memory_bank"),           # next to cortex_db/
        ]:
            if os.path.isdir(candidate):
                mb_path = candidate
                break

    _log(f"mb_path={mb_path}")

    # Open SQLite directly (no need for full cortex init)
    from neo_cortex.subscriber import configure_paths
    configure_paths(data_dir)
    from neo_cortex import config

    db_path = config.MEMORY_INDEX_DB_PATH
    if not Path(db_path).exists():
        _log(f"index db not found at {db_path} — exiting")
        return

    conn = sqlite3.connect(db_path)
    conn.row_factory = sqlite3.Row
    total = conn.execute("SELECT COUNT(*) FROM memories").fetchone()[0]
    sessions_count = conn.execute("SELECT COUNT(DISTINCT session_id) FROM memories").fetchone()[0]
    _log(f"db opened, memories={total}, sessions={sessions_count}")

    # --- Stats (MBEL) ---
    proj_summary = _get_project_summary(conn)
    proj_parts = [f"{p}#{d['count']}" for p, d in list(proj_summary.items())[:5]]
    print(f"§cortex @memories::{total} @sessions::{sessions_count}")
    print(f"@projects::{'+'.join(proj_parts)}")
    print()

    # --- Recent sessions (MBEL) ---
    recent = _get_recent_sessions(conn, n_sessions=args.n, project=args.project)
    if recent:
        print(f"§recentWork {{last {len(recent)} sessions}}")
        for sess in recent:
            projs = "+".join(sess["projects"])
            print(f"\n[{projs}]")
            for e in sess["entries"]:
                print(f">{e['activity']}::{e['summary']}")
                if e.get("facts"):
                    for f in e["facts"][:3]:
                        print(f"  +{f}")
                if e.get("files"):
                    print(f"  @files::{','.join(e['files'][:5])}")
        print()

    conn.close()

    # --- Memory Bank ---
    if mb_path:
        for fname in ["productContext.md", "systemPatterns.md", "techContext.md", "activeContext.md", "progress.md"]:
            fpath = os.path.join(mb_path, fname)
            if not os.path.exists(fpath):
                continue
            content = Path(fpath).read_text().strip()
            if len(content) > args.mb_max:
                content = content[: args.mb_max] + "\n[...truncated]"
            print(f"# MB::{fname}")
            print(content)
            print()


if __name__ == "__main__":
    main()
