#!/usr/bin/env python3
"""setup-gitea-labels.py: 一次性迁移脚本，把指定 label 命名空间改为 scoped。

#331 v1 第 2 件事：让 Gitea 平台原生互斥规则承担"一个 issue 同时只能挂一个
Status/* 或 Owner/*"的约束，免去 CLI 兜底逻辑。Gitea 的 scoped label 机制
是 label 名形如 `<scope>/<value>` 且 `exclusive=true` 时同 scope 自动互斥。

模式：
  python3 setup-gitea-labels.py                        # dry-run，列将改的 label
  python3 setup-gitea-labels.py --execute              # 真改
  python3 setup-gitea-labels.py --scope Status         # 只改 Status/*
  python3 setup-gitea-labels.py --execute --scope Owner

幂等：已 scoped 的 label 跳过；运行 N 次结果一致。
范围（默认）：Status/* + Owner/*（Kind/* 不动——可能想多 Kind 共存；
Priority/* / Reviewed/* 已 scoped）。

Auth: GITEA_API_TOKEN / GITEA_TOKEN（需要 write:repository 权限）。
退出码: 0 成功 / 1 用户错 / 2 系统错 / 3 约定拦截
设计依据: docs/standards/15-cli-design-spec.md
"""

from __future__ import annotations

import argparse
import json
import sys
from datetime import datetime, timezone
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
from _gitea_api import Api, detect_repo, get_token, paginate  # noqa: E402

DEFAULT_SCOPES = ("Status", "Owner")
REPO_ROOT = Path(__file__).resolve().parents[2]
AUDIT_DIR = REPO_ROOT / "testing" / "reports"


def patch_exclusive(api: Api, label: dict) -> tuple[bool, str]:
    """PATCH /labels/{id} to set exclusive=true. Preserves name/color/desc."""
    code, txt = api.patch(f"/labels/{label['id']}", {
        "name": label["name"],
        "color": label.get("color", "#cccccc"),
        "description": label.get("description", ""),
        "exclusive": True,
    })
    if code in (200, 201):
        return True, ""
    return False, f"HTTP {code}: {txt[:200]}"


def main() -> int:
    parser = argparse.ArgumentParser(
        description="Migrate Gitea labels to scoped (exclusive=true).",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="Examples:\n"
               "  setup-gitea-labels.py                 # dry-run all default scopes\n"
               "  setup-gitea-labels.py --execute       # apply\n"
               "  setup-gitea-labels.py --scope Owner --execute\n",
    )
    parser.add_argument("--scope", action="append", default=[],
                        help="label scope prefix (repeatable). "
                             f"Default: {','.join(DEFAULT_SCOPES)}")
    parser.add_argument("--execute", action="store_true",
                        help="actually apply changes (default: dry-run)")
    args = parser.parse_args()

    scopes = tuple(args.scope) if args.scope else DEFAULT_SCOPES

    # Batch scripts call get_token() without on_missing callback — falls back
    # to plain-text stderr + exit 1, matching sweep-remote-stale.py et al.
    # scripts/ops/gitea CLI passes a JSON-aware callback for --json mode;
    # see _gitea_api.get_token docstring.
    token = get_token()
    repo = detect_repo()
    api = Api(token, repo)

    labels = paginate(api, "/labels", {"limit": 50})

    targets: list[dict] = []  # need migration
    already: list[dict] = []  # already scoped
    for l in labels:
        name = l.get("name", "")
        if "/" not in name:
            continue
        scope = name.split("/", 1)[0]
        if scope not in scopes:
            continue
        if l.get("exclusive"):
            already.append(l)
        else:
            targets.append(l)

    print(f"repo: {repo}")
    print(f"scopes: {','.join(scopes)}")
    print(f"already scoped: {len(already)}")
    print(f"to migrate:     {len(targets)}")
    print("")
    for l in targets:
        print(f"  PATCH id={l['id']:4d}  {l['name']}")

    if not targets:
        print("\nnothing to do.")
        return 0

    if not args.execute:
        print("\n[dry-run] re-run with --execute to apply.")
        return 0

    print(f"\n[execute] applying {len(targets)} patches...")
    succeeded: list[str] = []
    failed: list[tuple[str, str]] = []
    for l in targets:
        ok, err = patch_exclusive(api, l)
        status = "✓" if ok else "✗"
        print(f"  {status} {l['name']}{(': ' + err) if err else ''}")
        if ok:
            succeeded.append(l["name"])
        else:
            failed.append((l["name"], err))

    AUDIT_DIR.mkdir(parents=True, exist_ok=True)
    ts = datetime.now(timezone.utc).strftime("%Y%m%d-%H%M%S")
    audit = AUDIT_DIR / f"setup-gitea-labels-{ts}.md"
    audit.write_text(
        f"# Gitea label scoped migration · {ts}\n\n"
        f"- repo: `{repo}`\n"
        f"- scopes: {','.join(scopes)}\n"
        f"- already scoped (skipped): {len(already)}\n"
        f"- patched: {len(succeeded)}\n"
        f"- failed: {len(failed)}\n\n"
        f"## Patched\n\n" +
        "\n".join(f"- {n}" for n in succeeded) +
        ("\n\n## Failed\n\n" + "\n".join(f"- {n}: {e}" for n, e in failed)
         if failed else "") +
        "\n",
        encoding="utf-8",
    )
    print(f"\naudit log: {audit.relative_to(REPO_ROOT)}")
    return 0 if not failed else 2


if __name__ == "__main__":
    sys.exit(main())
