"""task-2693 regression: callback owner enforcement (PR #152 hardening #1).

검증 대상: dispatch.normal_fallback_callback_helper.classify_session_propagation
범위: observed_owner_key 가 ANU key 인지에 따른 분류 + non-str 타입 isinstance 방어.

NOTE: 모듈은 enum 값 문자열을 `SESSION_PROPAGATION_OK` /
`SESSION_PROPAGATION_DISCONTINUITY` 상수로 export 한다. 본 테스트는
기존 callback_session_propagation 디렉토리와 동일한 import 컨벤션을 따른다
(worktree 본 강제 로딩을 위해 importlib.util.spec_from_file_location 사용).
"""
from __future__ import annotations

import importlib.util
import sys
from pathlib import Path

_ROOT = Path(__file__).resolve().parents[3]
if str(_ROOT) not in sys.path:
    sys.path.insert(0, str(_ROOT))


def _load_real(modname: str, relpath: str):
    existing = sys.modules.get(modname)
    if existing is not None and getattr(existing, "__file__", "").endswith(relpath):
        return existing
    spec = importlib.util.spec_from_file_location(modname, _ROOT / relpath)
    assert spec is not None and spec.loader is not None
    mod = importlib.util.module_from_spec(spec)
    sys.modules[modname] = mod
    spec.loader.exec_module(mod)
    return mod


_load_real("dispatch.callback_owner_enforcer", "dispatch/callback_owner_enforcer.py")
_load_real("dispatch.normal_fallback_callback_helper", "dispatch/normal_fallback_callback_helper.py")

from dispatch.normal_fallback_callback_helper import (  # noqa: E402
    SESSION_PROPAGATION_GAP,
    SESSION_PROPAGATION_OK,
    SESSION_PROPAGATION_SELF_KEY,
    classify_session_propagation,
)

# AUTHORITATIVE_CALLBACK_COLLECTOR_PROCESSED 는 SESSION_PROPAGATION_OK 의 값.
# 가독성 위해 별칭으로 사용.
AUTHORITATIVE_CALLBACK_COLLECTOR_PROCESSED = SESSION_PROPAGATION_OK

ANU_KEY = "c119085addb0f8b7"
EXECUTOR_KEY = "fedf78d1d09509f5"
SID = "53e89540-5bed-4692-a726-ed857820758a"


def test_self_key_classified_as_self_key():
    v = classify_session_propagation(
        observed_owner_key=EXECUTOR_KEY,
        executor_key=EXECUTOR_KEY,
        anu_keys=[ANU_KEY],
        chair_facing_session_id=SID,
        collector_session_id=SID,
        delivery_session_id=SID,
    )
    assert v.classification == SESSION_PROPAGATION_SELF_KEY, v


def test_non_anu_non_self_classified_as_self_key():
    v = classify_session_propagation(
        observed_owner_key="ffffffffffffffff",
        executor_key=EXECUTOR_KEY,
        anu_keys=[ANU_KEY],
        chair_facing_session_id=SID,
        collector_session_id=SID,
        delivery_session_id=SID,
    )
    assert v.classification == SESSION_PROPAGATION_SELF_KEY, v


def test_observed_owner_key_none_does_not_raise():
    v = classify_session_propagation(
        observed_owner_key=None,
        executor_key=EXECUTOR_KEY,
        anu_keys=[ANU_KEY],
        chair_facing_session_id=SID,
        collector_session_id=SID,
        delivery_session_id=SID,
    )
    assert v.classification == SESSION_PROPAGATION_SELF_KEY, v


def test_observed_owner_key_int_does_not_raise():
    v = classify_session_propagation(
        observed_owner_key=12345,  # type: ignore[arg-type]
        executor_key=EXECUTOR_KEY,
        anu_keys=[ANU_KEY],
        chair_facing_session_id=SID,
        collector_session_id=SID,
        delivery_session_id=SID,
    )
    assert v.classification == SESSION_PROPAGATION_SELF_KEY, v


def test_anu_key_with_missing_chair_sid_gives_gap():
    v = classify_session_propagation(
        observed_owner_key=ANU_KEY,
        executor_key=EXECUTOR_KEY,
        anu_keys=[ANU_KEY],
        chair_facing_session_id=None,
        collector_session_id=SID,
        delivery_session_id=SID,
    )
    assert v.classification == SESSION_PROPAGATION_GAP, v


def test_anu_key_with_3_sid_match_authoritative():
    v = classify_session_propagation(
        observed_owner_key=ANU_KEY,
        executor_key=EXECUTOR_KEY,
        anu_keys=[ANU_KEY],
        chair_facing_session_id=SID,
        collector_session_id=SID,
        delivery_session_id=SID,
    )
    assert v.classification == AUTHORITATIVE_CALLBACK_COLLECTOR_PROCESSED, v
