from __future__ import annotations

from pathlib import Path

from app.modules.rag.intent_router_v2 import ConversationState, IntentRouterV2
from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.phrases_loader import PhraseCatalogLoader
from tests.unit_tests.rag.intent_router_testkit import repo_context


def _by_id(case_id: str):
    cases = PhraseCatalogLoader().load(Path(__file__).resolve().parent / "fixtures" / "phrases.yaml")
    for case in cases:
        if case.case_id == case_id:
            return case
    raise AssertionError(f"case {case_id} not found")


def test_show_file_phrase_maps_to_open_file() -> None:
    case = _by_id("code-open-abstract-task")
    result = IntentRouterV2().route(case.text, ConversationState(), repo_context())

    assert result.query_plan.sub_intent == "OPEN_FILE"
    assert [item.layer_id for item in result.retrieval_spec.layer_queries] == ["C0_SOURCE_CHUNKS"]
    assert result.query_plan.path_hints
    assert result.query_plan.keyword_hints == []
    assert result.query_plan.symbol_candidates == []
    assert result.retrieval_constraints.fuzzy_symbol_search.enabled is False


def test_find_tests_prefers_test_scope() -> None:
    case = _by_id("code-find-tests-for-context")
    result = IntentRouterV2().route(case.text, ConversationState(), repo_context())

    assert result.query_plan.sub_intent == "FIND_TESTS"
    assert "tests/**" in result.retrieval_constraints.prefer_globs
    layers = [item.layer_id for item in result.retrieval_spec.layer_queries]
    assert "C3_ENTRYPOINTS" not in layers


def test_explain_function_has_symbol_kind_hint() -> None:
    case = _by_id("code-explain-handle-errors")
    result = IntentRouterV2().route(case.text, ConversationState(), repo_context())

    assert result.query_plan.symbol_kind_hint == "function"
    assert "tests/**" in result.retrieval_constraints.exclude_globs
    assert result.symbol_resolution.status == "pending"


def test_followup_marker_sets_followup_likely_mode() -> None:
    router = IntentRouterV2()
    first = router.route("Открой файл src/mail_order_bot/context.py", ConversationState(), repo_context())
    state = ConversationState().advance(first)

    second = router.route("Теперь объясни функцию handle_errors", state, repo_context())

    assert second.conversation_mode == "FOLLOWUP_LIKELY"
    assert second.query_plan.symbol_candidates == ["handle_errors"]


def test_docs_profile_uses_docs_constraints_and_disables_symbol_resolution() -> None:
    case = _by_id("docs-about-readme-deploy")
    result = IntentRouterV2().route(case.text, ConversationState(), repo_context())

    assert result.retrieval_profile == "docs"
    assert result.symbol_resolution.status == "not_requested"
    assert result.query_plan.symbol_candidates == []
    assert result.query_plan.symbol_kind_hint == "unknown"
    assert "README_DEPLOY.md" in result.retrieval_constraints.include_globs
    assert "src/**" not in result.retrieval_constraints.include_globs
    assert any(item in result.query_plan.keyword_hints for item in ["deploy", "deployment", "docker", "compose"])


def test_docs_path_scope_prefers_scoped_layers() -> None:
    case = _by_id("docs-about-readme-deploy")
    result = IntentRouterV2().route(case.text, ConversationState(), repo_context())

    layers = [item.layer_id for item in result.retrieval_spec.layer_queries]
    assert layers == ["D3_SECTION_INDEX", "D2_FACT_INDEX"]


def test_trace_flow_phrase_maps_to_trace_flow_sub_intent() -> None:
    result = IntentRouterV2().route("Покажи поток данных для Context.data", ConversationState(), repo_context())

    assert result.query_plan.sub_intent == "TRACE_FLOW"
    layer_ids = [item.layer_id for item in result.retrieval_spec.layer_queries]
    assert "C2_DEPENDENCY_GRAPH" in layer_ids
    assert "C3_ENTRYPOINTS" in layer_ids
    assert result.evidence_policy.require_flow is True


def test_explain_sub_intent_keeps_entrypoints_for_execution_trace_retrieval() -> None:
    result = IntentRouterV2().route("Объясни как работает Context", ConversationState(), repo_context())

    assert result.query_plan.sub_intent == "EXPLAIN"
    layer_ids = [item.layer_id for item in result.retrieval_spec.layer_queries]
    assert "C3_ENTRYPOINTS" in layer_ids
    assert "C4_SEMANTIC_ROLES" in layer_ids


def test_architecture_phrase_uses_architecture_sub_intent_and_semantic_roles_layer() -> None:
    result = IntentRouterV2().route("Какие сервисы и обработчики есть в архитектуре?", ConversationState(), repo_context())

    assert result.query_plan.sub_intent == "ARCHITECTURE"
    layer_ids = [item.layer_id for item in result.retrieval_spec.layer_queries]
    assert layer_ids[0] == "C4_SEMANTIC_ROLES"
    assert "C2_DEPENDENCY_GRAPH" in layer_ids
    assert "C3_ENTRYPOINTS" in layer_ids


def test_endpoint_phrase_maps_to_find_entrypoints() -> None:
    result = IntentRouterV2().route("Где health endpoint?", ConversationState(), repo_context())

    assert result.query_plan.sub_intent == "FIND_ENTRYPOINTS"
    layer_ids = [item.layer_id for item in result.retrieval_spec.layer_queries]
    assert layer_ids == ["C3_ENTRYPOINTS", "C0_SOURCE_CHUNKS"]


def test_request_flow_phrase_prefers_trace_flow_over_explain() -> None:
    result = IntentRouterV2().route(
        "Покажи как проходит запрос /health в HttpControlAppFactory",
        ConversationState(),
        repo_context(),
    )

    assert result.query_plan.sub_intent == "TRACE_FLOW"
    layer_ids = [item.layer_id for item in result.retrieval_spec.layer_queries]
    assert "C2_DEPENDENCY_GRAPH" in layer_ids
    assert "C3_ENTRYPOINTS" in layer_ids


def test_flow_phrase_with_start_prefers_trace_flow_over_entrypoints() -> None:
    result = IntentRouterV2().route(
        "Покажи поток выполнения при запуске RuntimeManager",
        ConversationState(),
        repo_context(),
    )

    assert result.query_plan.sub_intent == "TRACE_FLOW"
    layer_ids = [item.layer_id for item in result.retrieval_spec.layer_queries]
    assert "C2_DEPENDENCY_GRAPH" in layer_ids
