#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""scripts/stop_hook_terminal_enforcer.py — Stop Hook Terminal Enforcer (task-2779_b) dry-run.

Stop hook 시점에 terminal artifact 존재 여부를 확인하고 결과를 manifest로 기록하는 dry-run 전용 enforcer.

역할:
  - terminal artifact(report/.done/envelope) 부재 시 manifest를 STOP_HOOK_ENFORCED_TERMINAL_RESULT로
    승격하여 기록(격리 canonical root에만 surface)한다.
  - dry-run 전용: 어떤 경우에도 종료를 차단하지 않는다. 모든 경로에서 exit_code=0.
  - 기존 build_manifest/surface_manifest를 재사용한다.

★ 절대 금지 사항:
  - 실제 Stop hook 설치 0.
  - 실제 종료 차단 0.
  - strict/blocking mode 0.
  - callback fire/delivery 0.
  - subprocess import 0.
  - os.system 0.
  - network 호출 0.
  - cron/systemd 조작 0.
  - raw key/secret/ANU key literal 하드코딩 절대 0.
  - 운영 canonical root에 marker/manifest 생성 0 (코드로 차단).
"""
from __future__ import annotations

import argparse
import json
import sys
from pathlib import Path

# ── import 부트스트랩: scripts/ 에 있으므로 repo root = parents[1] ───────────
_REPO_ROOT = Path(__file__).resolve().parents[1]
if str(_REPO_ROOT) not in sys.path:
    sys.path.insert(0, str(_REPO_ROOT))

from dispatch.terminal_artifact_index import (  # noqa: E402
    build_manifest,
    surface_manifest,
    STATUS_TERMINAL_CALLBACK_MISSING,
)

# ── 모듈 레벨 상수 ────────────────────────────────────────────────────────────
STATUS_STOP_HOOK_ENFORCED_TERMINAL_RESULT = "STOP_HOOK_ENFORCED_TERMINAL_RESULT"
_ENFORCER_INTERNAL_ERROR_STATUS = "ENFORCER_INTERNAL_ERROR_FAIL_OPEN"

# dry-run 안전장치: 실제 운영 canonical에 marker 생성을 코드로 차단
_PRODUCTION_CANONICAL_ROOT = "/home/jay/workspace"
STATUS_DRYRUN_PRODUCTION_CANONICAL_REFUSED = "DRYRUN_PRODUCTION_CANONICAL_REFUSED"


def _is_production_canonical(canonical_root: str) -> bool:
    """canonical_root가 운영 root(_PRODUCTION_CANONICAL_ROOT)로 resolve되는지 검사.

    Path(...).resolve(strict=False) 비교로 symlink/상대경로 우회를 방지한다.
    resolve 실패 시 보수적으로 True(운영으로 간주하여 거부)를 반환한다.
    """
    try:
        given = Path(canonical_root).resolve(strict=False)
        prod = Path(_PRODUCTION_CANONICAL_ROOT).resolve(strict=False)
        return given == prod or prod in given.parents
    except (OSError, ValueError, RuntimeError):
        # resolve 실패 시 보수적으로 운영으로 간주 → 거부
        return True


def run_enforcer(
    task_id: str,
    *,
    canonical_root: str,
    worktree_root: str,
    branch: str | None = None,
    head: str | None = None,
    report_rel: str | None = None,
    done_rel: str | None = None,
    envelope_rel: str | None = None,
    mode: str = "dryrun",
) -> dict:
    """Stop hook terminal enforcer 실행 (dry-run 전용).

    dry-run 전용: 어떤 경우에도 종료를 차단하지 않는다. 모든 경로에서 exit_code=0.

    동작:
      1. _is_production_canonical로 canonical_root가 운영 root인지 검사.
      2. build_manifest로 artifact 존재 여부 확인.
      3. artifact 부재(STATUS_TERMINAL_CALLBACK_MISSING) 시 manifest를
         STATUS_STOP_HOOK_ENFORCED_TERMINAL_RESULT로 승격.
      4. 운영 canonical이면 surface하지 않고 refused_production=True.
         격리 canonical이면 surface_manifest로 격리 root에만 기록.
      5. 어떤 예외도 밖으로 던지지 않는다(fail-open).

    반환 dict 키:
      task_id, mode, status, statuses, terminal_missing, blocked(항상 False),
      exit_code(항상 0), surfaced_index_path, refused_production, failed_open.

    Parameters
    ----------
    task_id:
        대상 task ID.
    canonical_root:
        격리/테스트용 canonical root 경로 (운영 root 거부).
    worktree_root:
        worktree root 경로.
    branch:
        git branch 이름 (선택).
    head:
        git HEAD SHA (선택).
    report_rel:
        report 파일 상대경로 (선택, None이면 기본값 사용).
    done_rel:
        .done 파일 상대경로 (선택, None이면 기본값 사용).
    envelope_rel:
        callback envelope 상대경로 (선택, None이면 기본값 사용).
    mode:
        enforcer 모드. dry-run 고정 (blocking 미구현).
    """
    try:
        # ── 1. 운영 canonical root 검사 ──────────────────────────────────────
        is_prod = _is_production_canonical(canonical_root)

        # ── 2. artifact 존재 확인 manifest 빌드 ─────────────────────────────
        # 외부 path input은 build_manifest 내부 _safe_artifact_path를 통과하므로
        # traversal은 자동 거부됨. 별도 path 처리 추가 금지.
        manifest = build_manifest(
            task_id,
            canonical_root=canonical_root,
            worktree_root=worktree_root,
            branch=branch,
            head=head,
            report_rel=report_rel,
            done_rel=done_rel,
            envelope_rel=envelope_rel,
        )

        # ── 3. artifact 부재 판정 ─────────────────────────────────────────────
        terminal_missing = manifest.status == STATUS_TERMINAL_CALLBACK_MISSING

        if terminal_missing:
            # artifact 부재 시 STOP_HOOK_ENFORCED_TERMINAL_RESULT로 승격
            manifest.status = STATUS_STOP_HOOK_ENFORCED_TERMINAL_RESULT
            if STATUS_STOP_HOOK_ENFORCED_TERMINAL_RESULT not in manifest.statuses:
                manifest.statuses.append(STATUS_STOP_HOOK_ENFORCED_TERMINAL_RESULT)
            if STATUS_TERMINAL_CALLBACK_MISSING not in manifest.statuses:
                manifest.statuses.append(STATUS_TERMINAL_CALLBACK_MISSING)

        # ── 4. surface 처리 (운영 canonical 거부 안전장치) ───────────────────
        if is_prod:
            # 운영 canonical: surface하지 않고 거부 마커만 추가
            surfaced = ""
            if STATUS_DRYRUN_PRODUCTION_CANONICAL_REFUSED not in manifest.statuses:
                manifest.statuses.append(STATUS_DRYRUN_PRODUCTION_CANONICAL_REFUSED)
            refused_production = True
        else:
            # 격리 canonical: surface_manifest로 격리 root에만 기록
            surfaced = surface_manifest(manifest, canonical_root=canonical_root)
            refused_production = False

        # ── 5. 결과 dict 반환 ────────────────────────────────────────────────
        return {
            "task_id": task_id,
            "mode": mode,
            "status": manifest.status,
            "statuses": list(manifest.statuses),
            "terminal_missing": terminal_missing,
            "blocked": False,               # dry-run: 항상 False
            "exit_code": 0,                 # 항상 0
            "surfaced_index_path": surfaced,
            "refused_production": refused_production,
            "failed_open": False,
        }

    except Exception:  # noqa: BLE001 — fail-open: 어떤 예외도 밖으로 던지지 않는다
        # crash 금지, traceback 노출 금지
        return {
            "task_id": task_id,
            "mode": mode,
            "status": _ENFORCER_INTERNAL_ERROR_STATUS,
            "statuses": [_ENFORCER_INTERNAL_ERROR_STATUS],
            "terminal_missing": False,
            "blocked": False,
            "exit_code": 0,
            "surfaced_index_path": "",
            "refused_production": False,
            "failed_open": True,
        }


def _build_parser() -> argparse.ArgumentParser:
    """argparse 파서 생성.

    --canonical-root는 required=True (기본값 없음) — 실수로 운영 canonical default 사용 방지.
    """
    p = argparse.ArgumentParser(
        description=(
            "Stop Hook Terminal Enforcer CLI (task-2779_b) — dry-run 전용, 종료 차단 없음."
        ),
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    p.add_argument("--task-id", required=True, help="대상 task ID (예: task-2779_b).")
    p.add_argument("--worktree", required=True, help="worktree root 경로 (절대경로 권장).")
    p.add_argument(
        "--canonical-root",
        required=True,
        help="격리/테스트 canonical root 경로 필수 — 운영 root 지정 시 surface 거부됨.",
    )
    p.add_argument("--branch", default=None, help="git branch 이름 (선택).")
    p.add_argument("--head", default=None, help="git HEAD SHA (선택).")
    p.add_argument(
        "--report-rel",
        default=None,
        dest="report_rel",
        help="report 파일 상대경로 (선택).",
    )
    p.add_argument(
        "--done-rel",
        default=None,
        dest="done_rel",
        help=".done 파일 상대경로 (선택).",
    )
    p.add_argument(
        "--envelope-rel",
        default=None,
        dest="envelope_rel",
        help="callback envelope 상대경로 (선택).",
    )
    p.add_argument(
        "--json",
        action="store_true",
        dest="output_json",
        help="결과를 JSON으로 출력.",
    )
    return p


def _human_summary(result: dict) -> str:
    """사람이 읽을 수 있는 enforcer 결과 요약 문자열 반환."""
    lines = [
        f"[stop-hook-enforcer] task_id           : {result['task_id']}",
        f"[stop-hook-enforcer] mode              : {result['mode']}",
        f"[stop-hook-enforcer] status            : {result['status']}",
        f"[stop-hook-enforcer] statuses          : {result['statuses']!r}",
        f"[stop-hook-enforcer] terminal_missing  : {result['terminal_missing']}",
        f"[stop-hook-enforcer] blocked           : {result['blocked']}",
        f"[stop-hook-enforcer] exit_code         : {result['exit_code']}",
        f"[stop-hook-enforcer] refused_production: {result['refused_production']}",
        f"[stop-hook-enforcer] failed_open       : {result['failed_open']}",
    ]
    if result["surfaced_index_path"]:
        lines.append(
            f"[stop-hook-enforcer] surfaced_to       : {result['surfaced_index_path']}"
        )
    else:
        lines.append(
            "[stop-hook-enforcer] surfaced_to       : (surface 실패, 운영 거부, 또는 경로 없음)"
        )
    return "\n".join(lines)


def main(argv: list[str] | None = None) -> int:
    """CLI 메인 진입점.

    dry-run 전용: 어떤 경우에도 0 반환 — 종료를 절대 차단하지 않는다.
    argparse SystemExit는 그대로 코드 반환, 그 외에는 모든 경로에서 0 반환.
    """
    parser = _build_parser()
    try:
        args = parser.parse_args(argv)
    except SystemExit as exc:
        # argparse 자체 exit (--help 또는 필수 인자 누락) — 코드 그대로 반환
        return int(exc.code) if exc.code is not None else 1

    try:
        result = run_enforcer(
            args.task_id,
            canonical_root=args.canonical_root,
            worktree_root=args.worktree,
            branch=args.branch,
            head=args.head,
            report_rel=args.report_rel,
            done_rel=args.done_rel,
            envelope_rel=args.envelope_rel,
            mode="dryrun",
        )

        if args.output_json:
            print(json.dumps(result, ensure_ascii=False, indent=2))
        else:
            print(_human_summary(result))

        # dry-run: 항상 result["exit_code"](=0) 반환
        return result["exit_code"]

    except Exception:  # noqa: BLE001 — fail-open: dry-run은 어떤 경우에도 0 반환
        # crash 금지, traceback 노출 금지
        return 0


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