"""Ziyaretçi tanıma + profil + kimlik merge + KVKK anonimleştirme.

Moat mantığı burada. Tenant izolasyonu mutlak — her sorgu tenant_id ile.
"""
import secrets
from datetime import UTC, datetime
from uuid import uuid4

from fastapi import Depends

from app.models.visitor import FlovyVisitor
from app.repositories.chat_repo import ChatRepository
from app.repositories.visitor_repo import VisitorRepository
from app.schemas.visitor import (
    IdentifyRequest,
    IdentifyResponse,
    VisitorProfileResponse,
)
from app.services.visitor.fingerprint import compute_fingerprint, detect_device_type
from app.utils.exceptions import NotFoundError, ValidationError

_MAX_IP_HISTORY = 20


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


def _merge_list_by_id(a: list | None, b: list | None) -> list:
    out: list = list(a or [])
    seen = {item.get("id") for item in out if isinstance(item, dict)}
    for item in b or []:
        key = item.get("id") if isinstance(item, dict) else item
        if key not in seen:
            out.append(item)
            seen.add(key)
    return out


class VisitorService:
    def __init__(
        self,
        repo: VisitorRepository = Depends(),
        chat_repo: ChatRepository = Depends(),
    ):
        self.repo = repo
        self.chat_repo = chat_repo

    # ---------------- identify ----------------

    async def identify(
        self,
        tenant_id: str,
        body: IdentifyRequest,
        ip: str | None = None,
        user_agent: str | None = None,
    ) -> IdentifyResponse:
        fingerprint = body.fingerprint_hash or compute_fingerprint(body.fingerprint_signals)
        device_type = body.device_type or detect_device_type(user_agent)

        visitor = None
        if body.visitor_uuid:
            visitor = await self.repo.get_by_uuid(tenant_id, body.visitor_uuid)
        if visitor is None and fingerprint:
            # fingerprint yardımcı sinyal — tek başına IP ile merge tetiklenmez
            visitor = await self.repo.get_by_fingerprint(tenant_id, fingerprint)

        if visitor is None:
            visitor = await self.repo.create(
                tenant_id=tenant_id,
                visitor_uuid=body.visitor_uuid or uuid4().hex,
                fingerprint_hash=fingerprint,
                ip_address=ip,
                ip_history=[ip] if ip else [],
                device_type=device_type,
                browser=body.browser,
                language=body.language,
                is_returning=False,
            )
        else:
            visitor = await self.repo.update(
                visitor, **self._touch_fields(visitor, ip, fingerprint, device_type, body)
            )

        return IdentifyResponse(
            visitor_id=visitor.id,
            visitor_uuid=visitor.visitor_uuid,
            is_returning=visitor.is_returning,
        )

    def _touch_fields(
        self,
        visitor: FlovyVisitor,
        ip: str | None,
        fingerprint: str | None,
        device_type: str | None,
        body: IdentifyRequest,
    ) -> dict:
        ip_hist = list(visitor.ip_history or [])
        if ip and ip not in ip_hist:
            ip_hist = (ip_hist + [ip])[-_MAX_IP_HISTORY:]
        return {
            "ip_address": ip or visitor.ip_address,
            "ip_history": ip_hist,
            "fingerprint_hash": visitor.fingerprint_hash or fingerprint,
            "device_type": visitor.device_type or device_type,
            "browser": visitor.browser or body.browser,
            "language": visitor.language or body.language,
        }

    # ---------------- session sayaçları ----------------

    async def register_session(self, visitor_id: str, tenant_id: str) -> None:
        """Sohbet açılınca çağrılır — total_sessions++ ve is_returning hesapla."""
        visitor = await self.repo.get(visitor_id, tenant_id)
        if not visitor:
            return
        await self.repo.update(
            visitor,
            total_sessions=visitor.total_sessions + 1,
            last_session_at=_now(),
            is_returning=visitor.total_sessions >= 1,
        )

    # ---------------- kimlik merge (email/telefon) ----------------

    async def merge_identity(
        self,
        visitor_id: str,
        tenant_id: str,
        email: str | None = None,
        phone: str | None = None,
        name: str | None = None,
        kvkk_consent: bool = False,
    ) -> VisitorProfileResponse:
        visitor = await self.repo.get(visitor_id, tenant_id)
        if not visitor:
            raise NotFoundError("Ziyaretçi")
        if (email or phone or name) and not kvkk_consent:
            raise ValidationError("Kişisel veri saklamak için KVKK onayı gerekli.")

        # Aynı email/telefon başka anonim profile bağlı mı?
        existing = await self.repo.find_by_email_or_phone(tenant_id, email, phone)
        if existing and existing.id != visitor.id:
            merged = self._merge_profiles(source=existing, target=visitor)
            await self.chat_repo.reassign_visitor(tenant_id, existing.id, visitor.id)
            await self.repo.delete(existing)
            await self.repo.update(visitor, **merged)

        await self.repo.update(
            visitor,
            email=email or visitor.email,
            phone=phone or visitor.phone,
            name=name or visitor.name,
            kvkk_consent_at=visitor.kvkk_consent_at or (_now() if kvkk_consent else None),
        )
        return VisitorProfileResponse.model_validate(visitor)

    def _merge_profiles(self, source: FlovyVisitor, target: FlovyVisitor) -> dict:
        return {
            "total_sessions": target.total_sessions + source.total_sessions,
            "total_messages": target.total_messages + source.total_messages,
            "lifetime_value": (target.lifetime_value or 0) + (source.lifetime_value or 0),
            "products_viewed": _merge_list_by_id(target.products_viewed, source.products_viewed),
            "products_purchased": _merge_list_by_id(
                target.products_purchased, source.products_purchased
            ),
            "interests": _merge_list_by_id(target.interests, source.interests),
            "is_returning": True,
            "cart_abandoned": target.cart_abandoned or source.cart_abandoned,
        }

    # ---------------- profil + KVKK ----------------

    async def get_profile(self, visitor_id: str, tenant_id: str) -> VisitorProfileResponse:
        visitor = await self.repo.get(visitor_id, tenant_id)
        if not visitor:
            raise NotFoundError("Ziyaretçi")
        return VisitorProfileResponse.model_validate(visitor)

    async def forget(self, visitor_id: str, tenant_id: str) -> None:
        """'Verimi sil' — PII NULL'lanır, fingerprint randomize, ürün geçmişi temizlenir."""
        visitor = await self.repo.get(visitor_id, tenant_id)
        if not visitor:
            raise NotFoundError("Ziyaretçi")
        await self.repo.update(
            visitor,
            email=None,
            phone=None,
            name=None,
            fingerprint_hash=secrets.token_hex(32),
            ip_address=None,
            ip_history=[],
            products_viewed=[],
            products_purchased=[],
            cart_abandoned=None,
            interests=[],
            anonymized_at=_now(),
        )
