from __future__ import annotations

from typing import Any, Dict, Optional

from .http import HttpClient, AsyncHttpClient
from .types import (
    EventIngestRequest,
    EventIngestResponse,
    RegisterDeviceIdentityRequest,
    DeviceIdentityResponse,
    UnregisterDeviceIdentityRequest,
    UnregisterDeviceIdentityResponse,
    RegisterEmailIdentityRequest,
    EmailIdentityResponse,
    StartEmailVerifyRequest,
    StartEmailVerifyResponse,
    ConfirmEmailVerifyRequest,
    ConfirmEmailVerifyResponse,
    WebTokenRegisterRequest,
    WebTokenUnregisterRequest,
    DirectSendRequest,
    DirectSendResponse,
    BulkEmailImportRequest
)


class MessagingClient:
    """
    Messaging API (API key)
    Base path: /api/messaging

    IMPORTANT:
    - SDK mirrors ONLY public API schemas.
    - No templates, channels, or routing params here.
    - Templates + channels are attached to RULES via dashboard.
    """

    def __init__(self, http: HttpClient):
        self._http = http

    # ----------------------------
    # Events
    # ----------------------------

    def post_event(
        self,
        *,
        event: str,
        external_user_id: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
        idempotency_key: Optional[str] = None,
    ) -> EventIngestResponse:
        """
        Ingest a messaging event.

        Matches:
        POST /api/messaging/events
        EventIngestRequest
        """
        payload = EventIngestRequest(
            event=event,
            external_user_id=external_user_id,
            data=data,
            idempotency_key=idempotency_key,
        )
        resp = self._http.post(
            "/api/messaging/events",
            json=payload.model_dump(exclude_none=True),
        )
        return EventIngestResponse.model_validate(resp)

    # ----------------------------
    # Device identities (push)
    # ----------------------------
    
    def send(
        self,
        *,
        channel: str,
        external_user_id: str,
        title: str | None = None,
        body: str | None = None,
        data: dict | None = None,
        priority: str = "standard",
        idempotency_key: str | None = None,
    ) -> DirectSendResponse:
        payload = DirectSendRequest(
            channel=channel,
            external_user_id=external_user_id,
            title=title,
            body=body,
            data=data,
            priority=priority,
            idempotency_key=idempotency_key,
        )

        resp = self._http.post(
            "/api/messaging/send",
            json=payload.model_dump(exclude_none=True),
        )
        return DirectSendResponse.model_validate(resp)
    
    def bulk_import_email_identities(
        self,
        *,
        users: list[dict],
    ) -> None:
        payload = BulkEmailImportRequest(users=users)
        self._http.post(
            "/api/messaging/identities/email/bulk_import",
            json=payload.model_dump(),
        )

    def register_device(
        self,
        *,
        external_user_id: str,
        token: str,
        platform: str,
        app_id: Optional[str] = None,
    ) -> DeviceIdentityResponse:
        """
        Register a push device identity.

        POST /api/messaging/devices
        """
        payload = RegisterDeviceIdentityRequest(
            external_user_id=external_user_id,
            token=token,
            platform=platform,
            app_id=app_id,
        )
        resp = self._http.post("/api/messaging/devices", json=payload.model_dump(exclude_none=True))
        return DeviceIdentityResponse.model_validate(resp)

    def unregister_device(
        self,
        *,
        external_user_id: str,
        token: str,
    ) -> UnregisterDeviceIdentityResponse:
        """
        Unregister a push device identity.

        POST /api/messaging/devices/unregister
        """
        payload = UnregisterDeviceIdentityRequest(
            external_user_id=external_user_id,
            token=token,
        )
        resp = self._http.post("/api/messaging/devices/unregister", json=payload.model_dump())
        return UnregisterDeviceIdentityResponse.model_validate(resp)

    # ----------------------------
    # Email identities
    # ----------------------------

    def register_email_identity(
        self,
        *,
        external_user_id: str,
        email: str,
        label: Optional[str] = None,
    ) -> EmailIdentityResponse:
        """
        Register an email identity.

        POST /api/messaging/identities/email
        """
        payload = RegisterEmailIdentityRequest(
            external_user_id=external_user_id,
            email=email,
            label=label,
        )
        resp = self._http.post(
            "/api/messaging/identities/email",
            json=payload.model_dump(exclude_none=True),
        )
        return EmailIdentityResponse.model_validate(resp)

    def start_email_verification(
        self,
        *,
        external_user_id: str,
        email: str,
        label: Optional[str] = None,
    ) -> StartEmailVerifyResponse:
        """
        Start email verification flow.

        POST /api/messaging/identities/email/start_verify
        """
        payload = StartEmailVerifyRequest(
            external_user_id=external_user_id,
            email=email,
            label=label,
        )
        resp = self._http.post(
            "/api/messaging/identities/email/start_verify",
            json=payload.model_dump(exclude_none=True),
        )
        if resp is None:
            return StartEmailVerifyResponse(ok=True)
        return StartEmailVerifyResponse.model_validate(resp)

    def confirm_email_verification(
        self,
        *,
        external_user_id: str,
        email: str,
        code: str,
    ) -> ConfirmEmailVerifyResponse:
        """
        Confirm email verification.

        POST /api/messaging/identities/email/confirm
        """
        payload = ConfirmEmailVerifyRequest(
            external_user_id=external_user_id,
            email=email,
            code=code,
        )
        resp = self._http.post(
            "/api/messaging/identities/email/confirm",
            json=payload.model_dump(),
        )
        if resp is None:
            return ConfirmEmailVerifyResponse(ok=True)
        return ConfirmEmailVerifyResponse.model_validate(resp)

    # ----------------------------
    # Web push tokens
    # ----------------------------

    def register_web_token(
        self,
        *,
        external_user_id: str,
        token: str,
        app_id: Optional[str] = None,
    ) -> None:
        """
        Register a web push token.

        POST /api/messaging/web_tokens/register
        """
        payload = WebTokenRegisterRequest(
            external_user_id=external_user_id,
            token=token,
            app_id=app_id,
        )
        self._http.post(
            "/api/messaging/web_tokens/register",
            json=payload.model_dump(exclude_none=True),
        )

    def unregister_web_token(
        self,
        *,
        external_user_id: str,
        token: str,
    ) -> None:
        """
        Unregister a web push token.

        POST /api/messaging/web_tokens/unregister
        """
        payload = WebTokenUnregisterRequest(
            external_user_id=external_user_id,
            token=token,
        )
        self._http.post(
            "/api/messaging/web_tokens/unregister",
            json=payload.model_dump(),
        )


