"""Tests for CODE_QA evaluation harness: golden loader, compare logic, fixture-mode run."""

from __future__ import annotations

from pathlib import Path

import pytest

from app.modules.rag.code_qa_pipeline import CodeQAPipelineResult
from app.modules.rag.intent_router_v2.models import (
    CodeRetrievalFilters,
    EvidencePolicy,
    IntentRouterResult,
    QueryPlan,
    RetrievalSpec,
    SymbolResolution,
)

from tests.pipeline_setup.suite_01_synthetic.code_qa_eval.config import EvalConfig
from tests.pipeline_setup.suite_01_synthetic.code_qa_eval.golden_loader import GoldenCase, load_golden_cases
from tests.pipeline_setup.suite_01_synthetic.code_qa_eval.runner import EvalCaseResult, _compare, run_eval

_TEST_ROOT = Path(__file__).resolve().parent.parent


def test_load_golden_cases_returns_list() -> None:
    path = _TEST_ROOT / "golden" / "code_qa" / "cases.yaml"
    if not path.exists():
        pytest.skip("Golden cases file not found")
    cases = load_golden_cases(path)
    assert isinstance(cases, list)
    assert len(cases) >= 1
    c = cases[0]
    assert c.id
    assert c.query
    assert c.expected_intent in ("CODE_QA", "DOCS_QA")
    assert c.expected_sub_intent in ("OPEN_FILE", "EXPLAIN", "FIND_TESTS", "FIND_ENTRYPOINTS", "GENERAL_QA")
    assert c.expected_answer_mode in ("normal", "degraded", "insufficient")


def test_compare_passed_when_all_match() -> None:
    case = GoldenCase(
        id="test",
        query="Open app/main.py",
        expected_intent="CODE_QA",
        expected_sub_intent="OPEN_FILE",
        expected_answer_mode="normal",
    )
    result = _make_pipeline_result(intent="CODE_QA", sub_intent="OPEN_FILE", answer_mode="normal")
    passed, mismatches = _compare(case, result)
    assert passed is True
    assert len(mismatches) == 0


def test_compare_fails_on_intent_mismatch() -> None:
    case = GoldenCase(
        id="test",
        query="Open app/main.py",
        expected_intent="CODE_QA",
        expected_sub_intent="OPEN_FILE",
        expected_answer_mode="normal",
    )
    result = _make_pipeline_result(intent="DOCS_QA", sub_intent="OPEN_FILE", answer_mode="normal")
    passed, mismatches = _compare(case, result)
    assert passed is False
    assert any("intent" in m for m in mismatches)


def test_compare_fails_on_answer_mode_mismatch() -> None:
    case = GoldenCase(
        id="test",
        query="Explain NonExistent",
        expected_intent="CODE_QA",
        expected_sub_intent="EXPLAIN",
        expected_answer_mode="degraded",
    )
    result = _make_pipeline_result(intent="CODE_QA", sub_intent="EXPLAIN", answer_mode="normal")
    passed, mismatches = _compare(case, result)
    assert passed is False
    assert any("answer_mode" in m for m in mismatches)


def test_compare_path_scope_contains() -> None:
    case = GoldenCase(
        id="test",
        query="Open app/main.py",
        expected_intent="CODE_QA",
        expected_sub_intent="OPEN_FILE",
        expected_path_scope_contains=["app/main.py"],
    )
    result = _make_pipeline_result(
        intent="CODE_QA",
        sub_intent="OPEN_FILE",
        path_scope=["app/main.py"],
    )
    passed, _ = _compare(case, result)
    assert passed
    case_bad = GoldenCase(
        id="test2",
        query="Open other",
        expected_intent="CODE_QA",
        expected_sub_intent="OPEN_FILE",
        expected_path_scope_contains=["app/main.py"],
    )
    result_bad = _make_pipeline_result(intent="CODE_QA", sub_intent="OPEN_FILE", path_scope=[])
    passed_bad, mismatches_bad = _compare(case_bad, result_bad)
    assert not passed_bad
    assert any("path_scope" in m for m in mismatches_bad)


def test_eval_config_fixture_mode_by_default() -> None:
    config = EvalConfig.from_env(project_root=_TEST_ROOT)
    assert config.use_fixture is True
    assert "code_qa_repo" in str(config.repo_path)
    assert config.repo_path == _TEST_ROOT / "fixtures" / "code_qa_repo"
    assert config.golden_cases_path == _TEST_ROOT / "golden" / "code_qa" / "cases.yaml"
    assert config.test_results_dir == _TEST_ROOT / "test_results" / "code_qa_eval"


def test_run_eval_fixture_mode_structure() -> None:
    """Run full eval on fixture repo; validates harness path. Skips if DB/deps unavailable."""
    config = EvalConfig.from_env(project_root=_TEST_ROOT)
    if not config.repo_path.exists():
        pytest.skip("Fixture repo not found")
    if not config.golden_cases_path.exists():
        pytest.skip("Golden cases not found")
    try:
        results = run_eval(config)
    except Exception as e:
        msg = str(e).lower()
        if (
            "connect" in msg
            or "database" in msg
            or "engine" in msg
            or "modulenotfounderror" in msg
            or "sqlalchemy" in msg
        ):
            pytest.skip(f"DB or dependencies not available: {e}")
        raise
    assert isinstance(results, list)
    assert len(results) >= 1
    for r in results:
        assert isinstance(r, EvalCaseResult)
        assert r.case is not None
        assert r.pipeline_result is not None
        assert isinstance(r.passed, bool)
        assert isinstance(r.mismatches, list)


def _make_pipeline_result(
    *,
    intent: str = "CODE_QA",
    sub_intent: str = "EXPLAIN",
    answer_mode: str = "normal",
    path_scope: list[str] | None = None,
) -> CodeQAPipelineResult:
    from app.modules.rag.code_qa_pipeline.contracts import (
        EvidenceBundle,
        RetrievalRequest,
        RetrievalResult,
    )

    filters = CodeRetrievalFilters(path_scope=path_scope or [])
    router_result = IntentRouterResult(
        intent=intent,
        graph_id="CodeQAGraph",
        retrieval_profile="code",
        conversation_mode="START",
        query_plan=QueryPlan(raw="", normalized="", sub_intent=sub_intent),
        retrieval_spec=RetrievalSpec(filters=filters),
        symbol_resolution=SymbolResolution(),
        evidence_policy=EvidencePolicy(),
    )
    req = RetrievalRequest(rag_session_id="", query="", sub_intent=sub_intent, path_scope=path_scope or [])
    res = RetrievalResult()
    bundle = EvidenceBundle(resolved_sub_intent=sub_intent, evidence_count=1)
    return CodeQAPipelineResult(
        user_query="",
        rag_session_id="",
        router_result=router_result,
        retrieval_request=req,
        retrieval_result=res,
        evidence_bundle=bundle,
        evidence_gate_passed=(answer_mode == "normal"),
        answer_synthesis_input=None,
        diagnostics_report=None,
        answer_mode=answer_mode,
        timings_ms={},
    )
