from __future__ import annotations

import asyncio
from dataclasses import dataclass
from datetime import datetime, timezone
from importlib import import_module
from threading import Lock
from typing import Any, Literal, Protocol

from cachetools import TTLCache

from common.dao.clip import ClipDAO
from common.dao.user import UserDAO
from common.logging import get_logger
from common.models.common import Platform

try:
    Posthog: Any | None = getattr(import_module("posthog"), "Posthog")
except ImportError:  # pragma: no cover - exercised when dependency is unavailable
    Posthog = None

logger = get_logger(__name__)

DEFAULT_POSTHOG_HOST = "https://us.i.posthog.com"
ACCOUNT_GROUP_TYPE = "account"
UserRole = Literal["owner", "tenant"]


def user_distinct_id(user_id: int) -> str:
    """Return the canonical PostHog distinct id for a user."""
    return f"user:{int(user_id)}"


def account_group_key(owner_user_id: int) -> str:
    """Return the canonical PostHog group key for an account."""
    return f"account:{int(owner_user_id)}"


def normalize_platform_name(platform: Any) -> str:
    """Normalize platform identifiers to the analytics taxonomy."""
    value = platform.value if isinstance(platform, Platform) else str(platform or "")
    normalized = value.strip().lower()
    if normalized == Platform.DIRECT.value:
        return "tldrdirect"
    return normalized


@dataclass(frozen=True)
class AnalyticsIdentityContext:
    """Identity context for analytics events."""

    actor_user_id: int
    owner_user_id: int
    user_role: UserRole = "owner"

    @classmethod
    def for_owner_account(
        cls, user_id: int, *, user_role: UserRole = "owner"
    ) -> "AnalyticsIdentityContext":
        return cls(
            actor_user_id=int(user_id), owner_user_id=int(user_id), user_role=user_role
        )

    @property
    def distinct_id(self) -> str:
        return user_distinct_id(self.actor_user_id)

    @property
    def account_group_key(self) -> str:
        return account_group_key(self.owner_user_id)

    @property
    def groups(self) -> dict[str, str]:
        return {ACCOUNT_GROUP_TYPE: self.account_group_key}


class AnalyticsEnrichmentResolver(Protocol):
    """Protocol for account-level event enrichment."""

    async def resolve(self, identity: AnalyticsIdentityContext) -> dict[str, Any]:
        """Return event properties to attach to the analytics event."""


class NoOpAnalyticsEnrichmentResolver:
    """Resolver that returns no enrichment."""

    async def resolve(self, identity: AnalyticsIdentityContext) -> dict[str, Any]:
        return {}


class AccountAnalyticsEnrichmentResolver:
    """Short-TTL account enrichment resolver for PostHog events."""

    def __init__(
        self,
        *,
        user_dao: UserDAO,
        clip_dao: ClipDAO,
        ttl_seconds: int = 60,
        maxsize: int = 2048,
    ) -> None:
        self._user_dao = user_dao
        self._clip_dao = clip_dao
        self._cache: TTLCache[int, dict[str, Any]] = TTLCache(
            maxsize=maxsize, ttl=ttl_seconds
        )
        self._lock = asyncio.Lock()

    async def resolve(self, identity: AnalyticsIdentityContext) -> dict[str, Any]:
        owner_user_id = int(identity.owner_user_id)
        cached = self._cache.get(owner_user_id)
        if cached is not None:
            return dict(cached)

        async with self._lock:
            cached = self._cache.get(owner_user_id)
            if cached is not None:
                return dict(cached)

            owner_user = await self._user_dao.get_user_by_id(owner_user_id)
            clips_generated_lifetime = await self._clip_dao.count_user_clips_lifetime(
                owner_user_id
            )

            connected_platforms = sorted(
                {
                    normalize_platform_name(Platform.TWITCH)
                    for account in [owner_user.twitch_account]
                    if account is not None
                }
                | {
                    normalize_platform_name(Platform.KICK)
                    for account in [owner_user.kick_account]
                    if account is not None
                }
                | {
                    normalize_platform_name(platform)
                    for platform in (owner_user.social_media_platforms or [])
                    if platform
                }
            )

            created_at = owner_user.created_at
            if created_at.tzinfo is None:
                created_at = created_at.replace(tzinfo=timezone.utc)
            else:
                created_at = created_at.astimezone(timezone.utc)

            enrichment = {
                "connected_platforms": connected_platforms,
                "clips_generated_lifetime": int(clips_generated_lifetime),
                "account_age_days": max(
                    0, (datetime.now(timezone.utc) - created_at).days
                ),
            }
            self._cache[owner_user_id] = dict(enrichment)
            return enrichment

    def invalidate(self, owner_user_id: int) -> None:
        """Invalidate a cached account enrichment record."""
        self._cache.pop(int(owner_user_id), None)


class _NoOpPosthogClient:
    """Small no-op client used when PostHog is disabled or unconfigured."""

    disable_geoip = True

    def capture(self, *args: Any, **kwargs: Any) -> None:
        return None

    def set(self, *args: Any, **kwargs: Any) -> None:
        return None

    def set_once(self, *args: Any, **kwargs: Any) -> None:
        return None

    def group_identify(self, *args: Any, **kwargs: Any) -> None:
        return None

    def shutdown(self) -> None:
        return None


