#!/usr/bin/env python3
"""odysseus-notes — shell wrapper around the notes feature.

Reads the same SQLite the web UI uses. Output JSON; pipe-friendly.

    odysseus-notes list --label calendar | jq -r '.[] | .title'
    odysseus-notes show NOTE_ID
    odysseus-notes search "invoice"
    odysseus-notes create --title "Buy milk" --content "..."
    odysseus-notes delete NOTE_ID
"""

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, uuid
from datetime import datetime
from pathlib import Path

try:
    from core.database import SessionLocal, Note
    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 _load_items(raw) -> list:
    if not raw:
        return []
    try:
        items = json.loads(raw)
    except (TypeError, json.JSONDecodeError):
        return []
    if not isinstance(items, list):
        return []
    return [item for item in items if isinstance(item, dict)]


def _serialize(n: "Note") -> dict:
    return {
        "id": n.id,
        "title": n.title or "",
        "content": n.content or "",
        "items": _load_items(n.items),
        "note_type": n.note_type or "note",
        "color": n.color or "",
        "label": n.label or "",
        "pinned": bool(n.pinned),
        "archived": bool(n.archived),
        "due_date": n.due_date or "",
        "source": n.source or "user",
        "created_at": n.created_at.isoformat() if n.created_at else "",
        "updated_at": n.updated_at.isoformat() if n.updated_at else "",
    }


def cmd_list(args):
    db = SessionLocal()
    try:
        q = db.query(Note)
        if not args.archived:
            q = q.filter(Note.archived == False)  # noqa: E712
        if args.label:
            q = q.filter(Note.label == args.label)
        if args.pinned:
            q = q.filter(Note.pinned == True)  # noqa: E712
        q = q.order_by(Note.pinned.desc(), Note.sort_order.asc(), Note.updated_at.desc()).limit(args.limit)
        emit([_serialize(n) for n in q.all()], args)
    finally:
        db.close()


def cmd_show(args):
    db = SessionLocal()
    try:
        n = db.get(Note, args.id)
        if not n:
            fail(f"no note with id {args.id!r}")
        emit(_serialize(n), args)
    finally:
        db.close()


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


def cmd_create(args):
    db = SessionLocal()
    try:
        n = Note(
            id=str(uuid.uuid4()),
            title=args.title,
            content=args.content or "",
            note_type=args.type,
            color=args.color or None,
            label=args.label or None,
            pinned=bool(args.pin),
            source="user",
        )
        db.add(n)
        db.commit()
        db.refresh(n)
        emit(_serialize(n), args)
    finally:
        db.close()


def cmd_delete(args):
    db = SessionLocal()
    try:
        n = db.get(Note, args.id)
        if not n:
            fail(f"no note with id {args.id!r}")
        snap = _serialize(n)
        db.delete(n)
        db.commit()
        emit({"ok": True, "deleted": snap}, 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-notes", parents=[common])
    sub = p.add_subparsers(dest="cmd", required=True)

    pl = sub.add_parser("list", parents=[common])
    pl.add_argument("--label")
    pl.add_argument("--archived", action="store_true", help="include archived")
    pl.add_argument("--pinned", action="store_true", help="pinned only")
    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)

    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)

    pc = sub.add_parser("create", parents=[common])
    pc.add_argument("--title", required=True)
    pc.add_argument("--content", default="")
    pc.add_argument("--type", choices=["note", "checklist"], default="note")
    pc.add_argument("--color")
    pc.add_argument("--label")
    pc.add_argument("--pin", action="store_true")
    pc.set_defaults(func=cmd_create)

    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()))
