#!/usr/bin/env python3
"""chctl — manage a Content Hub mirror's content from the command line.

Talks to the mirror's admin API (the same backend the web GUI uses), so it works
from your laptop against a remote mirror. Configure the target once:

    export CHM_URL=http://mirror-host:9000      # admin API base
    export CHM_TOKEN=...                          # if ADMIN_TOKEN is set on the mirror

Commands:
    chctl list                                   list current local content
    chctl add <artifact.tgz|.zip> [--type T] [--build N]   add by artifact
    chctl add-entry --name N --type T --version V [--label ...] [--publisher ...]
    chctl remove <type> <name>                   remove an entry (+ its artifact)
    chctl rebuild                                re-merge + rewrite the served tree

Examples:
    chctl add ./myconn-1.0.0.tgz
    chctl add-entry --name acme --type connector --version 1.0.0 --label "Acme"
    chctl remove connector acme
"""

from __future__ import annotations

import argparse
import os
import sys

import requests


def _base() -> str:
    return os.environ.get("CHM_URL", "http://localhost:9000").rstrip("/")


def _headers() -> dict:
    tok = os.environ.get("CHM_TOKEN", "").strip()
    return {"Authorization": f"Bearer {tok}"} if tok else {}


def _show(resp: requests.Response) -> int:
    try:
        body = resp.json()
    except ValueError:
        body = resp.text
    if resp.ok:
        print(body if isinstance(body, str) else _fmt(body))
        return 0
    print(f"error {resp.status_code}: {body}", file=sys.stderr)
    return 1


def _fmt(obj) -> str:
    import json

    return json.dumps(obj, indent=2)


def cmd_list(args) -> int:
    r = requests.get(f"{_base()}/api/content", headers=_headers(), timeout=30)
    if not r.ok:
        return _show(r)
    d = r.json()
    print(f"{d['count']} local entr(y/ies):")
    for e in d["entries"]:
        print(f"  {e['type']:12} {e['name']:28} {e.get('version', ''):10} {e.get('label', '')}")
    return 0


def cmd_add(args) -> int:
    if not os.path.isfile(args.artifact):
        print(f"no such file: {args.artifact}", file=sys.stderr)
        return 1
    data = {}
    if args.type:
        data["type"] = args.type
    if args.build:
        data["buildNumber"] = str(args.build)
    with open(args.artifact, "rb") as fh:
        files = {"artifact": (os.path.basename(args.artifact), fh)}
        r = requests.post(f"{_base()}/api/content", headers=_headers(), data=data, files=files, timeout=120)
    return _show(r)


def cmd_add_entry(args) -> int:
    payload = {
        "name": args.name,
        "type": args.type,
        "version": args.version,
        "buildNumber": args.build,
        "label": args.label or args.name,
        "description": args.description or "",
        "publisher": args.publisher or "",
        "category": args.category or [],
    }
    r = requests.post(f"{_base()}/api/content", headers=_headers(), json=payload, timeout=60)
    return _show(r)


def cmd_remove(args) -> int:
    r = requests.delete(f"{_base()}/api/content/{args.type}/{args.name}", headers=_headers(), timeout=60)
    return _show(r)


def cmd_rebuild(args) -> int:
    r = requests.post(f"{_base()}/api/rebuild", headers=_headers(), timeout=120)
    return _show(r)


def main(argv=None) -> int:
    p = argparse.ArgumentParser(prog="chctl", description="Manage a Content Hub mirror's content.")
    sub = p.add_subparsers(dest="cmd", required=True)

    sub.add_parser("list", help="list current local content").set_defaults(fn=cmd_list)

    a = sub.add_parser("add", help="add content by artifact (.tgz/.zip)")
    a.add_argument("artifact")
    a.add_argument("--type", choices=["connector", "widget", "solutionpack", "ai_agent"], default=None)
    a.add_argument("--build", type=int, default=None)
    a.set_defaults(fn=cmd_add)

    ae = sub.add_parser("add-entry", help="add a catalog entry by fields (no artifact)")
    ae.add_argument("--name", required=True)
    ae.add_argument("--type", required=True, choices=["connector", "widget", "solutionpack", "ai_agent"])
    ae.add_argument("--version", required=True)
    ae.add_argument("--build", type=int, default=1)
    ae.add_argument("--label")
    ae.add_argument("--description")
    ae.add_argument("--publisher")
    ae.add_argument("--category")
    ae.set_defaults(fn=cmd_add_entry)

    rm = sub.add_parser("remove", help="remove an entry by type + name")
    rm.add_argument("type")
    rm.add_argument("name")
    rm.set_defaults(fn=cmd_remove)

    sub.add_parser("rebuild", help="re-merge + rewrite the served tree").set_defaults(fn=cmd_rebuild)

    args = p.parse_args(argv)
    try:
        return args.fn(args)
    except requests.exceptions.RequestException as exc:
        print(f"cannot reach mirror admin at {_base()}: {exc}", file=sys.stderr)
        return 2


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