# -*- coding: utf-8 -*-
"""dispatch.anu_terminal_relay — task-2730 allowlist-only terminal relay (static).

``agent_relay.required`` 예외 경로(green deterministic closeout 가 아닌 경우)에서만
사용하는 **2-tier terminal relay** surface. 보고(send_report) + ANU relay(relay_to_anu)
**두 함수만** 노출하며, 판단/처리 주경로(merge/PR/dispatch)로는 진입할 수 없다.

static enforcement (Codex #9 / corrected_spec_v2 §7):
  (a) subprocess ``shell=False`` + **고정 argv list** (문자열 passthrough 0).
  (b) 이 모듈은 git / gh / dispatch / merge 모듈을 **import 0** (정적 검사·테스트 보장).
  (c) argv[0] = cokacdir 바이너리 고정 + subcommand 화이트리스트(``--sendfile`` /
      ``--cron`` 만). 그 외 → ValueError raise.
  (d) merge / PR / dispatch 바이너리·subcommand 정적 거부(화이트리스트 외 전부 거부).

★ 이 모듈은 ANU key literal 을 **하드코딩하지 않는다**. relay_to_anu 의 ``--key``
  값은 호출자(권한 있는 ANU 세션)가 sealed key 로 런타임 주입한다. 실 발사는
  주입된 ``runner`` 가 수행 — 기본 runner 는 ``subprocess.run(shell=False)``.
"""
from __future__ import annotations

import subprocess
from typing import Callable, List, Optional

# ── 고정 상수 (argv[0]·chat·subcommand 화이트리스트) ──────────────────────────
COKACDIR_BINARY = "/usr/local/bin/cokacdir"
ANU_CHAT_ID = "6937032012"
DEFAULT_RELAY_AT = "30s"

# 허용 subcommand (화이트리스트). 그 외 subcommand → ValueError.
ALLOWED_SUBCOMMANDS = ("--sendfile", "--cron")

# 정적 거부 토큰: argv[0](바이너리)/subcommand 위치에 merge/PR/dispatch/shell 류가
# 끼어드는 것을 방어한다. (prompt 본문이 아니라 바이너리·subcommand 위치만 검사 —
# false positive 0.)
FORBIDDEN_BINARY_TOKENS = ("gh", "git", "merge", "dispatch", "sh", "bash", "python")


# ── 기본 runner (shell=False 고정) ────────────────────────────────────────────
def _default_runner(argv: List[str]):
    """고정 argv 를 ``shell=False`` 로 실행. capture_output·check=False (fail-safe)."""
    return subprocess.run(  # noqa: S603 — shell=False·고정 argv list·화이트리스트 검증 후
        argv,
        shell=False,
        check=False,
        capture_output=True,
        text=True,
    )


# ── 화이트리스트 검증 + 실행 ──────────────────────────────────────────────────
def _run_fixed_argv(argv: List[str], *, runner: Optional[Callable] = None):
    """고정 argv 를 화이트리스트 검증 후 runner 로 실행.

    검증 (fail-closed):
      - argv[0] == COKACDIR_BINARY (cokacdir 바이너리 고정 — gh/git/merge/dispatch 거부).
      - argv[1] in ALLOWED_SUBCOMMANDS (``--sendfile`` / ``--cron`` 만).
      - argv[0]·argv[1] 위치에 FORBIDDEN_BINARY_TOKENS 정확 일치 시 거부.
    위반 시 ValueError raise — relay 가 주경로(merge/PR/dispatch)로 진입 불가.
    """
    if not isinstance(argv, list) or len(argv) < 2:
        raise ValueError("terminal_relay: argv 는 [binary, subcommand, ...] list 여야 함.")
    if argv[0] != COKACDIR_BINARY:
        raise ValueError(
            f"terminal_relay: argv[0] 는 cokacdir 바이너리 고정이어야 함 (got {argv[0]!r}) "
            "— gh/git/merge/dispatch 바이너리 거부(static)."
        )
    if argv[1] not in ALLOWED_SUBCOMMANDS:
        raise ValueError(
            f"terminal_relay: subcommand {argv[1]!r} 는 화이트리스트 밖 "
            f"(허용: {ALLOWED_SUBCOMMANDS}) — merge/PR/dispatch subcommand 거부(static)."
        )
    # argv[0]/argv[1] 위치 토큰 정확 일치 방어(defense-in-depth).
    for pos in (0, 1):
        token = str(argv[pos]).strip().lower().lstrip("-")
        if token in FORBIDDEN_BINARY_TOKENS:
            raise ValueError(
                f"terminal_relay: forbidden binary/subcommand token {argv[pos]!r} "
                f"at position {pos} — static reject."
            )
    runner = runner or _default_runner
    return runner(argv)


# ── argv 빌더 (고정 argv list) ────────────────────────────────────────────────
def build_send_report_argv(file_path: str, chat_id: str = ANU_CHAT_ID) -> List[str]:
    """``cokacdir --sendfile <file> --chat <id>`` 고정 argv."""
    return [COKACDIR_BINARY, "--sendfile", str(file_path), "--chat", str(chat_id)]


def build_relay_argv(
    prompt: str,
    *,
    anu_key: str,
    chat_id: str = ANU_CHAT_ID,
    at: str = DEFAULT_RELAY_AT,
) -> List[str]:
    """``cokacdir --cron <prompt> --at <at> --chat <id> --key <ANU>`` 고정 argv.

    ``anu_key`` 는 런타임 주입값(sealed key) — 모듈 literal 0.
    """
    return [
        COKACDIR_BINARY,
        "--cron",
        str(prompt),
        "--at",
        str(at),
        "--chat",
        str(chat_id),
        "--key",
        str(anu_key),
    ]


# ── tier 1: 보고(send_report) ─────────────────────────────────────────────────
def send_report(
    file_path: str,
    *,
    chat_id: str = ANU_CHAT_ID,
    runner: Optional[Callable] = None,
):
    """report 파일을 chat 으로 전송(``cokacdir --sendfile`` 고정 argv).

    화이트리스트 검증 후 runner(기본 subprocess.run shell=False)로 실행. 실 발사는
    권한 있는 ANU 세션이 수행 — 테스트는 fake runner 주입으로 argv 만 검증한다.
    """
    argv = build_send_report_argv(file_path, chat_id)
    return _run_fixed_argv(argv, runner=runner)


# ── tier 2: ANU relay(relay_to_anu) ───────────────────────────────────────────
def relay_to_anu(
    prompt: str,
    *,
    anu_key: str,
    chat_id: str = ANU_CHAT_ID,
    at: str = DEFAULT_RELAY_AT,
    runner: Optional[Callable] = None,
):
    """terminal relay prompt 를 ANU 로 relay(``cokacdir --cron`` 고정 argv, 예외 경로만).

    ``anu_key`` 는 sealed key 런타임 주입값(모듈 literal 0). 화이트리스트 검증 후
    runner 로 실행. 실 발사는 권한 있는 ANU 세션이 수행한다(self-key 자가발사 0).
    """
    argv = build_relay_argv(prompt, anu_key=anu_key, chat_id=chat_id, at=at)
    return _run_fixed_argv(argv, runner=runner)


__all__ = [
    "COKACDIR_BINARY",
    "ANU_CHAT_ID",
    "DEFAULT_RELAY_AT",
    "ALLOWED_SUBCOMMANDS",
    "FORBIDDEN_BINARY_TOKENS",
    "build_send_report_argv",
    "build_relay_argv",
    "send_report",
    "relay_to_anu",
]
