# -*- coding: utf-8 -*-
"""utils.dispatch_status_enum — task-2645 dispatch status state machine.

회장 verbatim (2026-05-24 task-2644 사고 박제):
- dispatch.py 가 cron_response.status="ok" 만 보고 final success 로 처리하지 않는다.
- 발사 직후 status 는 ``DISPATCH_SUBMITTED_UNVERIFIED`` (cron 등록 응답 OK ≠ spawn 성공).
- spawn verification gate 통과 후에만 ``DISPATCH_VERIFIED_SPAWN`` 으로 전이.
- 4신호 silent drop 확정 시 ``DISPATCH_SILENT_DROP_HOLD``.
- 명시적 cron 에러 / pre-flight 차단은 기존 ``DISPATCH_ERROR``.

본 enum 은 dispatch 모듈과 spawn_verifier / regression 시험 양쪽에서 공유되는
단일 source-of-truth 이다. 문자열 상수 + 천이 검증 함수 두 가지를 제공한다.
"""
from __future__ import annotations

from typing import FrozenSet, Mapping


# ── 상태 상수 (회장 verbatim — paraphrase 금지) ─────────────────────────────
DISPATCH_SUBMITTED_UNVERIFIED = "DISPATCH_SUBMITTED_UNVERIFIED"
"""cron 등록 응답 ok 직후 (spawn verification 미수행). final success 보고 금지."""

DISPATCH_VERIFIED_SPAWN = "DISPATCH_VERIFIED_SPAWN"
"""spawn_verifier 4 항목 중 핵심 신호 hit → 봇이 실제로 떴음."""

DISPATCH_SILENT_DROP_HOLD = "DISPATCH_SILENT_DROP_HOLD"
"""4신호 모두 hit (silent drop 확정) — 회장 보고 필요, retry 보류."""

DISPATCH_ERROR = "DISPATCH_ERROR"
"""pre-flight 차단(HARD_BLOCK 등) 또는 cron 등록 자체 실패."""

DISPATCH_PRECHECK_BLOCKED = "DISPATCH_PRECHECK_BLOCKED"
"""prompt_byte_classifier 가 HARD_BLOCK / CHANNEL_OVERFLOW 로 차단."""


ALL_STATES: FrozenSet[str] = frozenset(
    {
        DISPATCH_SUBMITTED_UNVERIFIED,
        DISPATCH_VERIFIED_SPAWN,
        DISPATCH_SILENT_DROP_HOLD,
        DISPATCH_ERROR,
        DISPATCH_PRECHECK_BLOCKED,
    }
)

# 합법적 final-success 로 보고 가능한 상태 — 회장 verbatim ANCHOR-1.
FINAL_SUCCESS_STATES: FrozenSet[str] = frozenset({DISPATCH_VERIFIED_SPAWN})

# spawn verification 이전에는 절대 final success 아님.
NON_FINAL_STATES: FrozenSet[str] = frozenset({DISPATCH_SUBMITTED_UNVERIFIED})

# 회장 보고 hold 가 필요한 상태.
HOLD_FOR_CHAIR_STATES: FrozenSet[str] = frozenset({DISPATCH_SILENT_DROP_HOLD})


# ── 천이 규칙 (회장 verbatim 사상) ──────────────────────────────────────────
_ALLOWED_TRANSITIONS: Mapping[str, FrozenSet[str]] = {
    DISPATCH_PRECHECK_BLOCKED: frozenset(),
    DISPATCH_SUBMITTED_UNVERIFIED: frozenset(
        {
            DISPATCH_VERIFIED_SPAWN,
            DISPATCH_SILENT_DROP_HOLD,
            DISPATCH_ERROR,
        }
    ),
    DISPATCH_VERIFIED_SPAWN: frozenset(),
    DISPATCH_SILENT_DROP_HOLD: frozenset(),
    DISPATCH_ERROR: frozenset(),
}


def is_final_success(state: str) -> bool:
    """final success 로 회장 보고 가능한 상태인가."""
    return state in FINAL_SUCCESS_STATES


def is_hold_for_chair(state: str) -> bool:
    """silent drop 류 — 즉시 회장 보고 필요."""
    return state in HOLD_FOR_CHAIR_STATES


def assert_not_premature_success(state: str) -> None:
    """dispatch.py cron_response=ok 단독 final success 보고 차단 가드.

    회장 verbatim ANCHOR-1: "dispatch.py cron_response=ok ≠ spawn 성공 —
    DISPATCH_SUBMITTED_UNVERIFIED 거쳐야 함".

    Raises:
        AssertionError: state 가 SUBMITTED_UNVERIFIED 인 채로 final 보고 시도.
    """
    if state == DISPATCH_SUBMITTED_UNVERIFIED:
        raise AssertionError(
            "dispatch status=DISPATCH_SUBMITTED_UNVERIFIED 단독으로 final "
            "success 보고 금지 (회장 verbatim ANCHOR-1, task-2645 §17.8)."
        )


def can_transition(from_state: str, to_state: str) -> bool:
    """천이 가능 여부 검증."""
    if from_state not in ALL_STATES:
        return False
    if to_state not in ALL_STATES:
        return False
    return to_state in _ALLOWED_TRANSITIONS.get(from_state, frozenset())


__all__ = [
    "DISPATCH_SUBMITTED_UNVERIFIED",
    "DISPATCH_VERIFIED_SPAWN",
    "DISPATCH_SILENT_DROP_HOLD",
    "DISPATCH_ERROR",
    "DISPATCH_PRECHECK_BLOCKED",
    "ALL_STATES",
    "FINAL_SUCCESS_STATES",
    "NON_FINAL_STATES",
    "HOLD_FOR_CHAIR_STATES",
    "is_final_success",
    "is_hold_for_chair",
    "assert_not_premature_success",
    "can_transition",
]
