#!/usr/bin/env python3
"""odysseus-docs — shell wrapper for the living-document store.

Documents are the AI-editable text/markdown/code files the assistant
creates in-place (think: a scratchpad it can revise). Each document
has a content blob + version history.

    odysseus-docs list [--active] [--limit N]
    odysseus-docs show DOC_ID                # current content + metadata
    odysseus-docs versions DOC_ID            # version history (no content)
    odysseus-docs export DOC_ID --version N  # full content of a specific version
    odysseus-docs search "invoice"           # title + current_content match
    odysseus-docs delete DOC_ID              # soft-delete (is_active=False)

Reads `data/app.db` directly.
"""

from __future__ import annotations
import sys
import os, sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), "_lib"))
from cli import quiet_logs, emit, fail, common_parser, run, REPO_ROOT as _REPO_ROOT
quiet_logs()

import argparse, json, logging, os, sys
from pathlib import Path

try:
    from core.database import SessionLocal, Document, DocumentVersion
    quiet_logs()
except ModuleNotFoundError as e:
    sys.stderr.write(f"error: {e}\nhint: run from repo root with venv active.\n")
    sys.exit(2)


def _text_len(value) -> int:
    return len(value) if isinstance(value, str) else 0


def _serialize(d: "Document", include_content: bool = False) -> dict:
    out = {
        "id": d.id,
        "title": d.title,
        "language": d.language or "",
        "session_id": d.session_id or "",
        "version_count": d.version_count or 1,
        "is_active": bool(d.is_active),
        "tidy_verdict": d.tidy_verdict or "",
        "content_length": _text_len(d.current_content),
        "created_at": d.created_at.isoformat() if d.created_at else "",
        "updated_at": d.updated_at.isoformat() if d.updated_at else "",
    }
    if include_content:
        out["current_content"] = d.current_content or ""
    return out


def cmd_list(args):
    db = SessionLocal()
    try:
        q = db.query(Document)
        if args.active:
            q = q.filter(Document.is_active == True)  # noqa: E712
        if args.session:
            q = q.filter(Document.session_id == args.session)
        q = q.order_by(Document.updated_at.desc()).limit(args.limit)
        emit([_serialize(d) for d in q.all()], args)
    finally:
        db.close()


def cmd_show(args):
    db = SessionLocal()
    try:
        d = db.get(Document, args.id)
        if not d:
            fail(f"no document with id {args.id!r}")
        emit(_serialize(d, include_content=True), args)
    finally:
        db.close()


def cmd_versions(args):
    db = SessionLocal()
    try:
        d = db.get(Document, args.id)
        if not d:
            fail(f"no document with id {args.id!r}")
        rows = db.query(DocumentVersion).filter(
            DocumentVersion.document_id == args.id
        ).order_by(DocumentVersion.version_number.desc()).all()
        emit([
            {
                "version_number": v.version_number,
                "summary": v.summary or "",
                "source": v.source or "ai",
                "content_length": _text_len(v.content),
            } for v in rows
        ], args)
    finally:
        db.close()


def cmd_export(args):
    """Print the full content of a specific version as the JSON
    payload's `content` field, or raw text via --raw."""
    db = SessionLocal()
    try:
        d = db.get(Document, args.id)
        if not d:
            fail(f"no document with id {args.id!r}")
        if args.version is None:
            content = d.current_content or ""
            version = d.version_count or 1
        else:
            v = db.query(DocumentVersion).filter(
                DocumentVersion.document_id == args.id,
                DocumentVersion.version_number == args.version,
            ).first()
            if not v:
                fail(f"no version {args.version} on doc {args.id!r}")
            content = v.content or ""
            version = v.version_number
        if args.raw:
            sys.stdout.write(content)
            if not content.endswith("\n"):
                sys.stdout.write("\n")
            return
        emit({
            "id": d.id,
            "title": d.title,
            "version": version,
            "content": content,
        }, args)
    finally:
        db.close()


def cmd_search(args):
    db = SessionLocal()
    try:
        like = f"%{args.query}%"
        rows = db.query(Document).filter(
            (Document.title.ilike(like)) | (Document.current_content.ilike(like))
        ).order_by(Document.updated_at.desc()).limit(args.limit).all()
        emit([_serialize(d) for d in rows], args)
    finally:
        db.close()


def cmd_delete(args):
    db = SessionLocal()
    try:
        d = db.get(Document, args.id)
        if not d:
            fail(f"no document with id {args.id!r}")
        d.is_active = False
        db.commit()
        emit({"ok": True, "id": d.id, "soft_deleted": True}, args)
    finally:
        db.close()


def _build_parser():
    common = argparse.ArgumentParser(add_help=False)
    common.add_argument("--pretty", action="store_true")
    p = argparse.ArgumentParser(prog="odysseus-docs", parents=[common])
    sub = p.add_subparsers(dest="cmd", required=True)

    pl = sub.add_parser("list", parents=[common])
    pl.add_argument("--active", action="store_true", help="only is_active=True")
    pl.add_argument("--session", help="filter by session_id")
    pl.add_argument("--limit", type=int, default=50)
    pl.set_defaults(func=cmd_list)

    psh = sub.add_parser("show", parents=[common])
    psh.add_argument("id")
    psh.set_defaults(func=cmd_show)

    pv = sub.add_parser("versions", parents=[common])
    pv.add_argument("id")
    pv.set_defaults(func=cmd_versions)

    pe = sub.add_parser("export", parents=[common])
    pe.add_argument("id")
    pe.add_argument("--version", type=int, help="specific version (default: current)")
    pe.add_argument("--raw", action="store_true", help="write raw content to stdout")
    pe.set_defaults(func=cmd_export)

    ps = sub.add_parser("search", parents=[common])
    ps.add_argument("query")
    ps.add_argument("--limit", type=int, default=50)
    ps.set_defaults(func=cmd_search)

    pd = sub.add_parser("delete", parents=[common])
    pd.add_argument("id")
    pd.set_defaults(func=cmd_delete)

    return p


if __name__ == "__main__":
    sys.exit(run(_build_parser()))
