"""Chat iş mantığı — session aç/kapat, mesaj sakla/geçmiş, NPS, operatör devralma.

NOT: AI cevap üretimi + streaming (stream/init → Node.js) FlovyService orkestratörü
ile Adım 5-7'de bağlanır. Burada ziyaretçi mesajı kalıcı kaydedilir; AI tetikleme
o adımlarda send_visitor_message sonuna eklenecek.
"""
from datetime import UTC, datetime

from fastapi import Depends

from app.models.chat import ChatSession, ChatWidget
from app.repositories.chat_repo import ChatRepository
from app.schemas.chat import (
    ChatTurnResponse,
    CreateSessionRequest,
    MessageResponse,
    SessionResponse,
)
from app.schemas.visitor import IdentifyRequest
from app.services.ai.flovy_service import FlovyService
from app.services.visitor.visitor_service import VisitorService
from app.utils.exceptions import NotFoundError, ValidationError
from app.utils.security import generate_token


def _now() -> datetime:
    return datetime.now(UTC).replace(tzinfo=None)


class ChatService:
    def __init__(
        self,
        repo: ChatRepository = Depends(),
        visitor_service: VisitorService = Depends(),
        flovy_service: FlovyService = Depends(),
    ):
        self.repo = repo
        self.visitor_service = visitor_service
        self.flovy_service = flovy_service

    # ---------------- Widget tarafı ----------------

    async def create_session(
        self,
        widget: ChatWidget,
        body: CreateSessionRequest,
        ip: str | None = None,
        browser: str | None = None,
    ) -> SessionResponse:
        # 1. Ziyaretçiyi tanı (moat) — uuid/fingerprint varsa profili bul/oluştur
        visitor_id: str | None = None
        if body.visitor_uuid:
            ident = await self.visitor_service.identify(
                widget.tenant_id,
                IdentifyRequest(
                    visitor_uuid=body.visitor_uuid,
                    page_url=body.page_url,
                    referrer=body.referrer,
                ),
                ip=ip,
                user_agent=browser,
            )
            visitor_id = ident.visitor_id

        session = await self.repo.create_session(
            tenant_id=widget.tenant_id,
            widget_id=widget.id,
            visitor_id=visitor_id,
            session_token=generate_token(24),
            status="open",
            visitor_name=body.visitor_name,
            visitor_email=body.visitor_email,
            visitor_phone=body.visitor_phone,
            visitor_ip=ip,
            visitor_browser=browser,
            page_url=body.page_url,
            referrer=body.referrer,
            kvkk_consent_at=_now() if body.kvkk_consent else None,
        )

        # 2. Oturum sayacı + dönüş tespiti
        if visitor_id:
            await self.visitor_service.register_session(visitor_id, widget.tenant_id)
            # 3. Email/telefon + KVKK onayı geldiyse kimlik merge
            if (body.visitor_email or body.visitor_phone) and body.kvkk_consent:
                await self.visitor_service.merge_identity(
                    visitor_id,
                    widget.tenant_id,
                    email=body.visitor_email,
                    phone=body.visitor_phone,
                    name=body.visitor_name,
                    kvkk_consent=True,
                )

        return SessionResponse.model_validate(session)

    async def send_visitor_message(self, token: str, message: str) -> ChatTurnResponse:
        session = await self._get_open_session_by_token(token)
        visitor_msg = await self.repo.add_message(
            session_id=session.id,
            tenant_id=session.tenant_id,
            sender_type="visitor",
            message=message,
            message_type="text",
        )

        # AI orkestratörü — intent router → marker/RAG/LLM (Adım 7)
        # Operatör devraldıysa (escalated) AI tetiklenmez.
        if session.status == "escalated":
            return ChatTurnResponse(message=MessageResponse.model_validate(visitor_msg))

        result = await self.flovy_service.respond(session, message)

        session_updates: dict = {}
        if result.intent:
            session_updates["intent"] = result.intent
        if result.escalate:
            session_updates["status"] = "escalated"
            session_updates["escalated_at"] = _now()
        if result.clear_cart and session.session_metadata:
            meta = dict(session.session_metadata)
            meta.pop("cart", None)
            session_updates["session_metadata"] = meta
        if session_updates:
            session = await self.repo.update_session(session, **session_updates)

        reply_msg = await self.repo.add_message(
            session_id=session.id,
            tenant_id=session.tenant_id,
            sender_type="ai",
            message=result.reply_text or "",
            message_type="ai",
            meta={"ui_blocks": result.ui_blocks, "layer": result.layer},
            model=result.model,
        )

        return ChatTurnResponse(
            message=MessageResponse.model_validate(visitor_msg),
            reply=MessageResponse.model_validate(reply_msg),
            ui_blocks=result.ui_blocks,
            intent=result.intent,
            layer=result.layer,
            escalated=result.escalate,
        )

    async def get_history(self, token: str, limit: int = 100) -> list[dict]:
        session = await self._get_session_by_token(token)
        msgs = await self.repo.list_messages(session.id, session.tenant_id, limit)
        return [MessageResponse.model_validate(m).model_dump(mode="json") for m in msgs]

    async def close_session_by_token(self, token: str) -> None:
        session = await self._get_session_by_token(token)
        if session.status != "closed":
            await self.repo.update_session(session, status="closed", ended_at=_now())

    async def submit_nps(self, token: str, score: int, comment: str | None) -> None:
        session = await self._get_session_by_token(token)
        await self.repo.update_session(
            session, nps_score=score, nps_comment=comment, nps_submitted_at=_now()
        )

    async def idle_ping(self, token: str) -> None:
        session = await self._get_open_session_by_token(token)
        await self.repo.update_session(session, idle_ping_sent_at=_now())

    # ---------------- Panel tarafı ----------------

    async def list_sessions(
        self, tenant_id: str, status: str | None = None, limit: int = 50
    ) -> list[dict]:
        sessions = await self.repo.list_sessions(tenant_id, status, limit)
        return [SessionResponse.model_validate(s).model_dump(mode="json") for s in sessions]

    async def get_session_detail(self, session_id: str, tenant_id: str) -> dict:
        session = await self._get_session_or_404(session_id, tenant_id)
        msgs = await self.repo.list_messages(session.id, tenant_id)
        return {
            "session": SessionResponse.model_validate(session).model_dump(mode="json"),
            "messages": [
                MessageResponse.model_validate(m).model_dump(mode="json") for m in msgs
            ],
        }

    async def takeover(self, session_id: str, tenant_id: str) -> SessionResponse:
        session = await self._get_session_or_404(session_id, tenant_id)
        session = await self.repo.update_session(
            session, status="escalated", escalated_at=_now()
        )
        return SessionResponse.model_validate(session)

    async def operator_message(
        self, session_id: str, tenant_id: str, message: str
    ) -> MessageResponse:
        session = await self._get_session_or_404(session_id, tenant_id)
        msg = await self.repo.add_message(
            session_id=session.id,
            tenant_id=tenant_id,
            sender_type="operator",
            message=message,
            message_type="text",
        )
        return MessageResponse.model_validate(msg)

    async def close_session(self, session_id: str, tenant_id: str) -> None:
        session = await self._get_session_or_404(session_id, tenant_id)
        if session.status != "closed":
            await self.repo.update_session(session, status="closed", ended_at=_now())

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

    async def _get_session_by_token(self, token: str) -> ChatSession:
        session = await self.repo.get_session_by_token(token)
        if not session:
            raise NotFoundError("Oturum")
        return session

    async def _get_open_session_by_token(self, token: str) -> ChatSession:
        session = await self._get_session_by_token(token)
        if session.status == "closed":
            raise ValidationError("Bu sohbet kapatılmış.")
        return session

    async def _get_session_or_404(self, session_id: str, tenant_id: str) -> ChatSession:
        session = await self.repo.get_session(session_id, tenant_id)
        if not session:
            raise NotFoundError("Oturum")
        return session
