# -*- coding: utf-8 -*-
"""tests.test_dispatch_gate_integration_2645 — task-2645 §17.13/§17.17.

dispatch.py 가 cron_response.status="ok" 만 보고 final success 보고하지 못하게
한다. 직접 cron workaround 와 dispatch.py 결과를 동일 spawn gate 로 검증.
"""
from __future__ import annotations

import json
from pathlib import Path

import pytest

from utils.dispatch_spawn_verifier import (
    SpawnSignals,
    dispatched_dict_is_unverified,
    evaluate_signals,
    is_silent_drop_confirmed,
)
from utils.dispatch_status_enum import (
    DISPATCH_SILENT_DROP_HOLD,
    DISPATCH_SUBMITTED_UNVERIFIED,
    DISPATCH_VERIFIED_SPAWN,
    assert_not_premature_success,
    is_final_success,
)
from utils.prompt_byte_classifier import (
    PROMPT_HARD_BLOCK_THRESHOLD,
    classify_prompt_bytes,
    is_allowed,
)


FIXTURE_DIR = Path(__file__).parent / "fixtures" / "dispatch_gate"


def _settle_to_final(dispatched_dict: dict, signals: SpawnSignals) -> dict:
    """dispatch.py 반환 dict + spawn signals 를 최종 status 로 settle.

    회장 verbatim 게이트 — cron_response.status=ok 단독으로 final success 보지 않음.
    """
    if not dispatched_dict_is_unverified(dispatched_dict):
        # 이미 verification 통과된 dict
        return {"final_status": dispatched_dict.get("status"), "verification": None}
    cron_id = dispatched_dict.get("cron_response", {}).get("id") or ""
    r = evaluate_signals(cron_id, signals)
    final = r.next_status
    if final == DISPATCH_SUBMITTED_UNVERIFIED:
        # caller 재폴링 필요
        return {"final_status": DISPATCH_SUBMITTED_UNVERIFIED, "verification": r.to_json()}
    return {"final_status": final, "verification": r.to_json()}


# ── §17.13 dispatch.py vs 직접 cron 비교 fixture ────────────────────────
def test_false_ok_replay_dispatch_py_settles_to_silent_drop() -> None:
    fix = json.loads((FIXTURE_DIR / "dispatch_false_ok_replay.json").read_text())
    case = fix["dispatch_py_path"]

    # 1) dispatch.py 가 status="dispatched" + cron_response.status="ok" 반환 → unverified 로 인식
    assert dispatched_dict_is_unverified(case["dispatched_dict"]) is True
    assert case["expected_unverified_before_gate"] is True

    # 2) spawn signals 4신호 모두 hit (673AA5A6 박제)
    settled = _settle_to_final(case["dispatched_dict"], SpawnSignals(**case["spawn_signals"]))
    assert settled["final_status"] == DISPATCH_SILENT_DROP_HOLD
    assert settled["final_status"] == case["expected_final_status_after_gate"]

    # 3) is_final_success → False (회장 보고 금지)
    assert is_final_success(settled["final_status"]) is False
    assert case["expected_is_final_success"] is False


def test_false_ok_replay_direct_cron_settles_to_verified_spawn() -> None:
    fix = json.loads((FIXTURE_DIR / "dispatch_false_ok_replay.json").read_text())
    case = fix["direct_cron_path"]

    assert dispatched_dict_is_unverified(case["dispatched_dict"]) is True
    settled = _settle_to_final(case["dispatched_dict"], SpawnSignals(**case["spawn_signals"]))
    assert settled["final_status"] == DISPATCH_VERIFIED_SPAWN
    assert settled["final_status"] == case["expected_final_status_after_gate"]
    assert is_final_success(settled["final_status"]) is True


def test_dispatch_py_and_direct_cron_share_same_gate_logic() -> None:
    """동일한 spawn signals 면 dispatch.py 든 직접 cron 이든 동일 final status."""
    fix = json.loads((FIXTURE_DIR / "dispatch_false_ok_replay.json").read_text())
    sig_silent = SpawnSignals(**fix["dispatch_py_path"]["spawn_signals"])
    sig_alive = SpawnSignals(**fix["direct_cron_path"]["spawn_signals"])

    dispatch_py_dict = fix["dispatch_py_path"]["dispatched_dict"]
    direct_dict = fix["direct_cron_path"]["dispatched_dict"]

    # 두 경로에 silent drop signals 주입 → 모두 SILENT_DROP_HOLD
    s1 = _settle_to_final(dispatch_py_dict, sig_silent)
    s2 = _settle_to_final(direct_dict, sig_silent)
    assert s1["final_status"] == s2["final_status"] == DISPATCH_SILENT_DROP_HOLD

    # 두 경로에 alive signals 주입 → 모두 VERIFIED_SPAWN
    s3 = _settle_to_final(dispatch_py_dict, sig_alive)
    s4 = _settle_to_final(direct_dict, sig_alive)
    assert s3["final_status"] == s4["final_status"] == DISPATCH_VERIFIED_SPAWN


# ── §17.17 false dispatched report fails regression ─────────────────────
def test_false_dispatched_report_must_fail_assertion() -> None:
    """ANCHOR-1: status=DISPATCH_SUBMITTED_UNVERIFIED 단독 final success 보고 금지."""
    # 시도: dispatch.py 가 cron_response=ok 받은 직후 SUBMITTED_UNVERIFIED 단계
    pre_gate_status = DISPATCH_SUBMITTED_UNVERIFIED
    with pytest.raises(AssertionError):
        assert_not_premature_success(pre_gate_status)
    # 시도: 검증 없이 success 보고 (final_status=ok 가짜) — is_final_success False
    fake_success = "ok"
    assert is_final_success(fake_success) is False
    fake_success_2 = "dispatched"
    assert is_final_success(fake_success_2) is False


# ── prompt gate + spawn gate 합성 (정책 결합) ───────────────────────────
def test_prompt_hardblock_short_circuits_before_spawn_gate() -> None:
    """prompt >3900 bytes 면 dispatch 전 차단 — spawn gate 까지 가지 않음."""
    over = "x" * (PROMPT_HARD_BLOCK_THRESHOLD + 1)
    c = classify_prompt_bytes(over)
    assert c.hard_block is True
    assert is_allowed(over) is False


def test_prompt_target_proceeds_and_spawn_gate_then_settles() -> None:
    """≤3200 prompt + 정상 spawn 신호 → VERIFIED_SPAWN."""
    prompt = "x" * 2200  # OK_TARGET
    assert is_allowed(prompt) is True
    sig = SpawnSignals(
        workspace_dir_exists=True, workspace_dir_has_activity=True,
        executor_process_present=True, schedule_visible_in_cron_list=False,
        schedule_accepted_marker_present=True, schedule_history_log_present=True,
    )
    r = evaluate_signals("OK1234", sig)
    assert r.next_status == DISPATCH_VERIFIED_SPAWN
    assert is_silent_drop_confirmed(r) is False
