from datetime import datetime
from typing import Optional, Literal, Dict, Any, List, Union, TypedDict

from pydantic import BaseModel, Field
from typing_extensions import Required

from .base import NotchPayResponse
from .channels import NotchPayChannels

__all__ = [
    "NotchPayPayment",
    "NotchPayPaymentCreate",
    "NotchPayPaymentCreateResponse",
    "NotchPayPaymentResponse",
    "NotchPayCustomerData",
    "NotchPayProcessPayment",
    "NotchPayProcessPaymentData",
    "NotchPayPaymentStatus",
    "NotchPayAmounts",
]

NotchPayPaymentStatus = Literal["pending", "processing", "complete", "failed", "canceled", "expired"]


class NotchPayAmounts(BaseModel):
    """Model representing converted amounts of a payment.

    Attributes:
        converted: Converted amount.
        currency: Conversion currency code.
        rate: Applied conversion rate.
        total: Total amount.
    """

    converted: float = Field(..., description="Converted amount")
    currency: str = Field(..., description="Conversion currency code")
    rate: Optional[float] = Field(None, description="Conversion rate")
    total: float = Field(..., description="Total amount")


class NotchPayPayment(BaseModel):
    """Model representing a NotchPay payment.

    Attributes:
        reference: Payment reference (e.g., "ref_123456789").
        amount: Payment amount.
        amounts: Converted amounts.
        currency: Currency code (e.g., "XAF").
        status: Payment status (e.g., "complete", "pending", "failed").
        customer: Customer identifier (e.g., "cus_123456789").
        sandbox: Sandbox mode.
        fees: Transaction fees.
        charge: Who should pay the processing fees.
        created_at: Payment creation date.
        completed_at: Payment completion date.
        payment_method: Payment method used (e.g., "pm.ndzAfIh555SIPML1").
    """

    reference: str = Field(..., description="Payment reference")
    amount: float = Field(..., description="Payment amount")
    amounts: Optional[NotchPayAmounts] = Field(None, description="Converted amounts")
    currency: str = Field(..., description="Currency code")
    status: NotchPayPaymentStatus = Field(
        ..., description="Payment status"
    )
    fees: Optional[list[Any]] = Field(None, description="Transaction fees")
    customer: str = Field(..., description="Customer identifier")
    sandbox: bool = Field(..., description="Sandbox mode")
    charge: Optional[str] = Field(None, description="Who should pay the processing fees")
    created_at: datetime = Field(..., description="Payment creation date")
    completed_at: Optional[datetime] = Field(None, description="Payment completion date")
    payment_method: Optional[str] = Field(None, description="Payment method used")


class NotchPayCustomerData(TypedDict, total=False):
    """Customer data for payment initialization."""

    name: Optional[str]
    """Customer name."""

    email: Optional[str]
    """Customer email address."""

    phone: Optional[str]
    """Customer phone number."""


class NotchPayPaymentCreate(TypedDict, total=False):
    """Model for creating a payment.

    Attributes:
        amount: Payment amount.
        currency: Currency code.
        email: Customer email (required if phone and customer are not provided).
        phone: Customer phone (required if email and customer are not provided).
        customer: Existing customer ID or new customer data.
        description: Payment description.
        reference: Custom payment reference.
        callback: Callback URL after payment.
        locked_currency: Locked currency.
        locked_channel: Locked payment channel.
        locked_country: Locked country.
        items: List of items.
        shipping: Shipping information.
        address: Address.
        customer_meta: Customer metadata.
    """

    amount: Required[float]
    """Payment amount."""

    currency: Required[str]
    """Currency code."""

    email: Optional[str]
    """Customer email."""

    phone: Optional[str]
    """Customer phone number."""

    customer: Optional[Union[str, NotchPayCustomerData]]
    """Customer ID or customer data."""

    description: Optional[str]
    """Payment description."""

    reference: Optional[str]
    """Custom reference."""

    callback: Optional[str]
    """Callback URL."""

    locked_currency: Optional[str]
    """Locked currency."""

    locked_channel: Optional[str]
    """Locked channel."""

    locked_country: Optional[str]
    """Locked country."""

    items: Optional[List[Dict[str, Any]]]
    """List of items."""

    shipping: Optional[Dict[str, Any]]
    """Shipping information."""

    address: Optional[Dict[str, Any]]
    """Address."""

    customer_meta: Optional[Dict[str, Any]]
    """Customer metadata."""


class NotchPayPaymentResponse(NotchPayResponse):
    """Payment detail response."""

    transaction: NotchPayPayment = Field(..., description="Payment details")


class NotchPayPaymentCreateResponse(NotchPayPaymentResponse):
    """Payment creation response."""

    authorization_url: str = Field(..., description="Payment URL")


class NotchPayProcessPaymentData(TypedDict, total=False):
    """Data for processing a payment.

    Attributes:
        phone: Phone number for mobile payment.
        account_number: Account number.
        country: Country code.
    """

    phone: Optional[str]
    """Phone number."""

    account_number: Optional[str]
    """Account number."""

    country: Optional[str]
    """Country code."""


class NotchPayProcessPayment(TypedDict, total=False):
    """Model for processing a payment.

    Attributes:
        channel: Payment channel to use (e.g., "cm.mtn").
        data: Channel-specific data.
        client_ip: Client IP address.
    """

    channel: Required[NotchPayChannels]
    """Payment channel."""

    data: Optional[NotchPayProcessPaymentData]
    """Channel data."""

    client_ip: Optional[str]
    """Client IP address."""
