"""Cliente principal da abacapython.

Esta classe é a porta de entrada que a maioria dos desenvolvedores vai usar.
Ela instancia o transporte HTTP e expõe um resource por área da API.

Exemplo síncrono:

    from abacapython import AbacaPay

    client = AbacaPay(api_key="abc_dev_...")
    store = client.store.get()
    print(store.name)

Exemplo assíncrono:

    import asyncio
    from abacapython import AsyncAbacaPay

    async def main() -> None:
        async with AsyncAbacaPay(api_key="abc_dev_...") as client:
            store = await client.store.get()
            print(store.name)

    asyncio.run(main())
"""

from __future__ import annotations

import os

import httpx

from ._http import (
    DEFAULT_BACKOFF_FACTOR,
    DEFAULT_BACKOFF_MAX,
    DEFAULT_BASE_URL,
    DEFAULT_MAX_RETRIES,
    DEFAULT_TIMEOUT,
    AsyncHTTPTransport,
    HTTPTransport,
)
from .resources import (
    AsyncCheckoutsResource,
    AsyncCouponsResource,
    AsyncCustomersResource,
    AsyncPaymentLinksResource,
    AsyncPayoutsResource,
    AsyncPixQrCodesResource,
    AsyncPixTransfersResource,
    AsyncProductsResource,
    AsyncStoreResource,
    AsyncSubscriptionsResource,
    AsyncTrustMRRResource,
    AsyncWebhooksResource,
    CheckoutsResource,
    CouponsResource,
    CustomersResource,
    PaymentLinksResource,
    PayoutsResource,
    PixQrCodesResource,
    PixTransfersResource,
    ProductsResource,
    StoreResource,
    SubscriptionsResource,
    TrustMRRResource,
    WebhooksResource,
)

ENV_API_KEY = "ABACATEPAY_API_KEY"
ENV_BASE_URL = "ABACATEPAY_BASE_URL"


def _resolve_api_key(explicit: str | None) -> str:
    api_key = explicit or os.environ.get(ENV_API_KEY)
    if not api_key:
        raise ValueError(
            "API key não informada. Passe `api_key=...` ou defina a variável de "
            f"ambiente {ENV_API_KEY}."
        )
    return api_key


def _resolve_base_url(explicit: str | None) -> str:
    return explicit or os.environ.get(ENV_BASE_URL) or DEFAULT_BASE_URL


class AbacaPay:
    """Cliente síncrono da API AbacatePay.

    Args:
        api_key: Chave de API. Se omitida, lê de ``ABACATEPAY_API_KEY``.
        base_url: Override da URL base.
        timeout: Timeout em segundos para qualquer requisição.
        max_retries: Tentativas extras em caso de erros transitórios.
        backoff_factor: Fator base do backoff exponencial entre retries.
        backoff_max: Limite superior em segundos para qualquer espera.
        http_client: Instância customizada de :class:`httpx.Client` (opcional).
    """

    def __init__(
        self,
        api_key: str | None = None,
        *,
        base_url: str | None = None,
        timeout: float = DEFAULT_TIMEOUT,
        max_retries: int = DEFAULT_MAX_RETRIES,
        backoff_factor: float = DEFAULT_BACKOFF_FACTOR,
        backoff_max: float = DEFAULT_BACKOFF_MAX,
        http_client: httpx.Client | None = None,
    ) -> None:
        self._transport = HTTPTransport(
            api_key=_resolve_api_key(api_key),
            base_url=_resolve_base_url(base_url),
            timeout=timeout,
            max_retries=max_retries,
            backoff_factor=backoff_factor,
            backoff_max=backoff_max,
            client=http_client,
        )

        # Resources
        self.customers = CustomersResource(self._transport)
        self.products = ProductsResource(self._transport)
        self.checkouts = CheckoutsResource(self._transport)
        self.payment_links = PaymentLinksResource(self._transport)
        self.pix_qrcodes = PixQrCodesResource(self._transport)
        self.coupons = CouponsResource(self._transport)
        self.webhooks = WebhooksResource(self._transport)
        self.subscriptions = SubscriptionsResource(self._transport)
        self.payouts = PayoutsResource(self._transport)
        self.pix_transfers = PixTransfersResource(self._transport)
        self.store = StoreResource(self._transport)
        self.trust_mrr = TrustMRRResource(self._transport)

    @property
    def base_url(self) -> str:
        """URL base usada pelo cliente."""
        return self._transport.base_url

    def close(self) -> None:
        """Fecha o cliente HTTP subjacente."""
        self._transport.close()

    def __enter__(self) -> AbacaPay:
        return self

    def __exit__(self, *_: object) -> None:
        self.close()


class AsyncAbacaPay:
    """Cliente assíncrono da API AbacatePay. Mesma interface de :class:`AbacaPay`."""

    def __init__(
        self,
        api_key: str | None = None,
        *,
        base_url: str | None = None,
        timeout: float = DEFAULT_TIMEOUT,
        max_retries: int = DEFAULT_MAX_RETRIES,
        backoff_factor: float = DEFAULT_BACKOFF_FACTOR,
        backoff_max: float = DEFAULT_BACKOFF_MAX,
        http_client: httpx.AsyncClient | None = None,
    ) -> None:
        self._transport = AsyncHTTPTransport(
            api_key=_resolve_api_key(api_key),
            base_url=_resolve_base_url(base_url),
            timeout=timeout,
            max_retries=max_retries,
            backoff_factor=backoff_factor,
            backoff_max=backoff_max,
            client=http_client,
        )

        self.customers = AsyncCustomersResource(self._transport)
        self.products = AsyncProductsResource(self._transport)
        self.checkouts = AsyncCheckoutsResource(self._transport)
        self.payment_links = AsyncPaymentLinksResource(self._transport)
        self.pix_qrcodes = AsyncPixQrCodesResource(self._transport)
        self.coupons = AsyncCouponsResource(self._transport)
        self.webhooks = AsyncWebhooksResource(self._transport)
        self.subscriptions = AsyncSubscriptionsResource(self._transport)
        self.payouts = AsyncPayoutsResource(self._transport)
        self.pix_transfers = AsyncPixTransfersResource(self._transport)
        self.store = AsyncStoreResource(self._transport)
        self.trust_mrr = AsyncTrustMRRResource(self._transport)

    @property
    def base_url(self) -> str:
        return self._transport.base_url

    async def aclose(self) -> None:
        await self._transport.aclose()

    async def __aenter__(self) -> AsyncAbacaPay:
        return self

    async def __aexit__(self, *_: object) -> None:
        await self.aclose()


__all__ = ["AbacaPay", "AsyncAbacaPay"]
