"""Invoices resource."""

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

from ..client import AsyncHttpClient, HttpClient
from ..pagination import PaginatedResponse, async_paginate, paginate
from ..types import CreateCustomerNestedDto, InvoiceResponseDto

BASE_PATH = "/api/v1/invoices"


class InvoicesResource:
    """Synchronous invoices resource.

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

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

    def create(
        self,
        *,
        amount: float,
        currency: str,
        description: str,
        expires_at: str,
        customer: Dict[str, Any],
        reference: Optional[str] = None,
        period_start: Optional[str] = None,
        period_end: Optional[str] = None,
        next_payment_date: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
        success_url: Optional[str] = None,
        cancel_url: Optional[str] = None,
        asset: Optional[str] = None,
        chain: Optional[str] = None,
    ) -> InvoiceResponseDto:
        """Create a new invoice.

        Args:
            amount: Invoice amount in fiat currency.
            currency: Fiat currency code (e.g. "USD", "NGN").
            description: Invoice description shown to the customer.
            expires_at: Expiration date in ISO 8601 format.
            customer: Customer details dict with at least an ``email`` key.
            reference: Custom invoice reference. Auto-generated if omitted.
            period_start: Billing period start date. Optional.
            period_end: Billing period end date. Optional.
            next_payment_date: Next payment date for recurring invoices. Optional.
            metadata: Arbitrary metadata dict. Optional.
            success_url: Success redirect URL. Optional.
            cancel_url: Cancel redirect URL. Optional.
            asset: Crypto asset. Optional.
            chain: Blockchain network. Optional.

        Returns:
            The created invoice object.
        """
        body: Dict[str, Any] = {
            "amount": amount,
            "currency": currency,
            "description": description,
            "expiresAt": expires_at,
            "customer": customer,
        }
        if reference is not None:
            body["reference"] = reference
        if period_start is not None:
            body["periodStart"] = period_start
        if period_end is not None:
            body["periodEnd"] = period_end
        if next_payment_date is not None:
            body["nextPaymentDate"] = next_payment_date
        if metadata is not None:
            body["metadata"] = metadata
        if success_url is not None:
            body["successUrl"] = success_url
        if cancel_url is not None:
            body["cancelUrl"] = cancel_url
        if asset is not None:
            body["asset"] = asset
        if chain is not None:
            body["chain"] = chain

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

    def list(
        self,
        *,
        page: int = 1,
        size: int = 10,
        search: Optional[str] = None,
        status: Optional[str] = None,
        state: Optional[str] = None,
        currency: Optional[str] = None,
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
    ) -> PaginatedResponse:
        """List invoices with pagination.

        Returns:
            A PaginatedResponse containing InvoiceResponseDto items.
        """
        params: Dict[str, Any] = {
            "page": page,
            "size": size,
            "search": search,
            "status": status,
            "state": state,
            "currency": currency,
            "startDate": start_date,
            "endDate": end_date,
        }
        return self._client.request_paginated("GET", BASE_PATH, params=params)

    def list_auto_paginate(self, **kwargs: Any) -> Iterator[InvoiceResponseDto]:
        """Iterate over all invoices across all pages."""
        return paginate(self.list, params=kwargs)

    def get(self, reference: str) -> InvoiceResponseDto:
        """Get an invoice by reference.

        Args:
            reference: The invoice reference string.

        Returns:
            The invoice object.
        """
        return self._client.request("GET", f"{BASE_PATH}/reference/{reference}")


class AsyncInvoicesResource:
    """Asynchronous invoices resource."""

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

    async def create(
        self,
        *,
        amount: float,
        currency: str,
        description: str,
        expires_at: str,
        customer: Dict[str, Any],
        reference: Optional[str] = None,
        period_start: Optional[str] = None,
        period_end: Optional[str] = None,
        next_payment_date: Optional[str] = None,
        metadata: Optional[Dict[str, Any]] = None,
        success_url: Optional[str] = None,
        cancel_url: Optional[str] = None,
        asset: Optional[str] = None,
        chain: Optional[str] = None,
    ) -> InvoiceResponseDto:
        """Create a new invoice (async)."""
        body: Dict[str, Any] = {
            "amount": amount,
            "currency": currency,
            "description": description,
            "expiresAt": expires_at,
            "customer": customer,
        }
        if reference is not None:
            body["reference"] = reference
        if period_start is not None:
            body["periodStart"] = period_start
        if period_end is not None:
            body["periodEnd"] = period_end
        if next_payment_date is not None:
            body["nextPaymentDate"] = next_payment_date
        if metadata is not None:
            body["metadata"] = metadata
        if success_url is not None:
            body["successUrl"] = success_url
        if cancel_url is not None:
            body["cancelUrl"] = cancel_url
        if asset is not None:
            body["asset"] = asset
        if chain is not None:
            body["chain"] = chain

        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,
        status: Optional[str] = None,
        state: Optional[str] = None,
        currency: Optional[str] = None,
        start_date: Optional[str] = None,
        end_date: Optional[str] = None,
    ) -> PaginatedResponse:
        """List invoices with pagination (async)."""
        params: Dict[str, Any] = {
            "page": page,
            "size": size,
            "search": search,
            "status": status,
            "state": state,
            "currency": currency,
            "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[InvoiceResponseDto]:
        """Iterate over all invoices across all pages (async)."""
        return async_paginate(self.list, params=kwargs)

    async def get(self, reference: str) -> InvoiceResponseDto:
        """Get an invoice by reference (async)."""
        return await self._client.request("GET", f"{BASE_PATH}/reference/{reference}")
