"""Run golden cases through CodeQAPipelineRunner and compare to expected."""

from __future__ import annotations

from dataclasses import dataclass, field
from pathlib import Path

from app.modules.rag.code_qa_pipeline import CodeQAPipelineResult, CodeQAPipelineRunner
from app.modules.rag.contracts.enums import RagLayer
from app.modules.rag.intent_router_v2 import ConversationState, IntentRouterV2, RepoContext

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


@dataclass(slots=True)
class EvalCaseResult:
    """Result of evaluating one golden case."""

    case: GoldenCase
    pipeline_result: CodeQAPipelineResult
    passed: bool
    mismatches: list[str] = field(default_factory=list)


def _repo_context() -> RepoContext:
    return RepoContext(
        languages=["python"],
        available_domains=["CODE", "DOCS"],
        available_layers=[
            RagLayer.CODE_ENTRYPOINTS,
            RagLayer.CODE_SYMBOL_CATALOG,
            RagLayer.CODE_DEPENDENCY_GRAPH,
            RagLayer.CODE_SEMANTIC_ROLES,
            RagLayer.CODE_SOURCE_CHUNKS,
            RagLayer.DOCS_MODULE_CATALOG,
            RagLayer.DOCS_FACT_INDEX,
            RagLayer.DOCS_SECTION_INDEX,
            RagLayer.DOCS_POLICY_INDEX,
        ],
    )


def run_eval(config: EvalConfig) -> list[EvalCaseResult]:
    """Index repo, run all golden cases through the pipeline, compare to expected. Returns list of EvalCaseResult."""
    from app.modules.rag.persistence.repository import RagRepository
    from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.rag_db_adapter import RagDbAdapter, SessionEmbeddingDimensions
    from tests.pipeline_setup.utils.rag_indexer import RagSessionIndexer

    repo = RagRepository()
    repo.ensure_tables()
    indexer = RagSessionIndexer(repo)
    rag_session_id = indexer.index_repo(config.repo_path, project_id=config.project_id)

    adapter = RagDbAdapter(repository=repo, dim_resolver=SessionEmbeddingDimensions())
    router = IntentRouterV2()
    runner = CodeQAPipelineRunner(
        router=router,
        retrieval_adapter=adapter,
        repo_context=_repo_context(),
    )

    cases = load_golden_cases(config.golden_cases_path)

    results: list[EvalCaseResult] = []
    for case in cases:
        pipeline_result = runner.run(case.query, rag_session_id, run_retrieval=True, run_hydrate=True)
        passed, mismatches = _compare(case, pipeline_result)
        results.append(EvalCaseResult(case=case, pipeline_result=pipeline_result, passed=passed, mismatches=mismatches))
    return results


def _compare(case: GoldenCase, result: CodeQAPipelineResult) -> tuple[bool, list[str]]:
    mismatches: list[str] = []
    rr = result.router_result
    sub_intent = (rr.query_plan.sub_intent if rr.query_plan else None) or ""

    if rr.intent != case.expected_intent:
        mismatches.append(f"intent: expected {case.expected_intent}, got {rr.intent}")
    if sub_intent != case.expected_sub_intent:
        mismatches.append(f"sub_intent: expected {case.expected_sub_intent}, got {sub_intent}")
    if result.answer_mode != case.expected_answer_mode:
        mismatches.append(f"answer_mode: expected {case.expected_answer_mode}, got {result.answer_mode}")

    if case.expected_path_scope_contains:
        path_scope = list(getattr(rr.retrieval_spec.filters, "path_scope", []) or [])
        for want in case.expected_path_scope_contains:
            if not any(want in p for p in path_scope):
                mismatches.append(f"path_scope should contain '{want}', got {path_scope}")
    if case.expected_symbol_candidates_contain:
        candidates = list(rr.query_plan.symbol_candidates or []) if rr.query_plan else []
        for want in case.expected_symbol_candidates_contain:
            if want not in candidates:
                mismatches.append(f"symbol_candidates should contain '{want}', got {candidates}")
    if case.expected_layers:
        layers = [str(q.layer_id) for q in (rr.retrieval_spec.layer_queries or [])]
        for want in case.expected_layers:
            if want not in layers:
                mismatches.append(f"layers should include '{want}', got {layers}")

    passed = len(mismatches) == 0
    return passed, mismatches
