"""Phase 3.5 fix2: /ask 의 X-Source / X-Eval-Case-Id trust boundary.

`_resolve_eval_identity()` 단위 테스트.
- token 없음/틀림 + X-Source=eval → source='document_server', eval_case_id=None
- token 일치 + X-Source=eval + X-Eval-Case-Id=case_xxx → ('eval', 'case_xxx')
- token 틀림 + X-Eval-Case-Id 만 (X-Source 미지정) → eval_case_id=None
- 일반 호출 (X-Source=ui_search, no eval headers) → ('ui_search', None)
- env 미설정 (eval_runner_token='') 시 모든 eval claim 거부
"""

from __future__ import annotations

import os
import sys

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "app"))

import pytest


@pytest.fixture
def resolve_with_token(monkeypatch):
    """settings.eval_runner_token 을 monkey-patch 해서 _resolve_eval_identity 테스트."""
    def _make(token: str):
        from core import config as cfg_mod
        from api import search as search_mod
        # 두 모듈 모두에서 settings 객체 참조하므로 직접 attr 변경
        monkeypatch.setattr(search_mod.settings, "eval_runner_token", token)
        return search_mod._resolve_eval_identity
    return _make


def test_no_token_no_eval_headers_default(resolve_with_token):
    """일반 호출 — eval 헤더 없음, source 기본값."""
    resolve = resolve_with_token("secret123")
    assert resolve(None, None, None) == ("document_server", None)


def test_normal_source_with_token(resolve_with_token):
    """ui_search 호출 — eval 클레임 아님이라 token 무관."""
    resolve = resolve_with_token("secret123")
    assert resolve("ui_search", None, None) == ("ui_search", None)


def test_eval_claim_no_token_rejected(resolve_with_token):
    """X-Source=eval 인데 token 없음 → 거부, source='document_server'."""
    resolve = resolve_with_token("secret123")
    assert resolve("eval", "case_001", None) == ("document_server", None)


def test_eval_claim_wrong_token_rejected(resolve_with_token):
    """token 틀림 → 거부."""
    resolve = resolve_with_token("secret123")
    assert resolve("eval", "case_001", "wrong_token") == ("document_server", None)


def test_eval_claim_correct_token_accepted(resolve_with_token):
    """token 일치 → 'eval' source + case_id 적재."""
    resolve = resolve_with_token("secret123")
    assert resolve("eval", "case_001", "secret123") == ("eval", "case_001")


def test_eval_case_id_only_no_source_no_token(resolve_with_token):
    """X-Eval-Case-Id 만 있고 token 없음 → 거부, case_id=None."""
    resolve = resolve_with_token("secret123")
    assert resolve(None, "case_001", None) == ("document_server", None)


def test_eval_case_id_only_wrong_token(resolve_with_token):
    """X-Eval-Case-Id 만 + token 틀림 → 거부."""
    resolve = resolve_with_token("secret123")
    assert resolve(None, "case_001", "wrong") == ("document_server", None)


def test_env_unset_rejects_even_correct_format(resolve_with_token):
    """settings.eval_runner_token='' 인 환경 → 모든 eval 클레임 거부."""
    resolve = resolve_with_token("")
    # token 헤더가 와도 server side 가 비어있으면 거부 (constant-time False)
    assert resolve("eval", "case_001", "") == ("document_server", None)
    assert resolve("eval", "case_001", "anything") == ("document_server", None)


def test_non_eval_source_forces_case_id_none(resolve_with_token):
    """X-Source=ui_detail + X-Eval-Case-Id (실수로 같이 보냄) → case_id=None.

    eval claim 아님 (source != 'eval' 이고 case_id 가 fallback 으로 eval claim 트리거)
    이지만 source claim 이 명시적으로 non-eval 이라 token 검증 후 case_id None.
    """
    resolve = resolve_with_token("secret123")
    # case_id 가 있으면 eval claim 으로 처리됨 → token 없으면 거부 → ('ui_detail' 클레임,
    # 하지만 거부 분기에서 claimed_source != 'eval' 이라 그대로 'ui_detail' 반환, case_id=None)
    assert resolve("ui_detail", "case_001", None) == ("ui_detail", None)
