"""TRUE_SILENT_DROP hard timeout gate.

ANCHOR-3 (회장 verbatim):
  fire 후 30분 hard timeout. 다음 3 예외 중 하나라도 있으면
  TRUE_SILENT_DROP 단정 금지.
    - schedule_history pending
    - result marker present (memory/events/<task_id>.*-result-*.json)
    - callback envelope present (memory/.callback_inbox/*.acked
      또는 result/report/done marker 자체)
"""

from __future__ import annotations

from dataclasses import dataclass
from typing import Optional

from .source_collector import SourceSnapshot


HARD_TIMEOUT_SECONDS = 30 * 60  # 30 minutes


@dataclass(frozen=True)
class TimeoutDecision:
    silent_drop_eligible: bool
    elapsed_seconds: float
    hard_timeout_seconds: int
    blocking_exceptions: tuple[str, ...]
    reason: str


def _is_pending_status(value: Optional[str]) -> bool:
    if not value or not isinstance(value, str):
        return False
    return value.strip().lower() in {"pending", "running", "in_progress", "started", "fired"}


def evaluate_timeout_gate(
    sources: SourceSnapshot,
    elapsed_seconds: float,
    *,
    hard_timeout_seconds: int = HARD_TIMEOUT_SECONDS,
) -> TimeoutDecision:
    """TRUE_SILENT_DROP 적격성 평가.

    Returns
    -------
    TimeoutDecision.silent_drop_eligible 가 True 인 경우에만
    status_classifier 가 TRUE_SILENT_DROP 으로 분류할 수 있다.
    """
    exceptions: list[str] = []

    if _is_pending_status(sources.schedule_history_last_status):
        exceptions.append(
            f"schedule_history pending (last_status={sources.schedule_history_last_status!r})"
        )

    # result marker present 예외 (ANCHOR-3)
    if sources.result_present:
        exceptions.append("result_marker present")
    # callback envelope present 예외 (ANCHOR-2: done/report marker 도 callback evidence)
    if sources.done_present:
        exceptions.append("done_marker present")
    if sources.report_present:
        exceptions.append("report_marker present")
    if sources.callback_inbox_acked_present:
        exceptions.append("callback_inbox_acked present")

    if elapsed_seconds < hard_timeout_seconds:
        return TimeoutDecision(
            silent_drop_eligible=False,
            elapsed_seconds=float(elapsed_seconds),
            hard_timeout_seconds=hard_timeout_seconds,
            blocking_exceptions=tuple(exceptions),
            reason=(
                f"elapsed {elapsed_seconds:.0f}s < hard_timeout {hard_timeout_seconds}s "
                "→ silent drop 단정 금지"
            ),
        )

    if exceptions:
        return TimeoutDecision(
            silent_drop_eligible=False,
            elapsed_seconds=float(elapsed_seconds),
            hard_timeout_seconds=hard_timeout_seconds,
            blocking_exceptions=tuple(exceptions),
            reason=(
                "elapsed past hard_timeout 이나 ANCHOR-3 예외 존재: "
                + ", ".join(exceptions)
            ),
        )

    return TimeoutDecision(
        silent_drop_eligible=True,
        elapsed_seconds=float(elapsed_seconds),
        hard_timeout_seconds=hard_timeout_seconds,
        blocking_exceptions=(),
        reason=(
            f"elapsed {elapsed_seconds:.0f}s ≥ hard_timeout {hard_timeout_seconds}s "
            "+ ANCHOR-3 예외 0 → TRUE_SILENT_DROP 적격"
        ),
    )
