#!/usr/bin/env bash
# task-2457 Phase 2-A: pre-push guard hook (★ 완전 재작성, 기존 task-2449 내용 폐기)
# 설치: scripts/install-git-hooks.sh
#
# stdin 입력: <local_ref> <local_sha> <remote_ref> <remote_sha>
#
# 검증 순서:
#   1) TASKCTL_BYPASS=1 분기 (pre-commit과 동일 패턴)
#   2) refspec 검사 — refs/heads/main push 차단
#   3) branch == main 차단
#   4) branch에서 task-id 추출
#   5) cancelled marker 차단
#   6) lock 파일 존재 + branch/lock task-id 일치
#   7) scope_check (allowed_resources.paths 기반, capability snapshot이 있을 때만)
#   8) taskctl_verify fallback (스크립트 부재 시 fallback evidence 기록)
#   9) best-effort taskctl status — CANCELLED 차단
set -euo pipefail

WORKSPACE="$(git rev-parse --show-toplevel)"
export WORKSPACE
cd "$WORKSPACE"

# ===== 공통 유틸 =====
extract_task_id() {
    # 정규식: ^task/(task-N(.M)?)-
    echo "$1" | sed -nE 's|^task/(task-[0-9]+(\.[0-9]+)?)-.*|\1|p'
}

write_atomic_evidence() {
    local file="$1"
    local content="$2"
    local tmp="${file}.tmp.$$"
    mkdir -p "$(dirname "$file")"
    printf '%s' "$content" > "$tmp"
    mv "$tmp" "$file"
}

# ---------- 검증 1: TASKCTL_BYPASS 분기 ----------
if [[ "${TASKCTL_BYPASS:-0}" == "1" ]]; then
    REASON="${TASKCTL_BYPASS_REASON:-}"
    if [[ -z "$REASON" ]]; then
        echo "[BLOCKED] TASKCTL_BYPASS reason missing (set TASKCTL_BYPASS_REASON=...)" >&2
        exit 1
    fi
    CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
    BYPASS_TASK_ID=$(extract_task_id "$CURRENT_BRANCH")
    [[ -z "$BYPASS_TASK_ID" ]] && BYPASS_TASK_ID="unknown"
    TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
    ACTOR="${USER:-unknown}"
    EVIDENCE_FILE="$WORKSPACE/.tasks/evidence/${BYPASS_TASK_ID}/bypass-${TIMESTAMP}.json"
    write_atomic_evidence "$EVIDENCE_FILE" "{
  \"bypass\": true,
  \"timestamp\": \"${TIMESTAMP}\",
  \"actor\": \"${ACTOR}\",
  \"reason\": \"${REASON}\"
}
"
    echo "[BYPASS] pre-push guard skipped (evidence=$EVIDENCE_FILE)" >&2
    exit 0
fi

# ---------- 검증 2: refspec 검사 (stdin) ----------
# stdin이 비어 있으면 push hook 호출이 아닐 수 있음 (직접 실행 등) — 그대로 진행
STDIN_LINES=""
if [[ ! -t 0 ]]; then
    STDIN_LINES=$(cat || true)
fi
if [[ -n "$STDIN_LINES" ]]; then
    while IFS=' ' read -r _LOCAL_REF _LOCAL_SHA REMOTE_REF _REMOTE_SHA; do
        [[ -z "${REMOTE_REF:-}" ]] && continue
        if [[ "$REMOTE_REF" == "refs/heads/main" ]]; then
            echo "[BLOCKED] main direct push prohibited (refspec=$REMOTE_REF)" >&2
            exit 1
        fi
    done <<< "$STDIN_LINES"
fi

# ---------- 검증 3: branch == main 차단 ----------
CURRENT_BRANCH=$(git rev-parse --abbrev-ref HEAD)
if [[ "$CURRENT_BRANCH" == "main" ]]; then
    echo "[BLOCKED] main direct push prohibited" >&2
    exit 1
fi

# ---------- 검증 4: branch에서 task-id 추출 ----------
BRANCH_TASK_ID=$(extract_task_id "$CURRENT_BRANCH")
if [[ -z "$BRANCH_TASK_ID" ]]; then
    echo "[BLOCKED] branch does not match task pattern: task/task-N-bot (got: $CURRENT_BRANCH)" >&2
    exit 1
fi

# ---------- 검증 5: cancelled marker ----------
CANCELLED_MARKER="$WORKSPACE/memory/events/${BRANCH_TASK_ID}.cancelled"
if [[ -f "$CANCELLED_MARKER" ]]; then
    echo "[BLOCKED] task ${BRANCH_TASK_ID} cancelled (marker=$CANCELLED_MARKER)" >&2
    exit 1
fi

