from __future__ import annotations

from copy import deepcopy
from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.models import PipelineResult


class PipelineDiagnosticsBuilder:
    def build(self, result: PipelineResult) -> dict:
        payload = deepcopy(result.diagnostics or {})
        router_plan = dict(payload.get("router_plan") or {})
        execution = dict(payload.get("execution") or {})
        retrieval_block = payload.get("retrieval")

        payload["router"] = {
            "conversation_mode": result.conversation_mode,
            "keyword_hints": list(router_plan.get("keyword_hints") or []),
            "path_scope": list(router_plan.get("path_scope") or []),
        }
        payload["llm"] = {
            "used_evidence_count": len(result.rag_rows),
            "missing_evidence_reason": self._missing_evidence_reason(result),
        }

        if isinstance(retrieval_block, dict):
            retrieval_block["symbol_resolution"] = {
                "status": str(result.symbol_resolution.get("status") or "not_requested"),
                "resolved_symbol": result.symbol_resolution.get("resolved_symbol"),
                "alternatives": list(result.symbol_resolution.get("alternatives") or []),
            }
            retrieval_block["layers"] = self._layer_diagnostics(result, execution)

        return payload

    def _layer_diagnostics(self, result: PipelineResult, execution: dict) -> list[dict]:
        layers = list(execution.get("executed_layers") or [])
        counts = self._layer_hit_counts(result.rag_rows)
        fallback = {}
        retrieval = result.diagnostics.get("retrieval")
        if isinstance(retrieval, dict):
            fallback = dict(retrieval.get("fallback") or {})
        reason = fallback.get("reason")

        output: list[dict] = []
        for layer in layers:
            hit_count = int(counts.get(str(layer), 0))
            output.append(
                {
                    "layer": str(layer),
                    "status": "hit" if hit_count > 0 else "miss",
                    "hit_count": hit_count,
                    "fail_reason": reason if hit_count == 0 else None,
                }
            )
        return output

    def _layer_hit_counts(self, rows: list[dict]) -> dict[str, int]:
        counts: dict[str, int] = {}
        for row in rows:
            layer = str(row.get("layer") or "")
            counts[layer] = counts.get(layer, 0) + 1
        return counts

    def _missing_evidence_reason(self, result: PipelineResult) -> str | None:
        if result.mode != "full_chain":
            return None
        answer_policy = dict(result.diagnostics.get("answer_policy") or {})
        failure_reason = str(answer_policy.get("failure_reason") or "").strip()
        if failure_reason:
            return failure_reason
        if result.rag_rows:
            return None
        return "empty_retrieval_context"


class PipelineSummaryBuilder:
    def build(self, result: PipelineResult, diagnostics: dict) -> dict:
        router_plan = dict(diagnostics.get("router_plan") or {})
        retrieval_profile = router_plan.get("retrieval_profile")
        layers_hit = self._layers_hit(result)
        evidence_sufficient = len(result.rag_rows) > 0
        answer_status = self._answer_status(result, evidence_sufficient)

        return {
            "router": {
                "intent": result.intent,
                "sub_intent": router_plan.get("sub_intent"),
                "confidence": None,
            },
            "retrieval": {
                "profile": retrieval_profile,
                "layers_hit": layers_hit,
                "evidence_sufficient": evidence_sufficient,
            },
            "llm": {
                "answer_status": answer_status,
                "groundedness": self._groundedness(result, evidence_sufficient),
            },
        }

    def _layers_hit(self, result: PipelineResult) -> list[str]:
        layers = {str(row.get("layer") or "") for row in result.rag_rows if str(row.get("layer") or "")}
        return sorted(layers)

    def _answer_status(self, result: PipelineResult, evidence_sufficient: bool) -> str:
        if result.mode != "full_chain":
            return "partial"
        answer_policy = dict(result.diagnostics.get("answer_policy") or {})
        policy_mode = str(answer_policy.get("answer_mode") or "").strip()
        if policy_mode:
            return policy_mode
        if not (result.llm_answer or "").strip():
            return "failed"
        if not evidence_sufficient:
            return "insufficient_evidence"
        return "answered"

    def _groundedness(self, result: PipelineResult, evidence_sufficient: bool) -> str:
        if result.mode != "full_chain":
            return "not_applicable"
        if not (result.llm_answer or "").strip():
            return "speculative"
        if evidence_sufficient:
            return "grounded"
        return "weakly_grounded"
