"""Non-interactive State Hub repository-rename orchestration CLI."""

from __future__ import annotations

import argparse
import json
import os
import re
import shlex
import stat
import sys
import urllib.error
import urllib.parse
import urllib.request
import uuid
from pathlib import Path
from typing import Any, Callable

from repository_rename_workplan import (
    WorkplanGenerationError,
    build_generation_snapshot,
    content_checksum,
    default_output_path,
    render_workplan,
    write_workplan_exclusive,
)


CLI_SCHEMA_VERSION = "state-hub.repository-rename-cli.v1"
DEFAULT_API_BASE = os.environ.get("API_BASE", "http://127.0.0.1:8000")
FORWARD_PHASES = (
    "forge-renamed",
    "statehub-rebound",
    "source-synced",
    "consumers-verified",
    "completed",
)
NEXT_PHASE = {
    "preflighted": "forge-renamed",
    "forge-renamed": "statehub-rebound",
    "statehub-rebound": "source-synced",
    "source-synced": "consumers-verified",
    "consumers-verified": "completed",
}
_HEADERS = {"X-StateHub-Component": "state-hub.repository-rename-cli"}
_SENSITIVE_KEYS = {
    "authorization",
    "cookie",
    "set-cookie",
    "password",
    "secret",
    "api-key",
    "api_key",
    "access-token",
    "access_token",
    "refresh-token",
    "refresh_token",
    "credential",
}


class RenameCLIError(RuntimeError):
    def __init__(
        self,
        message: str,
        *,
        code: str = "rename_cli_error",
        status_code: int | None = None,
        details: Any = None,
    ) -> None:
        super().__init__(message)
        self.code = code
        self.status_code = status_code
        self.details = details


def _redact_text(value: str) -> str:
    value = re.sub(
        r"(https?://)[^/@\s]+:[^/@\s]+@",
        r"\1[REDACTED]@",
        value,
        flags=re.IGNORECASE,
    )
    value = re.sub(
        r"\b(Bearer|Basic)\s+[A-Za-z0-9._~+/=-]+",
        r"\1 [REDACTED]",
        value,
        flags=re.IGNORECASE,
    )
    value = re.sub(
        r"([?&][^=&#\s]*(?:api[_-]?key|password|secret|signature|token|credential)"
        r"[^=&#\s]*=)[^&#\s]+",
        r"\1[REDACTED]",
        value,
        flags=re.IGNORECASE,
    )
    return value


def _public(value: Any, *, redact_preflight_token: bool = True) -> Any:
    if isinstance(value, dict):
        result: dict[str, Any] = {}
        for key, item in value.items():
            normalized = key.lower()
            if normalized in _SENSITIVE_KEYS or any(
                marker in normalized
                for marker in ("password", "authorization", "credential")
            ):
                result[key] = "[REDACTED]"
            elif key == "preflight_token" and redact_preflight_token and item:
                result[key] = "[REDACTED: use --output preflight file]"
            else:
                result[key] = _public(
                    item, redact_preflight_token=redact_preflight_token
                )
        return result
    if isinstance(value, list):
        return [
            _public(item, redact_preflight_token=redact_preflight_token)
            for item in value
        ]
    if isinstance(value, str):
        return _redact_text(value)
    return value


