#!/usr/bin/env python3
"""Operator tool: report or GC orphan vault attachments (pre-upload failures, dangling refs).

Uses ``chat_storage`` directly against the local Mugi DB (same as the backend).

Examples:
  python scripts/vault_orphan_attachments.py report
  python scripts/vault_orphan_attachments.py gc --dry-run
  python scripts/vault_orphan_attachments.py gc --apply --max 20
"""
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

REPO_ROOT = Path(__file__).resolve().parents[1]
if str(REPO_ROOT) not in sys.path:
    sys.path.insert(0, str(REPO_ROOT))


def _print_json(obj: object) -> None:
    print(json.dumps(obj, indent=2, sort_keys=True))


def cmd_report(args: argparse.Namespace) -> int:
    import chat_storage as cs

    report = cs.orphan_vault_attachment_report(limit=args.limit, offset=args.offset)
    _print_json(report)
    return 0 if report.get("ok") else 1


def cmd_gc(args: argparse.Namespace) -> int:
    import chat_storage as cs

    dry_run = not args.apply
    result = cs.gc_orphan_vault_attachments(
        dry_run=dry_run,
        max_delete=args.max,
        include_dangling=args.include_dangling,
        include_deleted_note=args.include_deleted_note,
    )
    _print_json(result)
    if not result.get("ok"):
        if result.get("errors"):
            print(f"\n{result.get('error_count', len(result.get('errors') or []))} error(s) — see errors[]", file=sys.stderr)
        return 1
    if dry_run:
        print("\n(dry run — pass --apply to delete)", file=sys.stderr)
    return 0


def main() -> int:
    parser = argparse.ArgumentParser(description="Vault orphan attachment report / GC")
    sub = parser.add_subparsers(dest="command", required=True)

    report_p = sub.add_parser("report", help="List orphan attachment rows")
    report_p.add_argument("--limit", type=int, default=200)
    report_p.add_argument("--offset", type=int, default=0)
    report_p.set_defaults(func=cmd_report)

    gc_p = sub.add_parser("gc", help="Delete orphan attachments (default dry-run)")
    gc_p.add_argument("--apply", action="store_true", help="Actually delete (default is dry-run)")
    gc_p.add_argument("--max", type=int, default=50, dest="max", help="Max rows per run")
    gc_p.add_argument("--include-dangling", action="store_true", help="Also GC missing_note orphans")
    gc_p.add_argument(
        "--include-deleted-note",
        action="store_true",
        help="Also GC attachments on soft-deleted notes (destructive)",
    )
    gc_p.set_defaults(func=cmd_gc)

    args = parser.parse_args()
    return int(args.func(args))


if __name__ == "__main__":
    raise SystemExit(main())
