"""Phase 2: lightweight hybrid retriever (vector + graph)."""
from __future__ import annotations

from dataclasses import dataclass
from typing import List, Protocol

from .types import VectorMetadata, GraphEdge


class VectorStoreLike(Protocol):
    def search(self, query: str, top_k: int = 5) -> List[VectorMetadata]:
        ...


class GraphStoreLike(Protocol):
    def get_edges(self) -> List[GraphEdge]:
        ...


@dataclass
class HybridResult:
    id: str
    source: str
    score: float
    content: str


class HybridRetriever:
    """Naive hybrid retriever for Phase 2 demos/tests.

    Uses keyword search over vector metadata and simple graph edge matching
    with a Reciprocal Rank Fusion-style scoring.
    """

    def __init__(self, vector_store: VectorStoreLike, graph_store: GraphStoreLike):
        self.vector_store = vector_store
        self.graph_store = graph_store

    def retrieve(self, query: str, top_k: int = 5) -> List[HybridResult]:
        vector_hits = self.vector_store.search(query, top_k=top_k)
        graph_hits = self._graph_hits(query)

        fused = self._rrf(vector_hits, graph_hits, top_k=top_k)
        return fused

    def _graph_hits(self, query: str) -> List[GraphEdge]:
        q = query.lower()
        edges = []
        for edge in self.graph_store.get_edges():
            if edge.source.lower() in q or edge.target.lower() in q or edge.type.lower() in q:
                edges.append(edge)
        return edges

    def _rrf(
        self,
        vector_hits: List[VectorMetadata],
        graph_hits: List[GraphEdge],
        top_k: int,
    ) -> List[HybridResult]:
        scores = {}

        for rank, item in enumerate(vector_hits, start=1):
            scores[item.id] = scores.get(item.id, 0.0) + 1.0 / (60 + rank)

        for rank, edge in enumerate(graph_hits, start=1):
            edge_id = f"{edge.source}->{edge.type}->{edge.target}"
            scores[edge_id] = scores.get(edge_id, 0.0) + 1.0 / (60 + rank)

        results = []
        for id_, score in sorted(scores.items(), key=lambda x: x[1], reverse=True)[:top_k]:
            if "->" in id_:
                source = "graph"
                content = id_
            else:
                source = "vector"
                content = id_
            results.append(HybridResult(id=id_, source=source, score=round(score, 4), content=content))
        return results
