"""FindResult — resultado final de una busqueda completa agrupado por tipo de contexto."""

from __future__ import annotations

from dataclasses import dataclass, field
from typing import Iterator

from matched_context import MatchedContext
from query_plan import QueryPlan
from query_result import QueryResult


@dataclass
class FindResult:
    """Resultado final de una busqueda completa, agrupado por tipo de contexto.

    Opcionalmente incluye provenance (traza de busqueda) para observabilidad.

    Attributes:
        memories:       Contextos encontrados de tipo MEMORY.
        resources:      Contextos encontrados de tipo RESOURCE.
        skills:         Contextos encontrados de tipo SKILL.
        query_plan:     Plan de busqueda que genero estos resultados (provenance).
        query_results:  Resultados individuales por TypedQuery (provenance).
        total:          Total de contextos encontrados (calculado en __post_init__).
    """

    memories: list[MatchedContext] = field(default_factory=list)
    resources: list[MatchedContext] = field(default_factory=list)
    skills: list[MatchedContext] = field(default_factory=list)
    query_plan: QueryPlan | None = None
    query_results: list[QueryResult] | None = None
    total: int = 0

    def __post_init__(self) -> None:
        """Calcula el total de contextos encontrados."""
        self.total = len(self.memories) + len(self.resources) + len(self.skills)

    def __iter__(self) -> Iterator[MatchedContext]:
        """Itera sobre todos los MatchedContext (memories + resources + skills)."""
        yield from self.memories
        yield from self.resources
        yield from self.skills

    def to_dict(self, include_provenance: bool = False) -> dict:
        """Serializa el resultado a dict.

        Args:
            include_provenance: Si True, incluye query_plan y query_results en el output.

        Returns:
            dict con memories, resources, skills y total. Opcionalmente con provenance.
        """

        def _context_to_dict(ctx: MatchedContext) -> dict:
            return {
                "uri": ctx.uri,
                "context_type": ctx.context_type.value,
                "level": ctx.level,
                "abstract": ctx.abstract,
                "overview": ctx.overview,
                "category": ctx.category,
                "score": ctx.score,
                "match_reason": ctx.match_reason,
                "relations": [
                    {"uri": r.uri, "abstract": r.abstract} for r in ctx.relations
                ],
            }

        result: dict = {
            "memories": [_context_to_dict(c) for c in self.memories],
            "resources": [_context_to_dict(c) for c in self.resources],
            "skills": [_context_to_dict(c) for c in self.skills],
            "total": self.total,
        }

        if include_provenance and self.query_plan is not None:
            result["query_plan"] = {
                "session_context": self.query_plan.session_context,
                "reasoning": self.query_plan.reasoning,
                "queries": [
                    {
                        "query": q.query,
                        "context_type": q.context_type.value if q.context_type else None,
                        "intent": q.intent,
                        "priority": q.priority,
                        "target_directories": q.target_directories,
                    }
                    for q in self.query_plan.queries
                ],
            }

        if include_provenance and self.query_results is not None:
            result["query_results"] = [
                {
                    "query": qr.query.query,
                    "matched_count": len(qr.matched_contexts),
                    "searched_directories": qr.searched_directories,
                }
                for qr in self.query_results
            ]

        return result

    @classmethod
    def from_dict(cls, data: dict) -> FindResult:
        """Deserializa un FindResult desde un dict (tipicamente JSON parseado).

        Args:
            data: dict con estructura equivalente a la producida por to_dict().

        Returns:
            FindResult reconstruido. Los campos de provenance se omiten (None).
        """
        from context_type import ContextType
        from related_context import RelatedContext

        def _dict_to_context(d: dict) -> MatchedContext:
            return MatchedContext(
                uri=d["uri"],
                context_type=ContextType(d["context_type"]),
                level=d.get("level", 2),
                abstract=d.get("abstract", ""),
                overview=d.get("overview"),
                category=d.get("category", ""),
                score=d.get("score", 0.0),
                match_reason=d.get("match_reason", ""),
                relations=[
                    RelatedContext(uri=r["uri"], abstract=r.get("abstract", ""))
                    for r in d.get("relations", [])
                ],
            )

        return cls(
            memories=[_dict_to_context(c) for c in data.get("memories", [])],
            resources=[_dict_to_context(c) for c in data.get("resources", [])],
            skills=[_dict_to_context(c) for c in data.get("skills", [])],
        )
