from __future__ import annotations

import asyncio
from dataclasses import asdict

from app.core.agent.processes.v2 import V2IntentRouter
from app.core.rag.persistence.repository import RagRepository
from app.core.rag.retrieval.session_retriever import RagSessionRetriever
from app.core.agent.utils.process_v2.plan_resolver import V2RetrievalPolicyResolver
from app.core.agent.utils.process_v2.rag_retrieval import V2RagRetrievalAdapter
from tests.pipeline_setup_v3.shared.rag_indexer import DeterministicEmbedder
from tests.pipeline_setup_v4.core.models import ExecutionPayload, V4Case
from tests.pipeline_setup_v4.executors.process_v2_router_executor import _KeywordLlm


class ProcessV2RouterPlusPolicyRagExecutor:
    def __init__(self) -> None:
        self._router = V2IntentRouter(llm=_KeywordLlm(), enable_llm_disambiguation=True)
        self._resolver = V2RetrievalPolicyResolver()
        self._adapter = V2RagRetrievalAdapter(RagSessionRetriever(RagRepository(), DeterministicEmbedder()))

    def execute(self, case: V4Case) -> ExecutionPayload:
        if not case.rag_session_id:
            raise ValueError(f"Case '{case.case_id}' requires rag_session_id")
        return asyncio.run(self._execute_async(case))

    async def _execute_async(self, case: V4Case) -> ExecutionPayload:
        route = self._router.route(case.query)
        plan = self._resolver.resolve(route)
        rows = await self._adapter.fetch_rows(case.rag_session_id or "", route.normalized_query, plan)
        route_dump = asdict(route)
        rag_summary = _summarize_rows(rows)
        actual = {
            "domain": route.routing_domain,
            "intent": route.intent,
            "sub_intent": route.subintent,
            "profile": plan.profile,
            "layers": list(plan.layers),
            "limit": plan.limit,
            "filters": dict(plan.filters),
            "route": {
                "routing_domain": route.routing_domain,
                "intent": route.intent,
                "subintent": route.subintent,
                "target_terms": list(route.target_terms),
                "anchors": route_dump.get("anchors") or {},
            },
            "retrieval_plan": {
                "profile": plan.profile,
                "layers": list(plan.layers),
                "limit": plan.limit,
                "filters": dict(plan.filters),
            },
            "rag": rag_summary,
        }
        details = {
            "query": case.query,
            "rag_session_id": case.rag_session_id,
            "route": route_dump,
            "plan": actual["retrieval_plan"],
            "rag": {
                **rag_summary,
                "rows": rows[:20],
            },
        }
        return ExecutionPayload(actual=actual, details=details)


def _summarize_rows(rows: list[dict]) -> dict[str, object]:
    paths: list[str] = []
    layers: list[str] = []
    metadata_domains: list[str] = []
    metadata_subdomains: list[str] = []
    for row in rows:
        path = str(row.get("path") or "").strip()
        layer = str(row.get("layer") or "").strip()
        metadata = dict(row.get("metadata") or {})
        domain = str(metadata.get("domain") or "").strip()
        subdomain = str(metadata.get("subdomain") or "").strip()
        if path and path not in paths:
            paths.append(path)
        if layer and layer not in layers:
            layers.append(layer)
        if domain and domain not in metadata_domains:
            metadata_domains.append(domain)
        if subdomain and subdomain not in metadata_subdomains:
            metadata_subdomains.append(subdomain)
    return {
        "row_count": len(rows),
        "paths": paths,
        "layers": layers,
        "metadata_domains": metadata_domains,
        "metadata_subdomains": metadata_subdomains,
    }
