"""Payment sessions resource."""

from typing import Any, AsyncIterator, Dict, Iterator, Optional

from ..client import AsyncHttpClient, HttpClient
from ..pagination import PaginatedResponse, async_paginate, paginate
from ..types import (
    CalculateCheckoutAmountResponseDto,
    CheckoutSessionV2Dto,
    CreateCheckoutSessionDto,
)

BASE_PATH = "/api/v1/payments"


class PaymentsResource:
    """Synchronous payment sessions resource.

    Wraps endpoints under ``/api/v1/payments``.
    """

    def __init__(self, client: HttpClient) -> None:
        self._client = client

    def create(
        self,
        *,
        title: str,
        description: str,
        amount: str,
        currency: str,
        customer: Dict[str, Any],
        asset: Optional[str] = None,
        chain: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
        cancel_url: Optional[str] = None,
        success_url: Optional[str] = None,
        webhook_url: Optional[str] = None,
        fee_paid_by: Optional[str] = None,
    ) -> CheckoutSessionV2Dto:
        """Create a new checkout payment session.

        Args:
            title: Session title.
            description: Session description.
            amount: Amount in fiat currency (e.g. "100.00").
            currency: Fiat currency code (e.g. "USD", "NGN").
            customer: Customer details dict with at least an ``email`` key.
            asset: Crypto asset (e.g. "USDC", "USDT"). Optional.
            chain: Blockchain network (e.g. "base", "ethereum"). Optional.
            metadata: Arbitrary metadata dict. Optional.
            cancel_url: URL to redirect on cancel. Optional.
            success_url: URL to redirect on success. Optional.
            webhook_url: Webhook URL for payment events. Optional.
            fee_paid_by: "customer" or "merchant". Optional.

        Returns:
            The created checkout session object.
        """
        body: Dict[str, Any] = {
            "title": title,
            "description": description,
            "amount": amount,
            "currency": currency,
            "customer": customer,
        }
        if asset is not None:
            body["asset"] = asset
        if chain is not None:
            body["chain"] = chain
        if metadata is not None:
            body["metadata"] = metadata
        if cancel_url is not None:
            body["cancelUrl"] = cancel_url
        if success_url is not None:
            body["successUrl"] = success_url
        if webhook_url is not None:
            body["webhookUrl"] = webhook_url
        if fee_paid_by is not None:
            body["feePaidBy"] = fee_paid_by

        return self._client.request("POST", BASE_PATH, json_body=body)

    def list(
        self,
        *,
        page: int = 1,
        size: int = 10,
        search: Optional[str] = None,
        invoice_reference: Optional[str] = None,
        customer_id: Optional[str] = None,
        page_reference: Optional[str] = None,
        state: Optional[str] = None,
        payment_status: Optional[str] = None,
        asset: Optional[str] = None,
        chain: Optional[str] = None,
        min_amount: Optional[str] = None,
        max_amount: Optional[str] = None,
        include_transactions: Optional[bool] = None,
        type: Optional[str] = None,
        currency: Optional[str] = None,
        is_refunded: Optional[bool] = None,
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
    ) -> PaginatedResponse:
        """List payment sessions with pagination.

        Returns:
            A PaginatedResponse containing CheckoutSessionV2Dto items.
        """
        params: Dict[str, Any] = {
            "page": page,
            "size": size,
            "search": search,
            "invoiceReference": invoice_reference,
            "customerId": customer_id,
            "pageReference": page_reference,
            "state": state,
            "paymentStatus": payment_status,
            "asset": asset,
            "chain": chain,
            "minAmount": min_amount,
            "maxAmount": max_amount,
            "includeTransactions": include_transactions,
            "type": type,
            "currency": currency,
            "isRefunded": is_refunded,
            "startDate": start_date,
            "endDate": end_date,
        }
        return self._client.request_paginated("GET", BASE_PATH, params=params)

    def list_auto_paginate(self, **kwargs: Any) -> Iterator[CheckoutSessionV2Dto]:
        """Iterate over all payment sessions across all pages.

        Accepts the same keyword arguments as ``list()`` (except ``page``).
        """
        return paginate(self.list, params=kwargs)

    def get(self, reference: str, *, include_transactions: Optional[bool] = None) -> CheckoutSessionV2Dto:
        """Get a payment session by reference.

        Args:
            reference: The session reference string.
            include_transactions: Whether to include transaction details.

        Returns:
            The checkout session object.
        """
        params: Dict[str, Any] = {
            "includeTransactions": include_transactions,
        }
        return self._client.request(
            "GET",
            f"{BASE_PATH}/reference/{reference}",
            params=params,
        )

    def generate_deposit_address(
        self,
        reference: str,
        *,
        asset: str,
        chain: str,
    ) -> CheckoutSessionV2Dto:
        """Generate a deposit address for a payment session.

        Args:
            reference: The session reference string.
            asset: Crypto asset (e.g. "USDT").
            chain: Blockchain network (e.g. "ethereum").

        Returns:
            The updated checkout session with payment address.
        """
        body: Dict[str, Any] = {
            "asset": asset,
            "chain": chain,
        }
        return self._client.request(
            "POST",
            f"{BASE_PATH}/{reference}/address",
            json_body=body,
        )

    def estimate(
        self,
        *,
        amount: str,
        currency: str,
        asset: str,
        chain: str,
        fee_paid_by: Optional[str] = None,
        settlement_currency: Optional[str] = None,
    ) -> CalculateCheckoutAmountResponseDto:
        """Calculate the crypto amount for a given fiat amount.

        Args:
            amount: Fiat amount (e.g. "100.00").
            currency: Fiat currency (e.g. "USD").
            asset: Crypto asset (e.g. "USDC").
            chain: Blockchain network (e.g. "base").
            fee_paid_by: "customer" or "merchant". Optional.
            settlement_currency: Settlement currency. Optional.

        Returns:
            Estimated amounts and conversion rate.
        """
        params: Dict[str, Any] = {
            "amount": amount,
            "currency": currency,
            "asset": asset,
            "chain": chain,
            "feePaidBy": fee_paid_by,
            "settlementCurrency": settlement_currency,
        }
        return self._client.request("GET", f"{BASE_PATH}/estimate", params=params)


