#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""hooks/user_prompt_submit_hook_callback_inbox.py — 이중 안전망 (STAGED · live 미적용).

task-2644 ANU_CALLBACK_COLLECTOR_CONTROL_PLANE (회장 verbatim 우선순위 7).
spec: memory/specs/system_anu_callback_collector_control_plane_spec_260524.md
spec sha256: b27da557d4245bce476cd63f4ab174aefc8a25d2da07ec2c8d2c83b01ee96153

★ 본 hook 은 staged template only · ~/.claude/settings.json 에 등록되지 않은 상태.
★ ANCHOR-7: "본 ANU 대화 세션 hook = 이중 안전망 · 주 경로 아님".
★ 주 경로 = cokacdir 가 spawn 하는 별도 collector 세션 (SessionStart hook).

목적:
    cokacdir 가 spawn 한 collector 세션이 동작하지 못한 경우 (envelope 누락 / spawn
    실패 / 시간 초과 등), 회장-facing ANU 본 세션에 회장이 user prompt 를 보내는
    시점에 inbox 의 .callback 파일을 감지하여 처리 의무를 prompt 에 주입한다.

Claude Code UserPromptSubmit hook 계약:
    - stdin: {"session_id":..., "prompt":..., "hook_event_name":"UserPromptSubmit"}
    - stdout: 단순 텍스트 출력 → additional context 로 prompt 앞에 주입됨
    - exit 0: continue · non-zero 2: block + stderr 로 회장에게 사유 표시
"""
from __future__ import annotations

import json
import os
import sys
import time
from pathlib import Path
from typing import Any, Dict, List, Optional


SCHEMA = "hooks.user_prompt_submit_hook_callback_inbox.v1"
INBOX_DIR_ENV = "ANU_CALLBACK_INBOX_DIR"
INBOX_DIR_DEFAULT = "memory/system/.callback_inbox"
WORKSPACE_ROOT_ENV = "ANU_WORKSPACE_ROOT"
WORKSPACE_ROOT_DEFAULT = "/home/jay/workspace"
STALE_THRESHOLD_SECONDS = 600  # 10분 이상 미처리 시 강력 경고


def _read_stdin() -> Dict[str, Any]:
    try:
        if sys.stdin.isatty():
            return {}
        raw = sys.stdin.read()
        if not raw.strip():
            return {}
        return json.loads(raw)
    except Exception:
        return {}


def _resolve_inbox() -> Path:
    explicit = os.environ.get(INBOX_DIR_ENV)
    if explicit:
        return Path(explicit)
    root = os.environ.get(WORKSPACE_ROOT_ENV, WORKSPACE_ROOT_DEFAULT)
    return Path(root) / INBOX_DIR_DEFAULT


def _scan_inbox(inbox: Path) -> List[Dict[str, Any]]:
    if not inbox.is_dir():
        return []
    items: List[Dict[str, Any]] = []
    now = time.time()
    for f in sorted(inbox.glob("*.callback")):
        try:
            mtime = f.stat().st_mtime
            payload = json.loads(f.read_text(encoding="utf-8"))
            items.append(
                {
                    "path": str(f),
                    "callback_id": payload.get("callback_id"),
                    "task_id": payload.get("task_id"),
                    "age_seconds": int(now - mtime),
                    "stale": (now - mtime) > STALE_THRESHOLD_SECONDS,
                }
            )
        except Exception:
            items.append(
                {
                    "path": str(f),
                    "callback_id": None,
                    "task_id": None,
                    "age_seconds": None,
                    "stale": True,
                    "parse_error": True,
                }
            )
    return items


def build_injection(items: List[Dict[str, Any]]) -> str:
    if not items:
        return ""
    lines = []
    lines.append("# ⚠ ANU_CALLBACK_INBOX — 미처리 .callback 감지 (이중 안전망)")
    lines.append("")
    lines.append(
        "주 경로 (cokacdir SessionStart hook spawned collector) 가 미동작 · "
        "회장 본 세션에서 처리 요청. ★ 단, source attribution = "
        "`CALLBACK_LEDGER_RECONCILED` (post-hoc) 명시 필수. "
        "`RECEIVED_INBOUND_THIS_SESSION` 표현 금지."
    )
    lines.append("")
    for it in items:
        warn = " ⚠STALE" if it.get("stale") else ""
        cb = it.get("callback_id") or "<missing>"
        tk = it.get("task_id") or "<missing>"
        age = it.get("age_seconds")
        lines.append(f"- callback_id=`{cb}` task=`{tk}` age={age}s{warn}")
        lines.append(f"  - path: `{it.get('path')}`")
    lines.append("")
    lines.append("## 의무 7 단계 (Stop hook 검증 대상)")
    lines.append(
        "envelope parse → context recovery → terminal_state classify → "
        "next_action decide → auto/chair/noop → ledger write → "
        "source attribution + exit"
    )
    return "\n".join(lines)


def run(stdin_payload: Optional[Dict[str, Any]] = None) -> Dict[str, Any]:
    _ = stdin_payload if stdin_payload is not None else _read_stdin()
    inbox = _resolve_inbox()
    items = _scan_inbox(inbox)
    injection = build_injection(items)
    return {
        "schema": SCHEMA,
        "inbox_path": str(inbox),
        "pending_count": len(items),
        "stale_count": sum(1 for it in items if it.get("stale")),
        "items": items,
        "injection": injection,
        "hookSpecificOutput": {
            "hookEventName": "UserPromptSubmit",
            "additionalContext": injection if injection else None,
        },
    }


def main() -> int:
    try:
        result = run()
    except Exception as exc:  # pragma: no cover — fail-open
        sys.stderr.write(f"[user_prompt_submit_hook_callback_inbox] error: {exc}\n")
        return 0
    # Claude Code 는 hookSpecificOutput.additionalContext 우선 처리
    sys.stdout.write(json.dumps(result, ensure_ascii=False))
    sys.stdout.flush()
    return 0


if __name__ == "__main__":
    raise SystemExit(main())
