"""Builds normalized RetrievalResult from raw retrieval rows and report."""

from __future__ import annotations

from app.modules.rag.code_qa_pipeline.contracts import (
    CodeChunkItem,
    LayerOutcome,
    RetrievalResult,
)
from app.modules.rag.retrieval.test_filter import is_test_path


def build_retrieval_result(
    raw_rows: list[dict],
    retrieval_report: dict | None,
    symbol_resolution: dict | None,
) -> RetrievalResult:
    """Convert raw adapter rows and optional report into normalized RetrievalResult."""
    report = retrieval_report or {}
    sym = symbol_resolution or {}
    layers_seen: set[str] = set()
    code_chunks: list[CodeChunkItem] = []
    relations: list[dict] = []
    entrypoints: list[dict] = []
    test_candidates: list[dict] = []
    file_candidates: list[str] = []
    target_symbols: list[str] = []

    for row in raw_rows:
        layer = str(row.get("layer") or "")
        path = str(row.get("path") or "")
        if layer:
            layers_seen.add(layer)
        if path and path not in file_candidates:
            file_candidates.append(path)

        chunk = CodeChunkItem(
            layer=layer,
            path=path,
            title=str(row.get("title") or ""),
            content=str(row.get("content") or ""),
            start_line=row.get("span_start"),
            end_line=row.get("span_end"),
            metadata=dict(row.get("metadata") or {}),
        )
        code_chunks.append(chunk)

        if layer == "C1_SYMBOL_CATALOG":
            title = str(row.get("title") or "").strip()
            if title and title not in target_symbols:
                target_symbols.append(title)
        elif layer == "C2_DEPENDENCY_GRAPH":
            relations.append({"path": path, "metadata": chunk.metadata, "content": chunk.content[:500]})
        elif layer == "C3_ENTRYPOINTS":
            entrypoints.append({"path": path, "title": chunk.title, "metadata": chunk.metadata})
        elif is_test_path(path):
            test_candidates.append({"path": path, "title": chunk.title, "content": chunk.content[:300]})

    executed = list(report.get("executed_layers") or [])
    missing_layers = [lid for lid in executed if lid not in layers_seen]
    layer_outcomes = [
        LayerOutcome(
            layer_id=layer_id,
            hit_count=sum(1 for r in raw_rows if str(r.get("layer") or "") == layer_id),
            empty=layer_id not in layers_seen,
            fallback_used=bool(
                (report.get("fallback") or {}).get("used")
                and report.get("retrieval_mode_by_layer", {}).get(layer_id)
            ),
        )
        for layer_id in executed
    ]
    if not executed and raw_rows:
        for layer in layers_seen:
            layer_outcomes.append(
                LayerOutcome(
                    layer_id=layer,
                    hit_count=sum(1 for r in raw_rows if str(r.get("layer") or "") == layer),
                    empty=False,
                    fallback_used=False,
                )
            )

    resolved = str(sym.get("resolved_symbol") or "").strip() or None
    status = str(sym.get("status") or "not_requested")

    return RetrievalResult(
        target_symbol_candidates=target_symbols,
        resolved_symbol=resolved,
        symbol_resolution_status=status,
        file_candidates=file_candidates,
        code_chunks=code_chunks,
        relations=relations,
        entrypoints=entrypoints,
        test_candidates=test_candidates,
        layer_outcomes=layer_outcomes,
        missing_layers=missing_layers,
        raw_rows=raw_rows,
        retrieval_report=report,
    )
