"""Code generated by Speakeasy (https://speakeasy.com). DO NOT EDIT."""

from .basesdk import BaseSDK
from .httpclient import AsyncHttpClient, ClientOwner, HttpClient, close_clients
from .sdkconfiguration import SDKConfiguration
from .utils.logger import Logger, get_default_logger
from .utils.retries import RetryConfig
from gr4vy import models, utils
from gr4vy._hooks import SDKHooks
from gr4vy.models import internal
from gr4vy.types import OptionalNullable, UNSET
import httpx
import importlib
import sys
from typing import Any, Callable, Dict, Optional, TYPE_CHECKING, Union, cast
import weakref

if TYPE_CHECKING:
    from gr4vy.account_updater import AccountUpdater
    from gr4vy.audit_logs import AuditLogs
    from gr4vy.buyers_sdk import BuyersSDK
    from gr4vy.card_scheme_definitions_sdk import CardSchemeDefinitionsSDK
    from gr4vy.checkout_sessions import CheckoutSessions
    from gr4vy.digital_wallets_sdk import DigitalWalletsSDK
    from gr4vy.gift_cards_sdk import GiftCardsSDK
    from gr4vy.merchant_accounts_sdk import MerchantAccountsSDK
    from gr4vy.payment_links_sdk import PaymentLinksSDK
    from gr4vy.payment_methods_sdk import PaymentMethodsSDK
    from gr4vy.payment_options_sdk import PaymentOptionsSDK
    from gr4vy.payment_service_definitions_sdk import PaymentServiceDefinitionsSDK
    from gr4vy.payment_services_sdk import PaymentServicesSDK
    from gr4vy.payouts import Payouts
    from gr4vy.refunds_sdk import RefundsSDK
    from gr4vy.report_executions_sdk import ReportExecutionsSDK
    from gr4vy.reports_sdk import ReportsSDK
    from gr4vy.transactions import Transactions


