#!/usr/bin/env python3
"""
vaultq — the vault-ops CLI: deterministic retrieval verbs the model calls by
name instead of writing grep or throwaway Python. JSON out by default (token-lean
for tool use); `--pretty` for a human read. See README.md for the full design.

    vaultq resolve   "<name|email|phone>" [--kind person|org|area|any] [--limit N]
    vaultq find      "<query>" [--type T] [--area A] [--status S] [--tag G]
                                [--since YYYY-MM-DD] [--limit N]
    vaultq neighbors "<note>"  [--hops N] [--dir in|out|both] [--type T] [--limit N]
    vaultq route     "<text | @file>" [--limit N] [--explain]
    vaultq dupes     "<note | text | @file>" [--threshold 0.6] [--limit N]
    vaultq load      "<note | topic>" [--budget 6000] [--manifest]

All six verbs shipped (M2–M5). load's default output is the markdown pack
itself (its product IS prompt context); --manifest for the JSON view.
"""
import sys
import json
import argparse
from pathlib import Path

from .vaultgraph import find_vault_root  # noqa: E402
from . import resolve as _resolve  # noqa: E402
from . import find as _find  # noqa: E402
from . import neighbors as _neighbors  # noqa: E402
from . import route as _route  # noqa: E402
from . import dupes as _dupes  # noqa: E402
from . import load as _load  # noqa: E402


def _emit(out, pretty, printer):
    printer(out) if pretty else print(json.dumps(out, indent=1))


def cmd_resolve(args):
    _emit(_resolve.resolve(find_vault_root(), args.query,
                           kind=args.kind, limit=args.limit),
          args.pretty, _resolve.print_pretty)


def cmd_find(args):
    _emit(_find.find(find_vault_root(), args.query, type_=args.type_,
                     area=args.area, status=args.status, tag=args.tag,
                     since=args.since, limit=args.limit,
                     passages=args.passages),
          args.pretty, _find.print_pretty)


def cmd_neighbors(args):
    _emit(_neighbors.neighbors(find_vault_root(), args.seed, hops=args.hops,
                               direction=args.dir, type_=args.type_,
                               limit=args.limit),
          args.pretty, _neighbors.print_pretty)


def cmd_route(args):
    _emit(_route.route(find_vault_root(), _route._read_input(args.content),
                       limit=args.limit, explain=args.explain),
          args.pretty, _route.print_pretty)


def cmd_dupes(args):
    _emit(_dupes.dupes(find_vault_root(), args.query,
                       threshold=args.threshold, limit=args.limit),
          args.pretty, _dupes.print_pretty)


def cmd_load(args):
    md, man = _load.load(find_vault_root(), args.query, budget=args.budget)
    if md is None or args.manifest:
        print(json.dumps(man, indent=1, ensure_ascii=False))
    else:
        print(md)


def main():
    ap = argparse.ArgumentParser(
        prog="vaultq", description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    sub = ap.add_subparsers(dest="verb", required=True)

    r = sub.add_parser("resolve", help="entity (name/email/phone) → canonical note")
    r.add_argument("query")
    r.add_argument("--kind", choices=list(_resolve.KIND_TYPES), default="any")
    r.add_argument("--limit", type=int, default=8)
    r.add_argument("--pretty", action="store_true", help="human output")
    r.set_defaults(func=cmd_resolve)

    f = sub.add_parser("find", help="ranked keyword search (replaces grep -rl)")
    f.add_argument("query")
    f.add_argument("--type", dest="type_")
    f.add_argument("--area")
    f.add_argument("--status")
    f.add_argument("--tag")
    f.add_argument("--since", help="YYYY-MM-DD — only notes dated on/after")
    f.add_argument("--limit", type=int, default=20)
    f.add_argument("--passages", type=int, default=0, metavar="N",
                   help="attach up to N scored passages (with line numbers) per hit")
    f.add_argument("--pretty", action="store_true", help="human output")
    f.set_defaults(func=cmd_find)

    n = sub.add_parser("neighbors", help="graph neighborhood of a note")
    n.add_argument("seed", help="note name, alias, or rel path")
    n.add_argument("--hops", type=int, default=1)
    n.add_argument("--dir", choices=["in", "out", "both"], default="both")
    n.add_argument("--type", dest="type_")
    n.add_argument("--limit", type=int, default=50)
    n.add_argument("--pretty", action="store_true", help="human output")
    n.set_defaults(func=cmd_neighbors)

    rt = sub.add_parser("route", help="content → ranked candidate homes + confidence")
    rt.add_argument("content", help="text to route, or @/path/to/file")
    rt.add_argument("--limit", type=int, default=10)
    rt.add_argument("--explain", action="store_true")
    rt.add_argument("--pretty", action="store_true", help="human output")
    rt.set_defaults(func=cmd_route)

    d = sub.add_parser("dupes", help="near-duplicate candidates for a note/text")
    d.add_argument("query", help="existing note (name/rel), raw text, or @file")
    d.add_argument("--threshold", type=float, default=_dupes.THRESHOLD)
    d.add_argument("--limit", type=int, default=10)
    d.add_argument("--pretty", action="store_true", help="human output")
    d.set_defaults(func=cmd_dupes)

    lo = sub.add_parser("load", help="token-budgeted context pack for a note/topic")
    lo.add_argument("query", help="note name/alias/rel path, or a topic")
    lo.add_argument("--budget", type=int, default=_load.BUDGET)
    lo.add_argument("--manifest", action="store_true",
                    help="print the JSON manifest instead of the pack")
    lo.set_defaults(func=cmd_load)

    args = ap.parse_args()
    args.func(args)


if __name__ == "__main__":
    main()
