from __future__ import annotations

from typing import Any, Dict, Optional
from datetime import datetime

from pydantic import BaseModel, Field, ConfigDict, field_validator


class PaymentMethodCardDetailsResponse(BaseModel):
    """Response schema for card details."""
    id: int
    card_number: Optional[str] = None
    brand: Optional[str] = None
    expire_month: Optional[str] = None
    expire_year: Optional[str] = None
    reference_id: Optional[str] = None
    reference_token: Optional[str] = None
    is_default: bool = False
    is_autosaved: bool = True
    created_at: datetime
    last_used_at: Optional[datetime] = None

    model_config = ConfigDict(from_attributes=True)


class PaymentMethodAchDetailsResponse(BaseModel):
    """Response schema for ACH details."""
    id: int
    account_number: Optional[str] = None
    routing_number: Optional[str] = None
    account_name: Optional[str] = None
    bank_name: Optional[str] = None
    account_type: Optional[str] = None
    reference_id: Optional[str] = None
    reference_token: Optional[str] = None
    is_default: bool = False
    is_autosaved: bool = True
    created_at: datetime
    last_used_at: Optional[datetime] = None

    model_config = ConfigDict(from_attributes=True)


class PaymentMethodCardDetailsInput(BaseModel):
    brand: Optional[str] = Field(default=None, description="Card brand from frontend")
    last4: Optional[str] = Field(default=None, description="Card last 4 from frontend")
    exp_month: Optional[str] = Field(default=None, description="Card expiry month from frontend")
    exp_year: Optional[str] = Field(default=None, description="Card expiry year from frontend")


class PaymentMethodAchDetailsInput(BaseModel):
    account_name: Optional[str] = Field(default=None, description="Bank account holder name")
    bank_name: Optional[str] = Field(default=None, description="Bank name")
    account_type: Optional[str] = Field(default=None, description="Account type (checking/savings)")
    routing_number: Optional[str] = Field(default=None, description="Bank routing number")


class PaymentMethodTokenCreateSchema(BaseModel):
    payment_method_token: str = Field(..., description="Frontend iframe token")
    payment_method_id: Optional[str] = Field(default=None, description="Payment method ID which will be used as reference_id")
    billing_info: Optional[Dict[str, Any]] = None
    card_details: Optional[PaymentMethodCardDetailsInput] = Field(default=None, description="Card details from frontend")
    ach_details: Optional[PaymentMethodAchDetailsInput] = Field(default=None, description="ACH bank details from frontend")


class PaymentMethodSaveSchema(BaseModel):
    payment_method_token: str = Field(..., description="Frontend iframe token")
    payment_method_id: Optional[str] = Field(default=None, description="Payment method ID which will be used as reference_id")
    customer_id: Optional[int] = Field(default=None, description="Customer id to link the payment method")
    payer_id: Optional[int] = Field(default=None, description="Payer/contact id to link the payment method")
    payment_request_id: Optional[int] = Field(default=None, description="Payment request id associated with the method")
    billing_info: Optional[Dict[str, Any]] = None
    card_details: Optional[PaymentMethodCardDetailsInput] = Field(default=None, description="Card details from frontend")
    ach_details: Optional[PaymentMethodAchDetailsInput] = Field(default=None, description="ACH bank details from frontend")


class PaymentMethodResponseSchema(BaseModel):
    provider: str
    provider_payment_method_id: str
    brand: Optional[str] = None
    last4: Optional[str] = None
    exp_month: Optional[str] = None
    exp_year: Optional[str] = None
    billing: Optional[Dict[str, Any]] = None
    payment_method_id_internal: Optional[int] = None

    model_config = {
        "from_attributes": True,
    }


class PaymentMethodSaveResponseSchema(BaseModel):
    """Response schema for saved payment method with nested details."""
    id: int
    method: str
    payment_method_id: Optional[str] = None
    is_connected: bool = False
    scope: str
    customer_id: Optional[int] = None
    payer_id: Optional[int] = None
    merchant_id: int
    payment_request_id: Optional[int] = None
    card_details_id: Optional[int] = None
    ach_details_id: Optional[int] = None
    card_details: Optional[PaymentMethodCardDetailsResponse] = None
    ach_details: Optional[PaymentMethodAchDetailsResponse] = None
    created_at: datetime
    updated_at: Optional[datetime] = None

    model_config = ConfigDict(from_attributes=True)


class PaymentMethodListFilterSchema(BaseModel):
    """Schema for filtering payment method list queries."""
    
    search: Optional[str] = Field(None, description="Search across payment_method_id")
    method: Optional[str] = Field(None, description="Filter by payment method type (card/ach)")
    customer_id: Optional[int] = Field(None, description="Filter by customer ID")
    payer_id: Optional[int] = Field(None, description="Filter by payer/contact ID")
    payment_request_id: Optional[int] = Field(None, description="Filter by payment request ID")
    merchant_id: Optional[int] = Field(None, description="Filter by merchant ID (auto-applied)")
    is_connected: int = Field(0, description="Filter by connection status (0=all, 1=connected, -1=not connected)")
    scope: Optional[str] = Field(None, description="Filter by scope (merchant/customer)")
    
    @field_validator('method')
    @classmethod
    def validate_method(cls, v):
        """Validate payment method type."""
        if v is not None and v.lower() not in ['card', 'ach', 'cheque']:
            raise ValueError("Method must be 'card', 'ach', or 'cheque'")
        return v.lower() if v else v
    
    @field_validator('scope')
    @classmethod
    def validate_scope(cls, v):
        """Validate scope."""
        if v is not None and v.lower() not in ['merchant', 'customer']:
            raise ValueError("Scope must be 'merchant' or 'customer'")
        return v.lower() if v else v
    
    @field_validator('is_connected')
    @classmethod
    def validate_is_connected(cls, v):
        """Validate is_connected filter."""
        if v not in [-1, 0, 1]:
            raise ValueError("is_connected must be -1 (false), 0 (all), or 1 (true)")
        return v
