#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""scripts/pre_push_terminal_guard.py — Pre-push Terminal Artifact Guard (task-2779_a).

pre-push 시점에 terminal artifact 존재 여부를 확인하고 warn/envelope mode로 동작하는 가드.

역할:
  - terminal artifact(report/.done/envelope) 부재 시 canonical manifest에
    PUSH_ATTEMPT_WITHOUT_TERMINAL_MARKER 마커를 기록(surface)한다.
  - warn mode: 항상 exit code 0 반환 — push를 절대 차단하지 않는다.
  - 기존 build_manifest/surface_manifest 를 재사용한다.

★ 절대 금지 사항:
  - 실제 .git/hooks/pre-push 설치 0.
  - 실제 git push 실행 0.
  - subprocess로 git/push 호출 0.
  - callback fire/delivery 0.
  - cron/systemd 조작 0.
  - network 호출 0.
  - cokacdir 호출 0.
  - raw key/secret/ANU key literal 하드코딩 절대 0.
  - strict/blocking mode 구현 0.
  - Stop hook enforcer 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_PUSH_ATTEMPT_WITHOUT_TERMINAL,
)

# 가드 내부 오류 시 fail-open 마커
_GUARD_INTERNAL_ERROR_STATUS = "GUARD_INTERNAL_ERROR_FAIL_OPEN"


def run_guard(
    task_id,
    *,
    canonical_root,
    worktree_root,
    branch=None,
    head=None,
    report_rel=None,
    done_rel=None,
    envelope_rel=None,
    mode="warn",
) -> dict:
    """pre-push terminal artifact guard 실행.

    warn/envelope mode: push를 절대 차단하지 않는다. 항상 exit_code=0 반환.

    동작:
      1. build_manifest 로 artifact 존재 여부 확인.
      2. artifact 부재(STATUS_TERMINAL_CALLBACK_MISSING) 시 push_attempt=True로 표시하고
         PUSH_ATTEMPT_WITHOUT_TERMINAL_MARKER 마커를 manifest에 실어 surface.
      3. artifact 존재 시 그대로 surface.
      4. 어떤 예외도 밖으로 던지지 않는다(fail-open).

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

    Parameters
    ----------
    task_id:
        대상 task ID.
    canonical_root:
        canonical workspace 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:
        가드 모드. 현재 "warn" 고정 (blocking 미구현).
    """
    try:
        # ── 1. artifact 존재 확인 manifest 빌드 ─────────────────────────────
        # 외부 path input은 build_manifest 내부 _safe_artifact_path를 통과하므로
        # traversal은 자동 거부됨.
        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,
        )

        # ── 2. artifact 부재 판정 ────────────────────────────────────────────
        push_attempt = manifest.status == STATUS_TERMINAL_CALLBACK_MISSING

        if push_attempt:
            # artifact 부재 시 PUSH_ATTEMPT_WITHOUT_TERMINAL_MARKER 마커를 manifest에 실어 surface.
            manifest.status = STATUS_PUSH_ATTEMPT_WITHOUT_TERMINAL
            if STATUS_PUSH_ATTEMPT_WITHOUT_TERMINAL not in manifest.statuses:
                manifest.statuses.append(STATUS_PUSH_ATTEMPT_WITHOUT_TERMINAL)

        # ── 3/4. canonical manifest로 surface (실패해도 차단 안 함) ─────────
        # surface 실패(빈 문자열)여도 exit_code=0 유지(fail-open).
        surfaced = surface_manifest(manifest, canonical_root=canonical_root)

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

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


def _build_parser() -> argparse.ArgumentParser:
    """argparse 파서 생성."""
    p = argparse.ArgumentParser(
        description="Pre-push Terminal Artifact Guard CLI (task-2779_a) — warn mode, push 차단 없음.",
        formatter_class=argparse.RawDescriptionHelpFormatter,
    )
    p.add_argument("--task-id", required=True, help="대상 task ID (예: task-2779_a).")
    p.add_argument("--worktree", required=True, help="worktree root 경로 (절대경로 권장).")
    p.add_argument(
        "--canonical-root",
        default="/home/jay/workspace",
        help="canonical root 경로 (기본: /home/jay/workspace).",
    )
    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:
    """사람이 읽을 수 있는 가드 결과 요약 문자열 반환."""
    lines = [
        f"[pre-push-guard] task_id           : {result['task_id']}",
        f"[pre-push-guard] mode              : {result['mode']}",
        f"[pre-push-guard] status            : {result['status']}",
        f"[pre-push-guard] statuses          : {result['statuses']!r}",
        f"[pre-push-guard] push_attempt      : {result['push_attempt']}",
        f"[pre-push-guard] blocked           : {result['blocked']}",
        f"[pre-push-guard] exit_code         : {result['exit_code']}",
        f"[pre-push-guard] failed_open       : {result['failed_open']}",
    ]
    if result["surfaced_index_path"]:
        lines.append(f"[pre-push-guard] surfaced_to       : {result['surfaced_index_path']}")
    else:
        lines.append("[pre-push-guard] surfaced_to       : (surface 실패 또는 경로 없음)")
    return "\n".join(lines)


def main(argv=None) -> int:
    """CLI 메인 진입점.

    warn mode: 어떤 경우에도 0 반환 — push를 절대 차단하지 않는다.
    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_guard(
            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="warn",
        )

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

        # warn mode: 항상 result["exit_code"](=0) 반환
        return result["exit_code"]

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


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