def _api_request(
    api_base: str,
    method: str,
    path: str,
    body: dict[str, Any] | None = None,
    *,
    expected: type | tuple[type, ...] = dict,
) -> Any:
    data = json.dumps(body).encode("utf-8") if body is not None else None
    headers = dict(_HEADERS)
    if data is not None:
        headers["Content-Type"] = "application/json"
    request = urllib.request.Request(
        api_base.rstrip("/") + path,
        data=data,
        headers=headers,
        method=method,
    )
    try:
        with urllib.request.urlopen(request, timeout=30) as response:
            payload = json.loads(response.read())
    except urllib.error.HTTPError as exc:
        try:
            payload = json.loads(exc.read())
        except (json.JSONDecodeError, UnicodeDecodeError):
            payload = {}
        detail = payload.get("detail", payload) if isinstance(payload, dict) else {}
        if isinstance(detail, dict):
            message = str(detail.get("message") or f"State Hub returned HTTP {exc.code}")
            code = str(detail.get("code") or "state_hub_http_error")
            details = detail.get("details")
        else:
            message = str(detail or f"State Hub returned HTTP {exc.code}")
            code = "state_hub_http_error"
            details = None
        raise RenameCLIError(
            _redact_text(message),
            code=code,
            status_code=exc.code,
            details=_public(details),
        ) from exc
    except (urllib.error.URLError, TimeoutError) as exc:
        raise RenameCLIError(
            "State Hub is unavailable",
            code="state_hub_unavailable",
            details={"reason": _redact_text(str(exc.reason if hasattr(exc, "reason") else exc))},
        ) from exc
    except (json.JSONDecodeError, UnicodeDecodeError) as exc:
        raise RenameCLIError(
            "State Hub returned an invalid JSON response",
            code="invalid_state_hub_response",
        ) from exc
    if not isinstance(payload, expected):
        expected_name = (
            ", ".join(item.__name__ for item in expected)
            if isinstance(expected, tuple)
            else expected.__name__
        )
        raise RenameCLIError(
            f"State Hub returned JSON with the wrong shape; expected {expected_name}",
            code="invalid_state_hub_response",
        )
    return payload


def _load_json(path: str, *, expected: type | tuple[type, ...]) -> Any:
    try:
        if path == "-":
            value = json.load(sys.stdin)
        else:
            target = Path(path)
            if target.stat().st_mode & (stat.S_IRWXG | stat.S_IRWXO):
                raise RenameCLIError(
                    f"Refusing non-private input file {target}; require mode 0600",
                    code="unsafe_input_permissions",
                )
            value = json.loads(target.read_text(encoding="utf-8"))
    except RenameCLIError:
        raise
    except (OSError, json.JSONDecodeError) as exc:
        raise RenameCLIError(
            f"Could not read JSON input {path}",
            code="invalid_json_input",
            details={"reason": _redact_text(str(exc))},
        ) from exc
    if not isinstance(value, expected):
        names = (
            ", ".join(item.__name__ for item in expected)
            if isinstance(expected, tuple)
            else expected.__name__
        )
        raise RenameCLIError(
            f"JSON input {path} must contain {names}", code="invalid_json_input"
        )
    return value