# ---------- 검증 6: lock 파일 + 일치 ----------
LOCK_FILE="$WORKSPACE/.tasks/locks/${BRANCH_TASK_ID}.lock"
if [[ ! -f "$LOCK_FILE" ]]; then
    echo "[BLOCKED] start_task_guard not passed: .tasks/locks/${BRANCH_TASK_ID}.lock missing" >&2
    exit 1
fi
LOCK_TASK_ID=$(python3 -c "import json,sys;print(json.load(open(sys.argv[1])).get('task_id',''))" "$LOCK_FILE" 2>/dev/null || true)
if [[ -z "$LOCK_TASK_ID" ]]; then
    echo "[BLOCKED] lock file unparseable: $LOCK_FILE" >&2
    exit 1
fi
if [[ "$LOCK_TASK_ID" != "$BRANCH_TASK_ID" ]]; then
    echo "[BLOCKED] branch/lock task-id mismatch (branch=$BRANCH_TASK_ID, lock=$LOCK_TASK_ID)" >&2
    exit 1
fi

# ---------- 검증 7: scope_check ----------
SCOPE_STATUS="SKIPPED"
CAPABILITY_FILE="$WORKSPACE/memory/capabilities/${BRANCH_TASK_ID}.json"
if [[ -f "$CAPABILITY_FILE" ]]; then
    # origin/main 기준 변경 파일 (없으면 graceful PASS)
    CHANGED_FILES=$(git diff --name-only origin/main..HEAD 2>/dev/null || true)
    if [[ -n "$CHANGED_FILES" ]]; then
        SCOPE_RESULT=$(CAPABILITY_FILE="$CAPABILITY_FILE" CHANGED_FILES="$CHANGED_FILES" python3 - <<'PYEOF'
import json, os, fnmatch, sys
cap_path = os.environ["CAPABILITY_FILE"]
files = [f for f in os.environ["CHANGED_FILES"].splitlines() if f.strip()]
try:
    cap = json.load(open(cap_path))
except Exception as e:
    print("ERROR:" + str(e))
    sys.exit(0)
allowed = cap.get("allowed_resources", {}).get("paths", []) or []
if not allowed:
    print("PASS")
    sys.exit(0)
violations = []
for f in files:
    matched = False
    for pat in allowed:
        # ** glob → fnmatch는 ** 미지원이므로 *로 치환
        norm = pat.replace("**", "*")
        if fnmatch.fnmatch(f, pat) or fnmatch.fnmatch(f, norm) or f.startswith(pat.rstrip("*").rstrip("/") + "/"):
            matched = True
            break
    if not matched:
        violations.append(f)
if violations:
    print("FAIL:" + ",".join(violations))
else:
    print("PASS")
PYEOF
)
        if [[ "$SCOPE_RESULT" == FAIL:* ]]; then
            VIOL="${SCOPE_RESULT#FAIL:}"
            echo "[BLOCKED] scope violation: $VIOL" >&2
            exit 1
        fi
        SCOPE_STATUS="PASS"
    else
        SCOPE_STATUS="PASS"
    fi
fi

# ---------- 검증 8: taskctl_verify (task-2461 Phase 3 P2-1: strict 강제) ----------
TASKCTL_VERIFY="$WORKSPACE/scripts/taskctl_verify.py"
if [[ -f "$TASKCTL_VERIFY" ]]; then
    if ! python3 "$TASKCTL_VERIFY" "$BRANCH_TASK_ID"; then
        echo "[BLOCKED] taskctl_verify FAIL (task=$BRANCH_TASK_ID) — task-2461 P2-1 hard gate" >&2
        exit 1
    fi
else
    # task-2461 Phase 3: taskctl_verify.py 부재는 Phase 2-C rollback 신호 — fail-strict
    echo "[BLOCKED] scripts/taskctl_verify.py 부재 — Phase 2-C 롤백 가능성, push 차단 (task-2461 P2-1)" >&2
    exit 1
fi

# ---------- 검증 9: best-effort taskctl status ----------
TASKCTL="$WORKSPACE/scripts/taskctl.py"
if [[ -f "$TASKCTL" ]]; then
    STATE_OUT=$(python3 "$TASKCTL" status "$BRANCH_TASK_ID" --machine 2>/dev/null || true)
    if [[ -n "$STATE_OUT" ]]; then
        CURRENT_STATE=$(printf '%s' "$STATE_OUT" | python3 -c "
import json, sys
try:
    d = json.loads(sys.stdin.read())
    print(d.get('current_state') or '')
except Exception:
    pass
" 2>/dev/null || true)
        if [[ "$CURRENT_STATE" == "CANCELLED" ]]; then
            echo "[BLOCKED] task ${BRANCH_TASK_ID} state=CANCELLED (taskctl)" >&2
            exit 1
        fi
    fi
fi

echo "[OK] pre-push guard PASS (task-id=$BRANCH_TASK_ID, scope=$SCOPE_STATUS)" >&2
exit 0
