"""Public type definitions for the analytics SDK.

All types use ``from __future__ import annotations`` so that
``dict[str, Any]`` and ``X | Y`` syntax work on Python 3.9+.
"""

from __future__ import annotations

import datetime
from typing import Any, Callable, Literal, Protocol

from typing_extensions import NotRequired, TypedDict

# ---------------------------------------------------------------------------
# Event types
# ---------------------------------------------------------------------------

EventType = Literal["track", "screen", "identify", "group"]


class Properties(TypedDict, total=False):
    """Base type for event properties. Extend this for typed catalogs."""


# ---------------------------------------------------------------------------
# Server context
# ---------------------------------------------------------------------------


class ServerInfo(TypedDict):
    runtime: Literal["cpython", "pypy", "unknown"]
    python_version: str
    environment: str
    service_name: NotRequired[str | None]
    service_version: NotRequired[str | None]
    host: NotRequired[str | None]


class SDKInfo(TypedDict):
    name: str
    version: str


class ServerContext(TypedDict):
    server: ServerInfo
    sdk: SDKInfo


# ---------------------------------------------------------------------------
# Identity
# ---------------------------------------------------------------------------


class IdentityParams(TypedDict, total=False):
    user_id: str
    anonymous_id: str
    group_id: str


# ---------------------------------------------------------------------------
# Method params
# ---------------------------------------------------------------------------


class TrackParams(TypedDict, total=False):
    event: str
    properties: dict[str, Any]
    timestamp: datetime.datetime
    user_id: str
    anonymous_id: str
    group_id: str


# ---------------------------------------------------------------------------
# SDK options
# ---------------------------------------------------------------------------


class SDKOptions(TypedDict, total=False):
    endpoint: str
    timeout: float
    max_retries: int
    retry_delay: float
    batch_mode: bool
    batch_size: int
    flush_interval: float
    default_properties: dict[str, Any]
    service_name: str
    service_version: str
    debug: bool
    disabled: bool


# ---------------------------------------------------------------------------
# Server event (wire format)
# ---------------------------------------------------------------------------


class ServerEvent(TypedDict, total=False):
    messageId: str
    type: EventType
    timestamp: str
    userId: str | None
    anonymousId: str | None
    groupId: str | None
    event: str | None
    properties: dict[str, Any] | None
    traits: dict[str, Any] | None
    context: ServerContext


# ---------------------------------------------------------------------------
# Flush result
# ---------------------------------------------------------------------------


class FlushResult(TypedDict):
    sent: int
    failed: int


# ---------------------------------------------------------------------------
# Middleware protocols
# ---------------------------------------------------------------------------


class MiddlewareFn(Protocol):
    def __call__(
        self,
        event: dict[str, Any],
        next_fn: Callable[[dict[str, Any]], None],
    ) -> None: ...


class AsyncMiddlewareFn(Protocol):
    async def __call__(
        self,
        event: dict[str, Any],
        next_fn: Callable[[dict[str, Any]], Any],
    ) -> None: ...
