# -*- coding: utf-8 -*-
"""task-2644 regression: source_attribution_guard × 8 enum + 회장 verbatim phrase 차단."""
from __future__ import annotations

import pytest

from utils import source_attribution_guard as guard
from utils.source_attribution_guard import SourceAttribution


def test_source_enum_has_8_values():
    """회장 verbatim: source attribution enum 5 → 7 → 8 (LOG_LOOKUP_OR_SCHEDULE_HISTORY_VERIFICATION 추가)."""
    assert len(list(SourceAttribution)) == 8


def test_inbound_sources_explicit():
    assert guard.is_inbound_source(SourceAttribution.RECEIVED_INBOUND_THIS_SESSION.value)
    assert guard.is_inbound_source(SourceAttribution.CALLBACK_COLLECTOR_PROCESSED.value)
    # 나머지는 inbound 아님
    assert not guard.is_inbound_source(SourceAttribution.LOG_LOOKUP_AFTER_CHAIR_QUESTION.value)
    assert not guard.is_inbound_source(SourceAttribution.LOG_LOOKUP_PROACTIVE.value)
    assert not guard.is_inbound_source(SourceAttribution.MEMORY_RECALL.value)
    assert not guard.is_inbound_source(SourceAttribution.INFERENCE_ONLY.value)
    assert not guard.is_inbound_source(SourceAttribution.CALLBACK_LEDGER_RECONCILED.value)
    assert not guard.is_inbound_source(SourceAttribution.LOG_LOOKUP_OR_SCHEDULE_HISTORY_VERIFICATION.value)


@pytest.mark.parametrize("phrase", [
    "callback received from dev6",
    "callback 도착",
    "callback 수신",
    "콜백 도착 완료",
    "콜백 수신 완료",
    "수신 완료입니다",
    "callback inbound 확인",
])
def test_phrases_detected(phrase):
    matches = guard.contains_received_phrase(phrase)
    assert matches, f"phrase '{phrase}' must be detected"


@pytest.mark.parametrize("phrase", [
    "schedule_history chain status=ok 확인",
    "log 조회 결과 정상",
    "ledger reconciliation 완료",
    "context recovery 종료",
])
def test_phrases_not_detected(phrase):
    matches = guard.contains_received_phrase(phrase)
    assert matches == [], f"phrase '{phrase}' must NOT be detected (got {matches})"


def test_detect_received_misuse_non_inbound_source():
    """source=MEMORY_RECALL + 'callback received' 표현 → violation."""
    misuse, matches = guard.detect_received_misuse(
        "callback received 확인",
        SourceAttribution.MEMORY_RECALL.value,
    )
    assert misuse is True
    assert "callback received" in matches


def test_detect_received_misuse_inbound_source_ok():
    """source=RECEIVED_INBOUND_THIS_SESSION 이면 'received' 표현 허용."""
    misuse, _ = guard.detect_received_misuse(
        "callback received 정상",
        SourceAttribution.RECEIVED_INBOUND_THIS_SESSION.value,
    )
    assert misuse is False


def test_detect_received_misuse_collector_source_ok():
    """source=CALLBACK_COLLECTOR_PROCESSED 도 inbound 로 허용 (주 경로)."""
    misuse, _ = guard.detect_received_misuse(
        "callback received from spawn",
        SourceAttribution.CALLBACK_COLLECTOR_PROCESSED.value,
    )
    assert misuse is False


def test_detect_received_misuse_missing_source_violation():
    """source 미명시 + received phrase → violation (조건 7)."""
    misuse, _ = guard.detect_received_misuse("callback received", None)
    assert misuse is True


def test_detect_schedule_history_as_inbound_1c0f6f52_pattern():
    """1C0F6F52 패턴: source=LOG_LOOKUP_OR_SCHEDULE_HISTORY_VERIFICATION 인데 received phrase 사용 → 차단."""
    violation = guard.detect_schedule_history_as_inbound(
        "schedule_history chain status=ok 확인. callback 수신 완료.",
        SourceAttribution.LOG_LOOKUP_OR_SCHEDULE_HISTORY_VERIFICATION.value,
    )
    assert violation is True


def test_classify_source_collector_priority():
    """collector_mode + inbound envelope → CALLBACK_COLLECTOR_PROCESSED."""
    s = guard.classify_source(
        inbound_envelope_present=True,
        collector_mode=True,
        ledger_lookup_only=False,
        schedule_history_lookup_only=False,
        chair_question_triggered=False,
    )
    assert s == SourceAttribution.CALLBACK_COLLECTOR_PROCESSED.value


def test_classify_source_schedule_history_lookup():
    s = guard.classify_source(
        inbound_envelope_present=False,
        collector_mode=False,
        ledger_lookup_only=False,
        schedule_history_lookup_only=True,
        chair_question_triggered=False,
    )
    assert s == SourceAttribution.LOG_LOOKUP_OR_SCHEDULE_HISTORY_VERIFICATION.value


def test_validate_returns_violation_with_reason():
    result = guard.validate(
        "callback 도착 확인",
        SourceAttribution.MEMORY_RECALL.value,
    )
    assert result["violation"] is True
    assert "non-inbound" in (result["reason"] or "").lower() or "NON_INBOUND_SOURCE" in (result["reason"] or "")


def test_validate_returns_violation_for_schedule_history_misuse():
    result = guard.validate(
        "schedule_history chain ok · callback 수신 완료",
        SourceAttribution.LOG_LOOKUP_OR_SCHEDULE_HISTORY_VERIFICATION.value,
    )
    # 두 violation 중 하나는 반드시 잡힘 (조건 7 또는 조건 8)
    assert result["violation"] is True


def test_validate_passes_for_clean_text():
    result = guard.validate(
        "context recovery 완료 · next_action=NOOP_TERMINAL · ledger 기록",
        SourceAttribution.CALLBACK_COLLECTOR_PROCESSED.value,
    )
    assert result["violation"] is False
