"""FlovyService — AI orkestratör. Router → RAG → Memory → Prompt → LLM → Parser.

ARCHITECTURE §14: GemmaClient yalnız buradan çağrılır. Bu servis sohbet DB yazımı
YAPMAZ; AIResult döner, persistans ChatService'tedir.
NOT: Tool-use loop (search/cart/pay) Adım 8'de bağlanır; şimdilik marker'lar +
düz LLM metni + RAG/memory context.
"""
import logging
from dataclasses import dataclass, field

from fastapi import Depends

from app.config import settings
from app.models.chat import ChatSession
from app.repositories.chat_repo import ChatRepository
from app.repositories.tenant_repo import TenantRepository
from app.repositories.visitor_repo import VisitorRepository
from app.repositories.widget_repo import WidgetRepository
from app.services.ai.gemma_client import GemmaClient, get_gemma_client
from app.services.ai.intent_router import IntentRouter
from app.services.ai.prompt_builder import PromptBuilder
from app.services.ai.response_parser import ResponseParser
from app.services.product_service import ProductService
from app.services.rag.knowledge_retriever import KnowledgeRetriever
from app.services.visitor.memory_builder import MemoryBuilder
from app.utils.exceptions import ExternalServiceError

logger = logging.getLogger("flovy")

_HISTORY_LIMIT = 10
_FALLBACK = (
    "Şu anda yapay zekâ ile yanıt veremiyorum, "
    "dilerseniz sizi bir temsilciye bağlayabilirim."
)


@dataclass
class AIResult:
    reply_text: str | None = None
    ui_blocks: list[dict] = field(default_factory=list)
    escalate: bool = False
    clear_cart: bool = False
    intent: str | None = None
    layer: str = "llm"
    model: str | None = None


def _carousel(products: list[dict]) -> dict:
    items = []
    for p in products:
        images = p.get("images") or []
        items.append(
            {
                "id": p["id"],
                "title": p["title"],
                "price": p["price"],
                "compare_at_price": p.get("compare_at_price"),
                "stock_status": p.get("stock_status"),
                "stock_quantity": p.get("stock_quantity"),
                "image": images[0] if images else None,
                "has_variants": bool(p.get("variants")),
                "variants": p.get("variants") or [],
            }
        )
    return {"type": "product_carousel", "products": items}


class FlovyService:
    def __init__(
        self,
        intent_router: IntentRouter = Depends(),
        retriever: KnowledgeRetriever = Depends(),
        product_service: ProductService = Depends(),
        widget_repo: WidgetRepository = Depends(),
        visitor_repo: VisitorRepository = Depends(),
        tenant_repo: TenantRepository = Depends(),
        chat_repo: ChatRepository = Depends(),
        gemma: GemmaClient = Depends(get_gemma_client),
        prompt_builder: PromptBuilder = Depends(),
        response_parser: ResponseParser = Depends(),
    ):
        self.intent_router = intent_router
        self.retriever = retriever
        self.product_service = product_service
        self.widget_repo = widget_repo
        self.visitor_repo = visitor_repo
        self.tenant_repo = tenant_repo
        self.chat_repo = chat_repo
        self.gemma = gemma
        self.prompt_builder = prompt_builder
        self.response_parser = response_parser

    async def respond(self, session: ChatSession, user_message: str) -> AIResult:
        tenant_id = session.tenant_id
        widget = await self.widget_repo.get(session.widget_id, tenant_id)

        decision = await self.intent_router.decide(user_message)

        if decision.reply is not None:
            result = await self._handle_marker_or_text(
                decision.reply, tenant_id, user_message, widget
            )
            result.intent = decision.intent
            result.layer = decision.layer
            return result

        return await self._handle_llm(session, user_message, widget, tenant_id)

    # ---------------- rule/semantic ----------------

    async def _handle_marker_or_text(
        self, reply: str, tenant_id: str, user_message: str, widget
    ) -> AIResult:
        if reply == "__CATALOG__":
            return await self._catalog(tenant_id, query=None)
        if reply == "__SEARCH_PRODUCTS__":
            return await self._catalog(tenant_id, query=user_message)
        if reply == "__ESCALATE__":
            return AIResult(
                reply_text="Sizi bir müşteri temsilcisine bağlıyorum, lütfen bekleyin.",
                escalate=True,
            )
        if reply == "__CART_CLEAR__":
            return AIResult(reply_text="Sepetiniz temizlendi.", clear_cart=True)
        if reply == "__INTRO__":
            brand = self._brand_name(widget)
            return AIResult(
                reply_text=f"Ben {brand} dijital satış asistanıyım. Ürün önerebilir, "
                "sorularınızı yanıtlayabilir ve sipariş vermenize yardımcı olabilirim."
            )
        # düz hazır cevap
        return AIResult(reply_text=reply)

    async def _catalog(self, tenant_id: str, query: str | None) -> AIResult:
        products = await self.product_service.search_products(
            tenant_id, query=query, limit=8
        )
        if not products:
            return AIResult(reply_text="Bu kritere uygun ürün bulamadım.")
        return AIResult(
            reply_text="İşte size uygun ürünler:",
            ui_blocks=[_carousel(products)],
        )

    # ---------------- llm ----------------

    async def _handle_llm(
        self, session: ChatSession, user_message: str, widget, tenant_id: str
    ) -> AIResult:
        if widget is None or not widget.ai_enabled:
            return AIResult(reply_text=_FALLBACK, escalate=True)

        tenant = await self.tenant_repo.get_by_id(tenant_id)
        if tenant and tenant.monthly_message_used >= tenant.monthly_message_quota:
            return AIResult(
                reply_text="Aylık yanıt kotamız doldu, sizi bir temsilciye bağlayabilirim.",
                escalate=True,
            )

        try:
            rag_context = await self._rag_context(tenant_id, user_message)
            memory_context = await self._memory_context(session)
            history = await self.chat_repo.list_messages(
                session.id, tenant_id, limit=_HISTORY_LIMIT
            )
            system = self.prompt_builder.build_system(
                self._brand_name(widget),
                widget.persona,
                memory_context=memory_context,
                rag_context=rag_context,
            )
            contents = self.prompt_builder.build_contents(history, user_message)
            raw = await self.gemma.generate(contents, system_instruction=system)
            parsed = self.response_parser.parse(raw)
        except ExternalServiceError as e:
            logger.warning("LLM yanıt veremedi: %s", e.code)
            return AIResult(reply_text=_FALLBACK)

        if tenant:
            await self.tenant_repo.update(
                tenant, monthly_message_used=tenant.monthly_message_used + 1
            )

        text = parsed.text or "Bu konuda size nasıl yardımcı olabilirim?"
        # TODO(Adım8): parsed.tool_calls → ToolRegistry.execute (search/cart/pay/lead)
        return AIResult(reply_text=text, model=settings.gemma_model, layer="llm")

    # ---------------- helpers ----------------

    async def _rag_context(self, tenant_id: str, query: str) -> str:
        results = await self.retriever.top_k(tenant_id, query, k=5)
        snippets = [r["content"] for r in results if r.get("score", 0) > 0.3]
        return "\n---\n".join(snippets[:5])

    async def _memory_context(self, session: ChatSession) -> str:
        if not session.visitor_id:
            return ""
        visitor = await self.visitor_repo.get(session.visitor_id, session.tenant_id)
        return MemoryBuilder().build(visitor)

    def _brand_name(self, widget) -> str:
        if widget and widget.settings:
            return widget.settings.get("brand_name") or widget.name
        return widget.name if widget else "mağaza"