def _write_private_json(path: str, payload: dict[str, Any]) -> None:
    target = Path(path)
    target.parent.mkdir(parents=True, exist_ok=True)
    try:
        descriptor = os.open(target, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
    except FileExistsError as exc:
        raise RenameCLIError(
            f"Refusing to overwrite existing preflight file {target}",
            code="output_exists",
        ) from exc
    try:
        with os.fdopen(descriptor, "w", encoding="utf-8") as handle:
            json.dump(payload, handle, indent=2, sort_keys=True)
            handle.write("\n")
    except Exception:
        target.unlink(missing_ok=True)
        raise


def _confirmation(repo_id: str, old_slug: str, new_slug: str) -> str:
    return f"rename:{repo_id}:{old_slug}:{new_slug}"


def _rollback_confirmation(operation_id: str) -> str:
    return f"rollback:{operation_id}"


def _validated_uuid(value: str, *, field: str = "operation ID") -> str:
    try:
        return str(uuid.UUID(value))
    except (AttributeError, TypeError, ValueError) as exc:
        raise RenameCLIError(
            f"{field} must be a UUID",
            code="invalid_operation_id",
        ) from exc


def _command(*parts: str | None) -> list[str]:
    return [part for part in parts if part is not None]


def _next_for_operation(operation: dict[str, Any]) -> dict[str, Any]:
    operation_id = str(operation["id"])
    repo_id = str(operation["repo_id"])
    phase = str(operation["phase"])
    confirmation = _confirmation(
        repo_id, str(operation["old_slug"]), str(operation["new_slug"])
    )
    if phase in NEXT_PHASE:
        target = NEXT_PHASE[phase]
        command = _command(
            "statehub",
            "repo",
            "rename",
            "apply",
            operation_id,
            "--phase",
            target,
            "--confirm",
            confirmation,
        )
        if target in {"source-synced", "consumers-verified"}:
            command.extend(["--evidence-file", "<evidence.json>"])
        if target == "consumers-verified":
            command.extend(["--checks-file", "<checks.json>"])
        return {
            "action": "apply-phase",
            "phase": target,
            "expected_phase": phase,
            "requires_confirmation": True,
            "requires_evidence": target in {"source-synced", "consumers-verified"},
            "requires_checks": target == "consumers-verified",
            "command": command,
        }
    if phase == "rollback-preflight":
        return {
            "action": "execute-rollback",
            "requires_confirmation": True,
            "command": [
                "statehub",
                "repo",
                "rename",
                "rollback",
                operation_id,
                "--confirm",
                _rollback_confirmation(operation_id),
                "--execute",
            ],
        }
    if phase == "completed":
        return {"action": "none", "reason": "rename completed", "command": None}
    if phase == "rolled-back":
        return {"action": "none", "reason": "rename rolled back", "command": None}
    return {
        "action": "inspect",
        "reason": f"unsupported or unknown phase {phase!r}",
        "command": ["statehub", "repo", "rename", "status", operation_id],
    }


def _state_for_operation(operation: dict[str, Any]) -> str:
    if operation.get("phase") == "rolled-back":
        return "rolled-back"
    if operation.get("error_code"):
        return "failed"
    if operation.get("phase") == "completed":
        return "achieved"
    return "planned"


def _envelope(
    command: str,
    *,
    state: str,
    ok: bool,
    result: dict[str, Any] | None = None,
    operation_id: str | None = None,
    repo_id: str | None = None,
    phase: str | None = None,
    next_safe_action: dict[str, Any] | None = None,
    error: dict[str, Any] | None = None,
) -> dict[str, Any]:
    return {
        "schema_version": CLI_SCHEMA_VERSION,
        "command": command,
        "ok": ok,
        "state": state,
        "operation_id": operation_id,
        "repo_id": repo_id,
        "phase": phase,
        "next_safe_action": next_safe_action,
        "result": result,
        "error": error,
    }


def _emit(payload: dict[str, Any], *, as_json: bool) -> None:
    public = _public(payload)
    if as_json:
        print(json.dumps(public, indent=2, sort_keys=True))
        return
    state = str(public["state"]).upper()
    stream = sys.stderr if not public["ok"] else sys.stdout
    print(f"{state}: repository rename {public['command']}", file=stream)
    if public.get("operation_id"):
        print(f"  operation: {public['operation_id']}", file=stream)
    if public.get("repo_id"):
        print(f"  repository: {public['repo_id']}", file=stream)
    if public.get("phase"):
        print(f"  phase: {public['phase']}", file=stream)
    result = public.get("result") or {}
    if public["command"] == "preflight":
        print(f"  safe to apply: {bool(result.get('safe_to_apply'))}", file=stream)
        print(f"  blockers: {len(result.get('blockers') or [])}", file=stream)
        if result.get("preflight_file"):
            print(f"  private preflight: {result['preflight_file']}", file=stream)
    elif public["command"] == "generate-workplan" and result.get("output"):
        print(f"  workplan: {result['output']}", file=stream)
        print(f"  workplan id: {result.get('workplan_id')}", file=stream)
    error = public.get("error")
    if error:
        print(f"  error: {error.get('message')}", file=stream)
    action = public.get("next_safe_action") or {}
    if action.get("action"):
        print(f"  next safe action: {action['action']}", file=stream)
    if action.get("command"):
        print(f"  command: {shlex.join(action['command'])}", file=stream)


def _run(args: argparse.Namespace, action: Callable[[], dict[str, Any]]) -> None:
    try:
        payload = action()
    except RenameCLIError as exc:
        payload = _envelope(
            args.rename_command,
            state="failed",
            ok=False,
            operation_id=getattr(args, "operation_id", None),
            error={
                "code": exc.code,
                "message": str(exc),
                "status_code": exc.status_code,
                "details": exc.details,
            },
            next_safe_action={"action": "inspect-input-or-status", "command": None},
        )
        _emit(payload, as_json=args.as_json)
        raise SystemExit(1) from exc
    _emit(payload, as_json=args.as_json)
    if not payload["ok"]:
        raise SystemExit(2)


def _resolve_repo(args: argparse.Namespace, slug: str) -> dict[str, Any]:
    quoted = urllib.parse.quote(slug, safe="")
    repo = _api_request(args.api_base, "GET", f"/repos/{quoted}")
    if repo.get("slug_status") == "alias":
        raise RenameCLIError(
            f"{slug!r} is a prior alias; current canonical slug is {repo.get('canonical_slug')!r}",
            code="old_slug_is_alias",
        )
    return repo


def cmd_preflight(args: argparse.Namespace) -> None:
    def action() -> dict[str, Any]:
        operation_id = (
            _validated_uuid(args.operation_id)
            if args.operation_id
            else str(uuid.uuid4())
        )
        if args.output and Path(args.output).exists():
            raise RenameCLIError(
                f"Refusing to overwrite existing preflight file {args.output}",
                code="output_exists",
            )
        repo = _resolve_repo(args, args.old_slug)
        edge_writes: list[dict[str, Any]] = []
        if args.queued_edge_writes:
            edge_writes = _load_json(args.queued_edge_writes, expected=list)
            if not all(isinstance(item, dict) for item in edge_writes):
                raise RenameCLIError(
                    "Queued edge-write input must be a list of objects",
                    code="invalid_json_input",
                )
        report = _api_request(
            args.api_base,
            "POST",
            f"/repos/{repo['id']}/rename/preflight",
            {"new_slug": args.new_slug, "queued_edge_writes": edge_writes},
        )
        confirmation = _confirmation(str(repo["id"]), args.old_slug, args.new_slug)
        private = _envelope(
            "preflight",
            state="planned",
            ok=bool(report.get("safe_to_apply")),
            result=report,
            operation_id=operation_id,
            repo_id=str(repo["id"]),
            phase=None,
        )
        if args.output:
            _write_private_json(args.output, private)
        if report.get("safe_to_apply") and args.output:
            next_action = {
                "action": "start-operation",
                "requires_confirmation": True,
                "command": [
                    "statehub", "repo", "rename", "start",
                    args.old_slug, args.new_slug,
                    "--operation-id", operation_id,
                    "--preflight-file", args.output,
                    "--actor", "<actor>",
                    "--confirm", confirmation,
                ],
            }
        elif report.get("safe_to_apply"):
            next_action = {
                "action": "save-private-preflight",
                "requires_confirmation": False,
                "command": [
                    "statehub", "repo", "rename", "preflight",
                    args.old_slug, args.new_slug,
                    "--operation-id", operation_id,
                    "--output", "<preflight-report.json>",
                ],
            }
        else:
            next_action = {
                "action": "resolve-blockers",
                "requires_confirmation": False,
                "command": None,
            }
        public_report = dict(report)
        public_report["preflight_file"] = args.output
        return _envelope(
            "preflight",
            state="planned" if report.get("safe_to_apply") else "failed",
            ok=bool(report.get("safe_to_apply")),
            result=public_report,
            operation_id=operation_id,
            repo_id=str(repo["id"]),
            next_safe_action=next_action,
        )

    _run(args, action)


def cmd_generate_workplan(args: argparse.Namespace) -> None:
    def action() -> dict[str, Any]:
        if args.output and Path(args.output).expanduser().exists():
            raise RenameCLIError(
                f"Refusing to overwrite existing workplan {args.output}",
                code="output_exists",
            )
        repo = _resolve_repo(args, args.old_slug)
        workplans = _api_request(
            args.api_base,
            "GET",
            f"/workplans/?repo_id={urllib.parse.quote(str(repo['id']), safe='')}",
            expected=list,
        )
        if args.preflight_file:
            wrapper = _load_json(args.preflight_file, expected=dict)
            if (
                wrapper.get("schema_version") != CLI_SCHEMA_VERSION
                or wrapper.get("command") != "preflight"
                or str(wrapper.get("repo_id")) != str(repo["id"])
            ):
                raise RenameCLIError(
                    "Preflight file is not for this registered repository",
                    code="invalid_preflight_file",
                )
            report = wrapper.get("result") or {}
        else:
            report = _api_request(
                args.api_base,
                "POST",
                f"/repos/{repo['id']}/rename/preflight",
                {"new_slug": args.new_slug, "queued_edge_writes": []},
            )
        if (
            report.get("old_slug") != args.old_slug
            or report.get("new_slug") != args.new_slug
        ):
            raise RenameCLIError(
                "Preflight snapshot does not address the requested rename",
                code="invalid_preflight_file",
            )
        try:
            snapshot = build_generation_snapshot(
                repo=repo,
                preflight=report,
                workplans=workplans,
                repo_path_override=args.repo_path,
            )
            content = render_workplan(snapshot, owner=args.owner)
            output = (
                Path(args.output).expanduser().resolve()
                if args.output
                else default_output_path(snapshot)
            )
            write_workplan_exclusive(output, content)
        except WorkplanGenerationError as exc:
            raise RenameCLIError(str(exc), code=exc.code) from exc
        return _envelope(
            "generate-workplan",
            state="achieved",
            ok=True,
            result={
                "output": str(output),
                "workplan_id": snapshot.workplan_id,
                "workplan_prefix": snapshot.prefix,
                "content_checksum": content_checksum(content),
                "preflight_report_checksum": report.get("report_checksum"),
                "safe_to_apply_at_capture": bool(report.get("safe_to_apply")),
                "path_available": snapshot.path_available,
                "state_hub_uuid_fields_written": [],
            },
            repo_id=str(repo["id"]),
            next_safe_action={
                "action": "review-generated-workplan",
                "requires_confirmation": False,
                "command": None,
            },
        )

    _run(args, action)


def cmd_start(args: argparse.Namespace) -> None:
    def action() -> dict[str, Any]:
        operation_id = _validated_uuid(args.operation_id)
        try:
            existing = _api_request(
                args.api_base,
                "GET",
                f"/repository-renames/operations/{operation_id}",
            )
        except RenameCLIError as exc:
            if exc.status_code != 404:
                raise
        else:
            expected_confirmation = _confirmation(
                str(existing["repo_id"]),
                str(existing["old_slug"]),
                str(existing["new_slug"]),
            )
            if (
                existing.get("old_slug") != args.old_slug
                or existing.get("new_slug") != args.new_slug
                or existing.get("actor") != args.actor
                or args.confirm != expected_confirmation
            ):
                raise RenameCLIError(
                    "Operation ID is already bound to another repository rename",
                    code="operation_id_conflict",
                )
            existing = {**existing, "no_op": True}
            return _operation_envelope("start", existing)

        wrapper = _load_json(args.preflight_file, expected=dict)
        if (
            wrapper.get("schema_version") != CLI_SCHEMA_VERSION
            or wrapper.get("command") != "preflight"
        ):
            raise RenameCLIError(
                "Preflight file is not a repository-rename CLI preflight",
                code="invalid_preflight_file",
            )
        if str(wrapper.get("operation_id")) != operation_id:
            raise RenameCLIError(
                "Preflight operation ID does not match --operation-id",
                code="invalid_preflight_file",
            )
        report = wrapper.get("result") or {}
        repo = _resolve_repo(args, args.old_slug)
        if (
            str(wrapper.get("repo_id")) != str(repo["id"])
            or report.get("old_slug") != args.old_slug
            or report.get("new_slug") != args.new_slug
            or not report.get("safe_to_apply")
            or not report.get("preflight_token")
        ):
            raise RenameCLIError(
                "Preflight file does not authorize this repository rename",
                code="invalid_preflight_file",
            )
        expected_confirmation = _confirmation(
            str(repo["id"]), args.old_slug, args.new_slug
        )
        if args.confirm != expected_confirmation:
            raise RenameCLIError(
                "Explicit repository rename confirmation is incorrect",
                code="confirmation_mismatch",
            )
        operation = _api_request(
            args.api_base,
            "POST",
            f"/repos/{repo['id']}/rename/operations",
            {
                "operation_id": operation_id,
                "new_slug": args.new_slug,
                "preflight_token": report["preflight_token"],
                "confirmation": args.confirm,
                "actor": args.actor,
                "queued_edge_writes": report.get("queued_edge_writes") or [],
            },
        )
        return _operation_envelope("start", operation)

    _run(args, action)


def _load_operation(args: argparse.Namespace) -> dict[str, Any]:
    operation_id = _validated_uuid(args.operation_id)
    return _api_request(
        args.api_base,
        "GET",
        f"/repository-renames/operations/{operation_id}",
    )


def _operation_envelope(command: str, operation: dict[str, Any]) -> dict[str, Any]:
    return _envelope(
        command,
        state=_state_for_operation(operation),
        ok=not bool(operation.get("error_code")),
        result=operation,
        operation_id=str(operation["id"]),
        repo_id=str(operation["repo_id"]),
        phase=str(operation["phase"]),
        next_safe_action=_next_for_operation(operation),
    )


def cmd_status(args: argparse.Namespace) -> None:
    _run(args, lambda: _operation_envelope("status", _load_operation(args)))


def cmd_apply(args: argparse.Namespace) -> None:
    def action() -> dict[str, Any]:
        operation = _load_operation(args)
        expected_confirmation = _confirmation(
            str(operation["repo_id"]),
            str(operation["old_slug"]),
            str(operation["new_slug"]),
        )
        if args.confirm != expected_confirmation:
            raise RenameCLIError(
                "Explicit repository rename confirmation is incorrect",
                code="confirmation_mismatch",
            )
        evidence = (
            _load_json(args.evidence_file, expected=dict)
            if args.evidence_file
            else {}
        )
        checks = (
            _load_json(args.checks_file, expected=dict)
            if args.checks_file
            else {}
        )
        if any(not isinstance(value, bool) for value in checks.values()):
            raise RenameCLIError(
                "Checks file values must all be booleans",
                code="invalid_json_input",
            )
        updated = _api_request(
            args.api_base,
            "POST",
            f"/repos/{operation['repo_id']}/rename/operations/{operation['id']}/phases/{args.phase}",
            {
                "expected_phase": args.expected_phase or operation["phase"],
                "confirmation": args.confirm,
                "checks": checks,
                "evidence": evidence,
            },
        )
        return _operation_envelope("apply", updated)

    _run(args, action)


def cmd_verify(args: argparse.Namespace) -> None:
    def action() -> dict[str, Any]:
        operation = _load_operation(args)
        verification = _api_request(
            args.api_base,
            "GET",
            f"/repos/{operation['repo_id']}/rename/operations/{operation['id']}/verify",
        )
        if verification.get("ok"):
            next_action = _next_for_operation(operation)
            state = "achieved"
        else:
            next_action = {
                "action": "resolve-verification-failures",
                "command": [
                    "statehub", "repo", "rename", "status", str(operation["id"])
                ],
            }
            state = "failed"
        return _envelope(
            "verify",
            state=state,
            ok=bool(verification.get("ok")),
            result=verification,
            operation_id=str(operation["id"]),
            repo_id=str(operation["repo_id"]),
            phase=str(operation["phase"]),
            next_safe_action=next_action,
        )

    _run(args, action)


def cmd_rollback(args: argparse.Namespace) -> None:
    def action() -> dict[str, Any]:
        operation = _load_operation(args)
        operation_id = str(operation["id"])
        expected_confirmation = _rollback_confirmation(operation_id)
        if args.confirm != expected_confirmation:
            raise RenameCLIError(
                "Explicit rollback confirmation is incorrect",
                code="confirmation_mismatch",
            )
        if operation["phase"] == "rolled-back":
            return _operation_envelope("rollback", operation)
        if operation["phase"] != "rollback-preflight":
            preflight = _api_request(
                args.api_base,
                "POST",
                f"/repos/{operation['repo_id']}/rename/operations/{operation_id}/rollback-preflight",
                {
                    "expected_phase": args.expected_phase or operation["phase"],
                    "confirmation": args.confirm,
                },
            )
            operation = preflight["operation"]
            if not preflight.get("safe_to_rollback"):
                raise RenameCLIError(
                    "Repository rename rollback is unsafe",
                    code="rollback_unsafe",
                    details=preflight,
                )
        if not args.execute:
            return _operation_envelope("rollback", operation)
        rolled_back = _api_request(
            args.api_base,
            "POST",
            f"/repos/{operation['repo_id']}/rename/operations/{operation_id}/rollback",
            {"expected_phase": "rollback-preflight", "confirmation": args.confirm},
        )
        return _operation_envelope("rollback", rolled_back)

    _run(args, action)


def _common(parser: argparse.ArgumentParser) -> None:
    parser.add_argument(
        "--api-base", default=DEFAULT_API_BASE, help="State Hub API base URL"
    )
    parser.add_argument(
        "--json",
        action="store_true",
        dest="as_json",
        help="Emit the stable orchestration JSON contract",
    )


def configure_repo_commands(subparsers: argparse._SubParsersAction) -> None:
    repo = subparsers.add_parser("repo", help="Repository lifecycle commands")
    repo_sub = repo.add_subparsers(dest="repo_command", required=True)
    rename = repo_sub.add_parser(
        "rename", help="Governed repository rename lifecycle"
    )
    rename_sub = rename.add_subparsers(dest="rename_command", required=True)

    preflight = rename_sub.add_parser(
        "preflight",
        help="Inspect rename safety without mutating State Hub or Forgejo",
    )
    preflight.add_argument("old_slug")
    preflight.add_argument("new_slug")
    preflight.add_argument(
        "--operation-id",
        default=None,
        help="Client-owned UUID; generated when omitted",
    )
    preflight.add_argument(
        "--queued-edge-writes",
        default=None,
        help="Mode-0600 JSON list of queued-write evidence",
    )
    preflight.add_argument(
        "--output",
        default=None,
        help="Write the token-bearing preflight envelope to a new mode-0600 file",
    )
    _common(preflight)
    preflight.set_defaults(func=cmd_preflight)

    generate = rename_sub.add_parser(
        "generate-workplan",
        help="Generate a repository-native rename adoption workplan",
    )
    generate.add_argument("old_slug")
    generate.add_argument("new_slug")
    generate.add_argument(
        "--preflight-file",
        default=None,
        help="Use a private mode-0600 preflight envelope instead of refreshing",
    )
    generate.add_argument(
        "--repo-path",
        default=None,
        help="Visible target checkout used for workplan convention discovery",
    )
    generate.add_argument(
        "--output",
        default=None,
        help="Exclusive output path; defaults to the target workplans directory",
    )
    generate.add_argument("--owner", default="codex")
    _common(generate)
    generate.set_defaults(func=cmd_generate_workplan)

    start = rename_sub.add_parser(
        "start",
        help="Create an idempotent operation journal from a private preflight file",
    )
    start.add_argument("old_slug")
    start.add_argument("new_slug")
    start.add_argument("--operation-id", required=True)
    start.add_argument(
        "--preflight-file",
        required=True,
        help="Mode-0600 preflight file, or - for stdin",
    )
    start.add_argument("--actor", required=True)
    start.add_argument("--confirm", required=True, metavar="CONFIRMATION")
    _common(start)
    start.set_defaults(func=cmd_start)

    apply = rename_sub.add_parser(
        "apply", help="Apply exactly one compare-and-set lifecycle phase"
    )
    apply.add_argument("operation_id")
    apply.add_argument("--phase", required=True, choices=FORWARD_PHASES)
    apply.add_argument("--expected-phase", default=None)
    apply.add_argument("--confirm", required=True, metavar="CONFIRMATION")
    apply.add_argument(
        "--evidence-file",
        default=None,
        help="Mode-0600 JSON object, or - for stdin",
    )
    apply.add_argument(
        "--checks-file", default=None, help="Mode-0600 JSON boolean map"
    )
    _common(apply)
    apply.set_defaults(func=cmd_apply)

    status_parser = rename_sub.add_parser(
        "status", help="Read the durable operation journal"
    )
    status_parser.add_argument("operation_id")
    _common(status_parser)
    status_parser.set_defaults(func=cmd_status)

    verify = rename_sub.add_parser(
        "verify", help="Verify Forge identity and State Hub continuity"
    )
    verify.add_argument("operation_id")
    _common(verify)
    verify.set_defaults(func=cmd_verify)

    rollback = rename_sub.add_parser(
        "rollback",
        help="Preflight rollback; --execute is required to perform it",
    )
    rollback.add_argument("operation_id")
    rollback.add_argument("--expected-phase", default=None)
    rollback.add_argument("--confirm", required=True, metavar="CONFIRMATION")
    rollback.add_argument(
        "--execute",
        action="store_true",
        help="Execute after rollback preflight succeeds",
    )
    _common(rollback)
    rollback.set_defaults(func=cmd_rollback)


__all__ = [
    "CLI_SCHEMA_VERSION",
    "RenameCLIError",
    "configure_repo_commands",
]
