from __future__ import annotations

from datetime import datetime
from pathlib import Path

from app.modules.agent.code_qa_runtime import CodeQaRuntimeExecutor
from app.modules.agent.llm import AgentLlmService
from app.modules.agent.prompt_loader import PromptLoader
from app.modules.shared.gigachat.client import GigaChatClient
from app.modules.shared.gigachat.settings import GigaChatSettings
from app.modules.shared.gigachat.token_provider import GigaChatTokenProvider
from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.artifact_writer import ArtifactWriter
from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.diagnostics import clone_diagnostics
from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.models import PhraseCase, PipelineResult
from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.phrases_loader import PhraseCatalogLoader
from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.pipeline_config import PipelineRunConfig


class PipelineRuntime:
    def __init__(self, mode: str, test_name: str, test_root: Path) -> None:
        self._config = PipelineRunConfig.from_env(mode=mode, test_name=test_name, test_root=test_root)
        self._started_at = datetime.now()
        self._writer = ArtifactWriter(self._config.test_results_dir, test_name=self._config.test_name, run_started_at=self._started_at)
        self._rag_adapter = None
        self._session_resolver = None
        self._executor: CodeQaRuntimeExecutor | None = None

    @property
    def artifact_path(self) -> Path:
        return self._writer.path

    def load_cases(self) -> list[PhraseCase]:
        loader = PhraseCatalogLoader()
        cases = loader.filter_by_tag(loader.load(self._config.phrases_path), self._config.tag)
        if not cases:
            raise ValueError(f"No cases for tag={self._config.tag} in {self._config.phrases_path}")
        return cases

    def run_router_only(self, case: PhraseCase) -> PipelineResult:
        from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.pipeline_runner import RouterOnlyRunner

        result = RouterOnlyRunner(started_at=self._started_at).run_case(case)
        self._writer.write_record(result.to_record())
        return result

    def run_router_rag(self, case: PhraseCase) -> PipelineResult:
        result = self._run_router_rag_case(case)
        self._writer.write_record(result.to_record())
        return result

    def run_full_chain(self, case: PhraseCase) -> PipelineResult:
        result_payload = self._executor_instance().execute(user_query=case.text, rag_session_id=case.rag_session_id or "")
        route = dict(result_payload.diagnostics.router_result)
        retrieval = dict(result_payload.diagnostics.retrieval_request)
        diagnostics = {
            "router_plan": {
                "sub_intent": route.get("sub_intent"),
                "graph_id": route.get("graph_id"),
                "path_scope": list(retrieval.get("path_scope") or []),
                "layers": list(retrieval.get("requested_layers") or []),
                "symbol_candidates": list(result_payload.router_result.query_plan.symbol_candidates or []) if result_payload.router_result else [],
            },
            "timings_ms": dict(result_payload.diagnostics.timings_ms or {}),
            "answer_policy": {
                "short_circuit": not result_payload.llm_used,
                "answer_mode": _answer_status(result_payload.answer_mode, result_payload.llm_used),
                "failure_reason": ",".join(result_payload.validation.reasons),
            },
        }
        result = PipelineResult(
            case=case,
            mode="full_chain",
            run_started_at=self._started_at,
            rag_session_id=case.rag_session_id,
            intent=str(route.get("intent") or ""),
            graph_id=str(route.get("graph_id") or ""),
            conversation_mode=str(route.get("conversation_mode") or ""),
            query=str(retrieval.get("query") or case.text),
            rag_rows=list(result_payload.retrieval_result.raw_rows) if result_payload.retrieval_result else [],
            symbol_resolution=result_payload.router_result.symbol_resolution.model_dump() if result_payload.router_result else {},
            llm_answer=result_payload.final_answer,
            diagnostics=diagnostics,
        )
        self._writer.write_record(result.to_record())
        return result

    def _run_router_rag_case(self, case: PhraseCase) -> PipelineResult:
        from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.pipeline_runner import IntentRouterRagPipelineRunner

        rag_adapter, session_resolver = self._rag_components()
        runner = IntentRouterRagPipelineRunner(
            started_at=self._started_at,
            rag_adapter=rag_adapter,
            session_resolver=session_resolver,
        )
        return runner.run_case(case)

    def _rag_components(self):
        if self._rag_adapter is not None and self._session_resolver is not None:
            return self._rag_adapter, self._session_resolver
        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.suite_02_pipeline.pipeline_intent_rag.helpers.session_resolver import RagSessionResolver

        repository = RagRepository()
        self._rag_adapter = RagDbAdapter(repository=repository, dim_resolver=SessionEmbeddingDimensions())
        self._session_resolver = RagSessionResolver(config=self._config, repository=repository)
        return self._rag_adapter, self._session_resolver

    def _executor_instance(self) -> CodeQaRuntimeExecutor:
        if self._executor is None:
            self._executor = CodeQaRuntimeExecutor(_build_llm())
        return self._executor


def _ms(started: float) -> int:
    return 0


def _sub_intent_from_result(result: PipelineResult) -> str | None:
    router_plan = dict(result.diagnostics.get("router_plan") or {})
    value = str(router_plan.get("sub_intent") or "").strip()
    return value or None


def _answer_status(answer_mode: str, llm_used: bool) -> str:
    if answer_mode == "normal" and llm_used:
        return "answered"
    return answer_mode


def _build_llm() -> AgentLlmService:
    settings = GigaChatSettings.from_env()
    client = GigaChatClient(settings, GigaChatTokenProvider(settings))
    return AgentLlmService(client=client, prompts=PromptLoader())