class AsyncPaymentsResource:
    """Asynchronous payment sessions resource."""

    def __init__(self, client: AsyncHttpClient) -> None:
        self._client = client

    async def create(
        self,
        *,
        title: str,
        description: str,
        amount: str,
        currency: str,
        customer: Dict[str, Any],
        asset: Optional[str] = None,
        chain: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
        cancel_url: Optional[str] = None,
        success_url: Optional[str] = None,
        webhook_url: Optional[str] = None,
        fee_paid_by: Optional[str] = None,
    ) -> CheckoutSessionV2Dto:
        """Create a new checkout payment session (async)."""
        body: Dict[str, Any] = {
            "title": title,
            "description": description,
            "amount": amount,
            "currency": currency,
            "customer": customer,
        }
        if asset is not None:
            body["asset"] = asset
        if chain is not None:
            body["chain"] = chain
        if metadata is not None:
            body["metadata"] = metadata
        if cancel_url is not None:
            body["cancelUrl"] = cancel_url
        if success_url is not None:
            body["successUrl"] = success_url
        if webhook_url is not None:
            body["webhookUrl"] = webhook_url
        if fee_paid_by is not None:
            body["feePaidBy"] = fee_paid_by

        return await self._client.request("POST", BASE_PATH, json_body=body)

    async def list(
        self,
        *,
        page: int = 1,
        size: int = 10,
        search: Optional[str] = None,
        invoice_reference: Optional[str] = None,
        customer_id: Optional[str] = None,
        page_reference: Optional[str] = None,
        state: Optional[str] = None,
        payment_status: Optional[str] = None,
        asset: Optional[str] = None,
        chain: Optional[str] = None,
        min_amount: Optional[str] = None,
        max_amount: Optional[str] = None,
        include_transactions: Optional[bool] = None,
        type: Optional[str] = None,
        currency: Optional[str] = None,
        is_refunded: Optional[bool] = None,
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
    ) -> PaginatedResponse:
        """List payment sessions with pagination (async)."""
        params: Dict[str, Any] = {
            "page": page,
            "size": size,
            "search": search,
            "invoiceReference": invoice_reference,
            "customerId": customer_id,
            "pageReference": page_reference,
            "state": state,
            "paymentStatus": payment_status,
            "asset": asset,
            "chain": chain,
            "minAmount": min_amount,
            "maxAmount": max_amount,
            "includeTransactions": include_transactions,
            "type": type,
            "currency": currency,
            "isRefunded": is_refunded,
            "startDate": start_date,
            "endDate": end_date,
        }
        return await self._client.request_paginated("GET", BASE_PATH, params=params)

    def list_auto_paginate(self, **kwargs: Any) -> AsyncIterator[CheckoutSessionV2Dto]:
        """Iterate over all payment sessions across all pages (async)."""
        return async_paginate(self.list, params=kwargs)

    async def get(self, reference: str, *, include_transactions: Optional[bool] = None) -> CheckoutSessionV2Dto:
        """Get a payment session by reference (async)."""
        params: Dict[str, Any] = {
            "includeTransactions": include_transactions,
        }
        return await self._client.request(
            "GET",
            f"{BASE_PATH}/reference/{reference}",
            params=params,
        )

    async def generate_deposit_address(
        self,
        reference: str,
        *,
        asset: str,
        chain: str,
    ) -> CheckoutSessionV2Dto:
        """Generate a deposit address for a payment session (async)."""
        body: Dict[str, Any] = {
            "asset": asset,
            "chain": chain,
        }
        return await self._client.request(
            "POST",
            f"{BASE_PATH}/{reference}/address",
            json_body=body,
        )

    async def estimate(
        self,
        *,
        amount: str,
        currency: str,
        asset: str,
        chain: str,
        fee_paid_by: Optional[str] = None,
        settlement_currency: Optional[str] = None,
    ) -> CalculateCheckoutAmountResponseDto:
        """Calculate the crypto amount for a given fiat amount (async)."""
        params: Dict[str, Any] = {
            "amount": amount,
            "currency": currency,
            "asset": asset,
            "chain": chain,
            "feePaidBy": fee_paid_by,
            "settlementCurrency": settlement_currency,
        }
        return await self._client.request("GET", f"{BASE_PATH}/estimate", params=params)
