"""Katman 2 — Semantic router. Gemini embedding + cosine. Skor > 0.78 → fast-path.

Anchor embedding'leri Redis'te 7 gün cache'lenir (warm_anchors). Redis kapalıysa
her çağrıda yeniden embed edilir (graceful — [[env-setup]]).
"""
import hashlib
import json
import logging
from dataclasses import dataclass

from fastapi import Depends

from app.cache import cache
from app.services.rag.embedding_client import EmbeddingClient, get_embedding_client
from app.utils.cosine import cosine_similarity

logger = logging.getLogger("flovy")

SEMANTIC_THRESHOLD = 0.78
ANCHOR_CACHE_TTL = 7 * 24 * 3600

SEMANTIC_INTENTS: dict[str, dict] = {
    "catalog": {
        "reply": "__CATALOG__",
        "phrases": [
            "Ürünleriniz nelerdir",
            "Ne satıyorsunuz",
            "Kaç çeşit ürününüz var",
            "Ürün çeşitlerinizi görmek istiyorum",
        ],
    },
    "greeting": {
        "reply": "Merhaba! Size nasıl yardımcı olabilirim?",
        "phrases": ["Selamlar nasılsınız", "Günaydın efendim", "İyi günler size"],
    },
    "contact_request": {
        "reply": "__ESCALATE__",
        "phrases": [
            "Müşteri temsilcisine bağlanmak istiyorum",
            "Biriyle konuşabilir miyim",
            "İnsan desteği alabilir miyim",
        ],
    },
    "pricing_question": {
        "reply": "__SEARCH_PRODUCTS__",
        "phrases": ["Fiyatlarınız nedir", "Ne kadara satıyorsunuz", "En ucuz ürününüz hangisi"],
    },
    "who_are_you": {
        "reply": "__INTRO__",
        "phrases": ["Kendini tanıtır mısın", "Sen bir bot musun", "Kimle konuşuyorum"],
    },
}


@dataclass
class SemanticMatch:
    intent: str
    reply: str
    score: float


class SemanticRouter:
    def __init__(self, embedding: EmbeddingClient = Depends(get_embedding_client)):
        self.embedding = embedding

    async def _anchor_embedding(self, phrase: str) -> list[float]:
        key = f"flovy:anchor:{hashlib.md5(phrase.encode()).hexdigest()}"
        cached = await cache.get(key)
        if cached:
            return json.loads(cached)
        vec = await self.embedding.embed(phrase, task="RETRIEVAL_DOCUMENT")
        await cache.setex(key, ANCHOR_CACHE_TTL, json.dumps(vec))
        return vec

    async def match(self, message: str) -> SemanticMatch | None:
        msg_vec = await self.embedding.embed(message, task="RETRIEVAL_QUERY")

        best: SemanticMatch | None = None
        for intent, cfg in SEMANTIC_INTENTS.items():
            for phrase in cfg["phrases"]:
                anchor = await self._anchor_embedding(phrase)
                score = cosine_similarity(msg_vec, anchor)
                if best is None or score > best.score:
                    best = SemanticMatch(intent=intent, reply=cfg["reply"], score=score)

        if best and best.score >= SEMANTIC_THRESHOLD:
            return best
        return None
