"""The initialised SDK client."""

from __future__ import annotations

from typing import Any, Mapping, Optional, Union
from urllib.parse import quote

from .auth import Audience, TokenVerifier
from .config import ResolvedConfig, resolve_config
from .http import HttpClient
from .types import DeductionRequest, PartnerEcosystemConfig


class PartnerEcosystemClient:
    """The initialised SDK instance.

    Create one at start-up with :func:`ab_ecosystem.init` and reuse it for the
    lifetime of the process.
    """

    def __init__(self, config: PartnerEcosystemConfig) -> None:
        self.config: ResolvedConfig = resolve_config(config)
        self._http = HttpClient(self.config)
        #: JWKS-backed token verifier (the Node middleware's core).
        self.verifier = TokenVerifier(self.config)

    # -- Server-to-server actions ------------------------------------------

    def ping(self) -> Any:
        """Health check against the API using the project credentials."""
        return self._http.request("/ping")

    # -- User-scoped endpoints --------------------------------------------

    def get_user_profile(
        self, access_token: Optional[str] = None, user_id: Optional[str] = None
    ) -> Any:
        """``GET /api/v1/projects/:project_id/me``"""
        return self._http.request(
            self._project_path("me"), headers=_auth_headers(access_token, user_id)
        )

    def get_user_wallet(
        self, access_token: Optional[str] = None, user_id: Optional[str] = None
    ) -> Any:
        """``GET /api/v1/projects/:project_id/me``"""
        return self._http.request(
            self._project_path("me"), headers=_auth_headers(access_token, user_id)
        )

    def get_user_credit_wallet(
        self, access_token: Optional[str] = None, user_id: Optional[str] = None
    ) -> Any:
        """``GET /api/v1/projects/:project_id/credit-wallets``"""
        return self._http.request(
            self._project_path("credit-wallets"),
            headers=_auth_headers(access_token, user_id),
        )

    def get_user_subscription(
        self, access_token: Optional[str] = None, user_id: Optional[str] = None
    ) -> Any:
        """``GET /api/v1/projects/:project_id/subscription``"""
        return self._http.request(
            self._project_path("subscription"),
            headers=_auth_headers(access_token, user_id),
        )

    def send_deduction_request(
        self,
        wallet_id: str,
        payload: Union[DeductionRequest, Mapping[str, Any]],
        access_token: Optional[str] = None,
        user_id: Optional[str] = None,
    ) -> Any:
        """Send a deduction request against a specific credit wallet.

        ``POST /api/v1/projects/:project_id/credit-wallets/:wallet_id/deduct``

        ``payload`` may be a :class:`DeductionRequest` or a plain mapping; the
        outgoing body always carries ``amount``, ``meta``, ``reference`` and
        ``note``.
        """
        data = (
            payload
            if isinstance(payload, DeductionRequest)
            else DeductionRequest(**dict(payload))
        )
        body = {
            "amount": data.amount,
            "meta": data.meta or {},
            "reference": data.reference or "",
            "note": data.note or "",
        }
        suffix = f"credit-wallets/{quote(str(wallet_id), safe='')}/deduct"
        return self._http.request(
            self._project_path(suffix),
            method="POST",
            body=body,
            headers=_auth_headers(access_token, user_id),
        )

    # -- Auth --------------------------------------------------------------

    def verify_token(
        self,
        token: str,
        *,
        audience: Optional[Audience] = None,
        issuer: Optional[str] = None,
    ) -> dict[str, Any]:
        """Verify a bearer JWT against the project's JWKS endpoint."""
        return self.verifier.verify(token, audience=audience, issuer=issuer)

    def close(self) -> None:
        """Release the underlying HTTP connection pool."""
        self._http.close()

    # -- Internals ---------------------------------------------------------

    def _project_path(self, suffix: str) -> str:
        return f"/api/v1/projects/{quote(self.config.project_id, safe='')}/{suffix}"


def _auth_headers(
    access_token: Optional[str], user_id: Optional[str]
) -> Optional[dict[str, str]]:
    """Resolve per-call auth headers.

    - ``access_token`` present -> ``Authorization: Bearer <token>`` (user JWT).
    - else ``user_id`` present -> keep the default project HTTP Basic auth and
      add a ``user-id`` header so the API can resolve the user.
    - else -> project HTTP Basic auth only (no override).
    """
    if access_token:
        return {"authorization": f"Bearer {access_token}"}
    if user_id:
        return {"user-id": user_id}
    return None
