"""adversarial_filter 纯判定：hash / 指纹 / canonical / 翻转判定。"""

from app.question_gen.adversarial_filter import (
    FlipDecision,
    agent_config_fingerprint,
    canonical_answer_text,
    judge_flip,
    question_hash,
)

_OPTIONS = ("A. 蒸", "B. 炒", "C. 煮", "D. 炸")


def test_question_hash_stable_and_payload_sensitive():
    h1 = question_hash("?", _OPTIONS, "A")
    h2 = question_hash("?", _OPTIONS, "A")
    assert h1 == h2
    h3 = question_hash("?", _OPTIONS, "B")  # answer 变 → hash 变
    assert h1 != h3
    h4 = question_hash("?", ("A. 蒸", "B. 炒", "C. 煮", "D. 烤"), "A")  # option 变 → 变
    assert h1 != h4
    h5 = question_hash("!", _OPTIONS, "A")  # question 变 → 变
    assert h1 != h5


def test_agent_config_fingerprint_changes_with_inputs():
    a = agent_config_fingerprint(skill_mode="auto", max_steps=40, model="m1")
    b = agent_config_fingerprint(skill_mode="auto", max_steps=41, model="m1")
    c = agent_config_fingerprint(skill_mode="manual", max_steps=40, model="m1")
    assert a != b and a != c


def test_canonical_answer_text_maps_letter_to_option_text():
    assert canonical_answer_text(_OPTIONS, "C") == "煮"
    assert canonical_answer_text(_OPTIONS, "c") == "煮"


def test_canonical_answer_text_invalid_returns_none():
    assert canonical_answer_text(_OPTIONS, "Z") is None
    assert canonical_answer_text(_OPTIONS, "") is None
    assert canonical_answer_text(_OPTIONS, None) is None


def test_canonical_answer_text_invalid_inputs_return_none():
    for bad in ["", "AB", "E", "1", " ", "AA"]:
        assert canonical_answer_text(_OPTIONS, bad) is None, bad
    assert canonical_answer_text(_OPTIONS, "b") == "炒"  # 合法仍工作（去前缀、大小写不敏感）


def test_judge_flip_different_answers_passed():
    # P 选"蒸"，Q（镜像）选"炒"→ 语义不同 → passed
    d = judge_flip(p_text="蒸", q_text="炒")
    assert d is FlipDecision.PASSED


def test_judge_flip_same_answer_filtered():
    d = judge_flip(p_text="蒸", q_text="蒸")
    assert d is FlipDecision.FILTERED_NO_FLIP


def test_judge_flip_invalid_answer_skipped():
    assert judge_flip(p_text=None, q_text="炒") is FlipDecision.FLIP_SKIPPED
    assert judge_flip(p_text="蒸", q_text=None) is FlipDecision.FLIP_SKIPPED