class AnalyticsTracker:
    """Thin PostHog wrapper that centralizes event identity and enrichment."""

    def __init__(
        self,
        *,
        service_name: str,
        client: Any | None,
        enrichment_resolver: AnalyticsEnrichmentResolver | None = None,
        enabled: bool = False,
    ) -> None:
        self.service_name = service_name
        self._client = client or _NoOpPosthogClient()
        self._enrichment_resolver = (
            enrichment_resolver or NoOpAnalyticsEnrichmentResolver()
        )
        self.enabled = bool(enabled and client is not None)

    async def _core_properties(
        self, identity: AnalyticsIdentityContext
    ) -> dict[str, Any]:
        enrichment = await self._enrichment_resolver.resolve(identity)
        return {
            **enrichment,
            "user_id": int(identity.actor_user_id),
            "user_role": identity.user_role,
            "account_id": int(identity.owner_user_id),
        }

    async def capture(
        self,
        event: str,
        *,
        identity: AnalyticsIdentityContext,
        properties: dict[str, Any] | None = None,
    ) -> None:
        if not self.enabled:
            return

        try:
            payload = {
                **(properties or {}),
                **(await self._core_properties(identity)),
            }
            self._client.capture(
                event,
                distinct_id=identity.distinct_id,
                properties=payload,
                groups=identity.groups,
                disable_geoip=True,
            )
        except Exception as exc:  # pragma: no cover - defensive logging
            logger.warning(
                "PostHog capture failed",
                extra={
                    "service": self.service_name,
                    "event": event,
                    "distinct_id": identity.distinct_id,
                    "error": str(exc),
                },
            )

    async def identify(
        self,
        *,
        identity: AnalyticsIdentityContext,
        properties: dict[str, Any] | None = None,
        set_once: dict[str, Any] | None = None,
    ) -> None:
        if not self.enabled:
            return

        try:
            merged_properties = {
                **(properties or {}),
                **(await self._core_properties(identity)),
            }
            self._client.set(
                distinct_id=identity.distinct_id,
                properties=merged_properties,
                disable_geoip=True,
            )
            if set_once:
                self._client.set_once(
                    distinct_id=identity.distinct_id,
                    properties=set_once,
                    disable_geoip=True,
                )
        except Exception as exc:  # pragma: no cover - defensive logging
            logger.warning(
                "PostHog identify failed",
                extra={
                    "service": self.service_name,
                    "distinct_id": identity.distinct_id,
                    "error": str(exc),
                },
            )

    async def group_identify(
        self,
        *,
        identity: AnalyticsIdentityContext,
        properties: dict[str, Any] | None = None,
    ) -> None:
        if not self.enabled:
            return

        try:
            payload = {
                **(properties or {}),
                "account_id": int(identity.owner_user_id),
            }
            self._client.group_identify(
                ACCOUNT_GROUP_TYPE,
                identity.account_group_key,
                payload,
                disable_geoip=True,
            )
        except Exception as exc:  # pragma: no cover - defensive logging
            logger.warning(
                "PostHog group identify failed",
                extra={
                    "service": self.service_name,
                    "group_key": identity.account_group_key,
                    "error": str(exc),
                },
            )

    def shutdown(self) -> None:
        try:
            self._client.shutdown()
        except Exception as exc:  # pragma: no cover - defensive logging
            logger.warning(
                "PostHog shutdown failed",
                extra={"service": self.service_name, "error": str(exc)},
            )


_tracker_lock = Lock()
_tracker_instance: AnalyticsTracker | None = None


def configure_posthog_client(
    *,
    enabled: bool,
    project_api_key: str | None,
    host: str | None,
    service_name: str,
    enrichment_resolver: AnalyticsEnrichmentResolver | None = None,
) -> AnalyticsTracker:
    """Configure and cache the process-wide PostHog tracker."""
    global _tracker_instance
    with _tracker_lock:
        if _tracker_instance is not None:
            _tracker_instance.shutdown()

        if enabled and project_api_key and Posthog is not None:
            client = Posthog(
                project_api_key,
                host=host or DEFAULT_POSTHOG_HOST,
                disable_geoip=True,
            )
            _tracker_instance = AnalyticsTracker(
                service_name=service_name,
                client=client,
                enrichment_resolver=enrichment_resolver,
                enabled=True,
            )
        else:
            if enabled and not project_api_key:
                logger.warning(
                    "PostHog is enabled but no project API key was provided",
                    extra={"service": service_name},
                )
            _tracker_instance = AnalyticsTracker(
                service_name=service_name,
                client=None,
                enrichment_resolver=enrichment_resolver,
                enabled=False,
            )

        return _tracker_instance


def get_posthog_client() -> AnalyticsTracker:
    """Return the configured PostHog tracker or a disabled tracker."""
    global _tracker_instance
    with _tracker_lock:
        if _tracker_instance is None:
            _tracker_instance = AnalyticsTracker(
                service_name="unconfigured",
                client=None,
                enrichment_resolver=NoOpAnalyticsEnrichmentResolver(),
                enabled=False,
            )
        return _tracker_instance


def shutdown_posthog_client() -> None:
    """Flush and shutdown the process-wide PostHog tracker."""
    global _tracker_instance
    with _tracker_lock:
        if _tracker_instance is not None:
            _tracker_instance.shutdown()
        _tracker_instance = None