class Gr4vy(BaseSDK):
    r"""Gr4vy: The Gr4vy API."""

    account_updater: "AccountUpdater"
    buyers: "BuyersSDK"
    payment_methods: "PaymentMethodsSDK"
    gift_cards: "GiftCardsSDK"
    card_scheme_definitions: "CardSchemeDefinitionsSDK"
    digital_wallets: "DigitalWalletsSDK"
    transactions: "Transactions"
    refunds: "RefundsSDK"
    payment_options: "PaymentOptionsSDK"
    payment_service_definitions: "PaymentServiceDefinitionsSDK"
    payment_services: "PaymentServicesSDK"
    audit_logs: "AuditLogs"
    reports: "ReportsSDK"
    report_executions: "ReportExecutionsSDK"
    checkout_sessions: "CheckoutSessions"
    merchant_accounts: "MerchantAccountsSDK"
    payouts: "Payouts"
    payment_links: "PaymentLinksSDK"
    _sub_sdk_map = {
        "account_updater": ("gr4vy.account_updater", "AccountUpdater"),
        "buyers": ("gr4vy.buyers_sdk", "BuyersSDK"),
        "payment_methods": ("gr4vy.payment_methods_sdk", "PaymentMethodsSDK"),
        "gift_cards": ("gr4vy.gift_cards_sdk", "GiftCardsSDK"),
        "card_scheme_definitions": (
            "gr4vy.card_scheme_definitions_sdk",
            "CardSchemeDefinitionsSDK",
        ),
        "digital_wallets": ("gr4vy.digital_wallets_sdk", "DigitalWalletsSDK"),
        "transactions": ("gr4vy.transactions", "Transactions"),
        "refunds": ("gr4vy.refunds_sdk", "RefundsSDK"),
        "payment_options": ("gr4vy.payment_options_sdk", "PaymentOptionsSDK"),
        "payment_service_definitions": (
            "gr4vy.payment_service_definitions_sdk",
            "PaymentServiceDefinitionsSDK",
        ),
        "payment_services": ("gr4vy.payment_services_sdk", "PaymentServicesSDK"),
        "audit_logs": ("gr4vy.audit_logs", "AuditLogs"),
        "reports": ("gr4vy.reports_sdk", "ReportsSDK"),
        "report_executions": ("gr4vy.report_executions_sdk", "ReportExecutionsSDK"),
        "checkout_sessions": ("gr4vy.checkout_sessions", "CheckoutSessions"),
        "merchant_accounts": ("gr4vy.merchant_accounts_sdk", "MerchantAccountsSDK"),
        "payouts": ("gr4vy.payouts", "Payouts"),
        "payment_links": ("gr4vy.payment_links_sdk", "PaymentLinksSDK"),
    }

    def __init__(
        self,
        bearer_auth: Optional[Union[Optional[str], Callable[[], Optional[str]]]] = None,
        merchant_account_id: Optional[str] = None,
        id: Optional[str] = None,
        server: Optional[str] = None,
        server_url: Optional[str] = None,
        url_params: Optional[Dict[str, str]] = None,
        client: Optional[HttpClient] = None,
        async_client: Optional[AsyncHttpClient] = None,
        retry_config: OptionalNullable[RetryConfig] = UNSET,
        timeout_ms: Optional[int] = None,
        debug_logger: Optional[Logger] = None,
    ) -> None:
        r"""Instantiates the SDK configuring it with the provided parameters.

        :param bearer_auth: The bearer_auth required for authentication
        :param merchant_account_id: Configures the merchant_account_id parameter for all supported operations
        :param id: Allows setting the id variable for url substitution
        :param server: The server by name to use for all methods
        :param server_url: The server URL to use for all methods
        :param url_params: Parameters to optionally template the server URL with
        :param client: The HTTP client to use for all synchronous methods
        :param async_client: The Async HTTP client to use for all asynchronous methods
        :param retry_config: The retry configuration to use for all supported methods
        :param timeout_ms: Optional request timeout applied to each operation in milliseconds
        """
        client_supplied = True
        if client is None:
            client = httpx.Client(follow_redirects=True)
            client_supplied = False

        assert issubclass(
            type(client), HttpClient
        ), "The provided client must implement the HttpClient protocol."

        async_client_supplied = True
        if async_client is None:
            async_client = httpx.AsyncClient(follow_redirects=True)
            async_client_supplied = False

        if debug_logger is None:
            debug_logger = get_default_logger()

        assert issubclass(
            type(async_client), AsyncHttpClient
        ), "The provided async_client must implement the AsyncHttpClient protocol."

        security: Any = None
        if callable(bearer_auth):
            # pylint: disable=unnecessary-lambda-assignment
            security = lambda: models.Security(bearer_auth=bearer_auth())
        else:
            security = models.Security(bearer_auth=bearer_auth)

        if server_url is not None:
            if url_params is not None:
                server_url = utils.template_url(server_url, url_params)

        server_defaults: Dict[str, Dict[str, str]] = {
            "sandbox": {
                "id": id or "example",
            },
            "production": {
                "id": id or "example",
            },
        }

        _globals = internal.Globals(
            merchant_account_id=utils.get_global_from_env(
                merchant_account_id, "GR4VY_MERCHANT_ACCOUNT_ID", str
            ),
        )

        BaseSDK.__init__(
            self,
            SDKConfiguration(
                client=client,
                client_supplied=client_supplied,
                async_client=async_client,
                async_client_supplied=async_client_supplied,
                globals=_globals,
                security=security,
                server_url=server_url,
                server=server,
                server_defaults=server_defaults,
                retry_config=retry_config,
                timeout_ms=timeout_ms,
                debug_logger=debug_logger,
            ),
            parent_ref=self,
        )

        hooks = SDKHooks()

        # pylint: disable=protected-access
        self.sdk_configuration.__dict__["_hooks"] = hooks

        current_server_url, *_ = self.sdk_configuration.get_server_details()
        server_url, self.sdk_configuration.client = hooks.sdk_init(
            current_server_url, client
        )
        if current_server_url != server_url:
            self.sdk_configuration.server_url = server_url

        weakref.finalize(
            self,
            close_clients,
            cast(ClientOwner, self.sdk_configuration),
            self.sdk_configuration.client,
            self.sdk_configuration.client_supplied,
            self.sdk_configuration.async_client,
            self.sdk_configuration.async_client_supplied,
        )

    def dynamic_import(self, modname, retries=3):
        for attempt in range(retries):
            try:
                return importlib.import_module(modname)
            except KeyError:
                # Clear any half-initialized module and retry
                sys.modules.pop(modname, None)
                if attempt == retries - 1:
                    break
        raise KeyError(f"Failed to import module '{modname}' after {retries} attempts")

    def __getattr__(self, name: str):
        if name in self._sub_sdk_map:
            module_path, class_name = self._sub_sdk_map[name]
            try:
                module = self.dynamic_import(module_path)
                klass = getattr(module, class_name)
                instance = klass(self.sdk_configuration, parent_ref=self)
                setattr(self, name, instance)
                return instance
            except ImportError as e:
                raise AttributeError(
                    f"Failed to import module {module_path} for attribute {name}: {e}"
                ) from e
            except AttributeError as e:
                raise AttributeError(
                    f"Failed to find class {class_name} in module {module_path} for attribute {name}: {e}"
                ) from e

        raise AttributeError(
            f"'{type(self).__name__}' object has no attribute '{name}'"
        )

    def __dir__(self):
        default_attrs = list(super().__dir__())
        lazy_attrs = list(self._sub_sdk_map.keys())
        return sorted(list(set(default_attrs + lazy_attrs)))

    def __enter__(self):
        return self

    async def __aenter__(self):
        return self

    def __exit__(self, exc_type, exc_val, exc_tb):
        if (
            self.sdk_configuration.client is not None
            and not self.sdk_configuration.client_supplied
        ):
            self.sdk_configuration.client.close()
        self.sdk_configuration.client = None

    async def __aexit__(self, exc_type, exc_val, exc_tb):
        if (
            self.sdk_configuration.async_client is not None
            and not self.sdk_configuration.async_client_supplied
        ):
            await self.sdk_configuration.async_client.aclose()
        self.sdk_configuration.async_client = None
