from __future__ import annotations

from dataclasses import dataclass

from tests.pipeline_setup.suite_02_pipeline.pipeline_intent_rag.helpers.models import PipelineResult


@dataclass(slots=True, frozen=True)
class AnswerPolicyDecision:
    answer: str
    answer_mode: str
    failure_reason: str = ""


class FullChainAnswerPolicy:
    def decide(self, result: PipelineResult) -> AnswerPolicyDecision | None:
        sub_intent = _sub_intent(result)
        if sub_intent == "OPEN_FILE":
            return self._missing_file(result)
        if sub_intent == "EXPLAIN":
            return self._missing_symbol(result)
        if sub_intent == "FIND_ENTRYPOINTS":
            return self._confirmed_entrypoints(result)
        return None

    def _missing_file(self, result: PipelineResult) -> AnswerPolicyDecision | None:
        requested_path = _requested_path(result)
        if not requested_path or _has_exact_file_match(result, requested_path):
            return None
        return AnswerPolicyDecision(
            answer=f"Файл {requested_path} не найден.",
            answer_mode="degraded",
            failure_reason="file_not_found",
        )

    def _missing_symbol(self, result: PipelineResult) -> AnswerPolicyDecision | None:
        symbol_resolution = dict(result.symbol_resolution or {})
        if str(symbol_resolution.get("status") or "").strip() != "not_found":
            return None
        symbol = _requested_symbol(result)
        if not symbol:
            return None
        answer = f"Сущность {symbol} не найдена в доступном коде."
        alternatives = [str(item).strip() for item in symbol_resolution.get("alternatives") or [] if str(item).strip()]
        if alternatives:
            answer = f"{answer} Близкие совпадения: {', '.join(alternatives[:3])}."
        return AnswerPolicyDecision(
            answer=answer,
            answer_mode="degraded",
            failure_reason="symbol_not_found",
        )

    def _confirmed_entrypoints(self, result: PipelineResult) -> AnswerPolicyDecision | None:
        routes = _confirmed_routes(result)
        if not routes:
            return None
        lines = [_render_route(route) for route in routes]
        answer = "\n".join(f"- {line}" for line in lines)
        if len(lines) == 1:
            answer = lines[0]
        return AnswerPolicyDecision(answer=answer, answer_mode="answered")


def _sub_intent(result: PipelineResult) -> str:
    router_plan = dict(result.diagnostics.get("router_plan") or {})
    return str(router_plan.get("sub_intent") or "").strip().upper()


def _requested_path(result: PipelineResult) -> str:
    router_plan = dict(result.diagnostics.get("router_plan") or {})
    path_scope = tuple(str(item).strip() for item in router_plan.get("path_scope") or [] if str(item).strip())
    return path_scope[0] if path_scope else ""


def _has_exact_file_match(result: PipelineResult, requested_path: str) -> bool:
    requested = requested_path.strip().lower()
    for row in result.rag_rows:
        row_path = str(row.get("path") or "").strip().lower()
        if row_path == requested:
            return True
    return False


def _requested_symbol(result: PipelineResult) -> str:
    router_plan = dict(result.diagnostics.get("router_plan") or {})
    symbols = tuple(str(item).strip() for item in router_plan.get("symbol_candidates") or [] if str(item).strip())
    return symbols[0] if symbols else ""


def _confirmed_routes(result: PipelineResult) -> list[dict]:
    seen: set[tuple[str, str]] = set()
    items: list[dict] = []
    for row in result.rag_rows:
        if str(row.get("layer") or "") != "C3_ENTRYPOINTS":
            continue
        metadata = dict(row.get("metadata") or {})
        method = str(metadata.get("http_method") or "").strip()
        route_path = str(metadata.get("route_path") or "").strip()
        if not method or not route_path:
            continue
        key = (method, route_path)
        if key in seen:
            continue
        seen.add(key)
        items.append(metadata)
    return items


def _render_route(metadata: dict) -> str:
    method = str(metadata.get("http_method") or "").strip()
    route_path = str(metadata.get("route_path") or "").strip()
    declaring = str(metadata.get("declaring_symbol") or "").strip()
    handler = str(metadata.get("handler_symbol") or "").strip()
    label = f"{method} {route_path}"
    parts = [label]
    if declaring:
        parts.append(f"объявлен в {declaring}")
    if handler:
        handler_name = handler.rsplit(".", 1)[-1]
        parts.append(f"обрабатывается {handler_name}")
    return " — ".join((parts[0], ", ".join(parts[1:]))) if len(parts) > 1 else parts[0]