# =====================================================================
# ASYNC CLIENT
# =====================================================================

class AsyncMessagingClient:
    def __init__(self, http: AsyncHttpClient):
        self._http = http

    async def post_event(
        self,
        *,
        event: str,
        external_user_id: Optional[str] = None,
        data: Optional[Dict[str, Any]] = None,
        idempotency_key: Optional[str] = None,
    ) -> EventIngestResponse:
        payload = EventIngestRequest(
            event=event,
            external_user_id=external_user_id,
            data=data,
            idempotency_key=idempotency_key,
        )
        resp = await self._http.post(
            "/api/messaging/events",
            json=payload.model_dump(exclude_none=True),
        )
        return EventIngestResponse.model_validate(resp)
    

    async def send(
        self,
        *,
        channel: str,
        external_user_id: str,
        title: str | None = None,
        body: str | None = None,
        data: dict | None = None,
        priority: str = "standard",
        idempotency_key: str | None = None,
    ) -> DirectSendResponse:
        payload = DirectSendRequest(
            channel=channel,
            external_user_id=external_user_id,
            title=title,
            body=body,
            data=data,
            priority=priority,
            idempotency_key=idempotency_key,
        )

        resp = await self._http.post(
            "/api/messaging/send",
            json=payload.model_dump(exclude_none=True),
        )
        return DirectSendResponse.model_validate(resp)
    
    async def bulk_import_email_identities(
        self,
        *,
        users: list[dict],
    ) -> None:
        payload = BulkEmailImportRequest(users=users)
        await self._http.post(
            "/api/messaging/identities/email/bulk_import",
            json=payload.model_dump(),
        )

    async def register_device(
        self,
        *,
        external_user_id: str,
        token: str,
        platform: str,
        app_id: Optional[str] = None,
    ) -> DeviceIdentityResponse:
        payload = RegisterDeviceIdentityRequest(
            external_user_id=external_user_id,
            token=token,
            platform=platform,
            app_id=app_id,
        )
        resp = await self._http.post(
            "/api/messaging/devices",
            json=payload.model_dump(exclude_none=True),
        )
        return DeviceIdentityResponse.model_validate(resp)

    async def unregister_device(
        self,
        *,
        external_user_id: str,
        token: str,
    ) -> UnregisterDeviceIdentityResponse:
        payload = UnregisterDeviceIdentityRequest(
            external_user_id=external_user_id,
            token=token,
        )
        resp = await self._http.post(
            "/api/messaging/devices/unregister",
            json=payload.model_dump(),
        )
        return UnregisterDeviceIdentityResponse.model_validate(resp)

    async def register_email_identity(
        self,
        *,
        external_user_id: str,
        email: str,
        label: Optional[str] = None,
    ) -> EmailIdentityResponse:
        payload = RegisterEmailIdentityRequest(
            external_user_id=external_user_id,
            email=email,
            label=label,
        )
        resp = await self._http.post(
            "/api/messaging/identities/email",
            json=payload.model_dump(exclude_none=True),
        )
        return EmailIdentityResponse.model_validate(resp)

    async def start_email_verification(
        self,
        *,
        external_user_id: str,
        email: str,
        label: Optional[str] = None,
    ) -> StartEmailVerifyResponse:
        payload = StartEmailVerifyRequest(
            external_user_id=external_user_id,
            email=email,
            label=label,
        )
        resp = await self._http.post(
            "/api/messaging/identities/email/start_verify",
            json=payload.model_dump(exclude_none=True),
        )
        if resp is None:
            return StartEmailVerifyResponse(ok=True)
        return StartEmailVerifyResponse.model_validate(resp)

    async def confirm_email_verification(
        self,
        *,
        external_user_id: str,
        email: str,
        code: str,
    ) -> ConfirmEmailVerifyResponse:
        payload = ConfirmEmailVerifyRequest(
            external_user_id=external_user_id,
            email=email,
            code=code,
        )
        resp = await self._http.post(
            "/api/messaging/identities/email/confirm",
            json=payload.model_dump(),
        )
        if resp is None:
            return ConfirmEmailVerifyResponse(ok=True)
        return ConfirmEmailVerifyResponse.model_validate(resp)

    async def register_web_token(
        self,
        *,
        external_user_id: str,
        token: str,
        app_id: Optional[str] = None,
    ) -> None:
        payload = WebTokenRegisterRequest(
            external_user_id=external_user_id,
            token=token,
            app_id=app_id,
        )
        await self._http.post(
            "/api/messaging/web_tokens/register",
            json=payload.model_dump(exclude_none=True),
        )

    async def unregister_web_token(
        self,
        *,
        external_user_id: str,
        token: str,
    ) -> None:
        payload = WebTokenUnregisterRequest(
            external_user_id=external_user_id,
            token=token,
        )
        await self._http.post(
            "/api/messaging/web_tokens/unregister",
            json=payload.model_dump(),
        )
