"""task-2554+2 §5 신규 fixture #5: OWNER_GEMINI_TRIGGER_TOKEN env 부재 시 FAILED + audit.

회장 §명시 (2026-05-12): token_provider 가 빈 문자열을 반환하면 TokenBoundaryViolation
즉시 발생. http_post 0회. audit 에 PENDING 등 사전 기록도 발생하지 않음.

추가: env_override 에 BOT_GITHUB_TOKEN / GH_TOKEN / GITHUB_TOKEN / OWNER_PAT / PAT_TOKEN 등
forbidden token env 가 명시 전달되면 TokenBoundaryViolation. fallback 절대 X.
"""

from __future__ import annotations

import json
from pathlib import Path

import pytest

from anu_v2.owner_trigger_audit import OwnerTriggerAudit
from anu_v2.owner_trigger_only import (
    FORBIDDEN_TOKEN_ENV_NAMES,
    OwnerTriggerOnly,
    TOKEN_ENV_NAME,
    TokenBoundaryViolation,
)


_HEAD_A = "a" * 40


def _write_decision(tmp_path: Path) -> Path:
    decision = {
        "schema": "anu_v2.owner_trigger_decision.v1",
        "task_id": "task-2554+2-test",
        "pr": 105,
        "current_head": _HEAD_A,
        "queue_head": True,
        "current_head_confirmed": True,
        "gemini_evidence_fresh": False,
        "nudge_count_for_pr_head": 0,
        "allowed_action": "POST_GEMINI_REVIEW_TRIGGER_COMMENT",
        "comment_body": "/gemini review",
        "allowed": True,
    }
    p = tmp_path / "decision.json"
    p.write_text(json.dumps(decision), encoding="utf-8")
    return p


def _build_module(tmp_path: Path, *, token: str = "owner-token"):
    posts: list[dict] = []
    audit = OwnerTriggerAudit(tmp_path)

    def http_post(method, path, body, headers):
        posts.append({"method": method, "path": path, "body": dict(body)})
        return {"status": 201}

    mod = OwnerTriggerOnly(
        workspace_root=tmp_path,
        http_post=http_post,
        token_provider=lambda: token,
        audit=audit,
    )
    return mod, posts, audit


def test_empty_token_raises_token_boundary_violation(tmp_path):
    """token_provider 가 빈 문자열 → TokenBoundaryViolation 즉시 fail-closed."""
    decision_path = _write_decision(tmp_path)
    mod, posts, audit = _build_module(tmp_path, token="")
    with pytest.raises(TokenBoundaryViolation, match=TOKEN_ENV_NAME):
        mod.trigger_gemini_review(
            decision_path=decision_path,
            owner="o",
            repo="r",
            current_head_actual=_HEAD_A,
        )
    # http_post 호출 0
    assert posts == []


def test_non_string_token_raises_token_boundary_violation(tmp_path):
    """token_provider 가 None / int / dict 반환 시도 → TokenBoundaryViolation."""
    decision_path = _write_decision(tmp_path)
    posts: list[dict] = []
    audit = OwnerTriggerAudit(tmp_path)

    def http_post(method, path, body, headers):
        posts.append({"method": method})
        return {"status": 201}

    mod = OwnerTriggerOnly(
        workspace_root=tmp_path,
        http_post=http_post,
        token_provider=lambda: None,  # type: ignore[arg-type]
        audit=audit,
    )
    with pytest.raises(TokenBoundaryViolation):
        mod.trigger_gemini_review(
            decision_path=decision_path,
            owner="o",
            repo="r",
            current_head_actual=_HEAD_A,
        )
    assert posts == []


def test_forbidden_token_env_in_override_raises_boundary_violation(tmp_path):
    """env_override 에 BOT_GITHUB_TOKEN 등 forbidden token 이 명시되면 TokenBoundaryViolation."""
    decision_path = _write_decision(tmp_path)
    mod, posts, _ = _build_module(tmp_path)
    for forbidden in FORBIDDEN_TOKEN_ENV_NAMES:
        env_override = {forbidden: "do-not-use"}
        with pytest.raises(TokenBoundaryViolation, match=forbidden):
            mod.trigger_gemini_review(
                decision_path=decision_path,
                owner="o",
                repo="r",
                current_head_actual=_HEAD_A,
                env_override=env_override,
            )
    # http_post 한 번도 호출되지 않음
    assert posts == []


def test_empty_token_does_not_record_pending_in_audit(tmp_path):
    """token 부재 시 PENDING/POSTED/FAILED 어느 record 도 audit 에 남지 않음 (transaction 안에서 raise)."""
    decision_path = _write_decision(tmp_path)
    posts: list[dict] = []
    audit = OwnerTriggerAudit(tmp_path)

    def http_post(method, path, body, headers):
        posts.append({"method": method})
        return {"status": 201}

    mod = OwnerTriggerOnly(
        workspace_root=tmp_path,
        http_post=http_post,
        token_provider=lambda: "",
        audit=audit,
    )
    with pytest.raises(TokenBoundaryViolation):
        mod.trigger_gemini_review(
            decision_path=decision_path,
            owner="o",
            repo="r",
            current_head_actual=_HEAD_A,
        )
    # token 확인은 transaction 안에서 일어나지만 PENDING record 전 단계 — audit 빈 채로 유지.
    rows = list(audit._iter_rows())
    assert rows == []
    assert posts == []
