from pydantic import BaseModel, Field, field_validator, ConfigDict
from typing import Optional, List
from datetime import datetime, date
from decimal import Decimal
from enum import Enum

class PaymentMethod(str, Enum):
    CREDIT_CARD = "credit_card"
    DEBIT_CARD = "debit_card"
    BANK_TRANSFER = "bank_transfer"
    CASH = "cash"
    PAYPAL = "paypal"
    STRIPE = "stripe"
    OTHER = "other"

class TransactionStatus(str, Enum):
    PENDING = "pending"
    PROCESSING = "processing"
    COMPLETED = "completed"
    FAILED = "failed"
    REFUNDED = "refunded"

class InvoiceStatus(str, Enum):
    DRAFT = "draft"
    SENT = "sent"
    PAID = "paid"
    OVERDUE = "overdue"
    CANCELLED = "cancelled"

class DiscountType(str, Enum):
    PERCENTAGE = "percentage"
    FIXED_AMOUNT = "fixed_amount"
    EARLY_BIRD = "early_bird"
    GROUP = "group"
    LOYALTY = "loyalty"

# Payment schemas
class PaymentBase(BaseModel):
    booking_id: str
    payment_method: PaymentMethod
    transaction_id: Optional[str] = None
    amount: Decimal
    currency: str
    payment_status: TransactionStatus
    payment_date: Optional[datetime] = None
    payment_gateway: Optional[str] = None
    gateway_response: Optional[str] = None
    notes: Optional[str] = None
    
    @field_validator("amount")
    @classmethod
    def validate_amount(cls, v):
        if v <= 0:
            raise ValueError("Payment amount must be greater than zero")
        return v
    
    @field_validator("currency")
    @classmethod
    def validate_currency(cls, v):
        if len(v) != 3:
            raise ValueError("Currency code must be 3 characters")
        return v.upper()

class PaymentCreate(PaymentBase):
    pass

class PaymentUpdate(BaseModel):
    booking_id: Optional[str] = None
    payment_method: Optional[PaymentMethod] = None
    transaction_id: Optional[str] = None
    amount: Optional[Decimal] = None
    currency: Optional[str] = None
    payment_status: Optional[TransactionStatus] = None
    payment_date: Optional[datetime] = None
    payment_gateway: Optional[str] = None
    gateway_response: Optional[str] = None
    notes: Optional[str] = None
    
    @field_validator("amount")
    @classmethod
    def validate_amount(cls, v):
        if v is not None and v <= 0:
            raise ValueError("Payment amount must be greater than zero")
        return v
    
    @field_validator("currency")
    @classmethod
    def validate_currency(cls, v):
        if v is not None and len(v) != 3:
            raise ValueError("Currency code must be 3 characters")
        return v.upper() if v else v

class PaymentResponse(PaymentBase):
    id: str
    created_at: datetime
    updated_at: datetime
    
    model_config = ConfigDict(from_attributes=True)

# Invoice Line Item schemas
class InvoicelineitemBase(BaseModel):
    invoice_id: str
    description: str
    quantity: int
    unit_price: Decimal
    total_price: Decimal
    item_order: int
    
    @field_validator("quantity")
    @classmethod
    def validate_quantity(cls, v):
        if v <= 0:
            raise ValueError("Quantity must be greater than zero")
        return v
    
    @field_validator("unit_price", "total_price")
    @classmethod
    def validate_price(cls, v):
        if v < 0:
            raise ValueError("Price cannot be negative")
        return v

class InvoicelineitemCreate(InvoicelineitemBase):
    pass

class InvoicelineitemUpdate(BaseModel):
    invoice_id: Optional[str] = None
    description: Optional[str] = None
    quantity: Optional[int] = None
    unit_price: Optional[Decimal] = None
    total_price: Optional[Decimal] = None
    item_order: Optional[int] = None
    
    @field_validator("quantity")
    @classmethod
    def validate_quantity(cls, v):
        if v is not None and v <= 0:
            raise ValueError("Quantity must be greater than zero")
        return v
    
    @field_validator("unit_price", "total_price")
    @classmethod
    def validate_price(cls, v):
        if v is not None and v < 0:
            raise ValueError("Price cannot be negative")
        return v

class InvoicelineitemResponse(InvoicelineitemBase):
    id: str
    created_at: datetime
    updated_at: datetime
    
    model_config = ConfigDict(from_attributes=True)

# Invoice schemas
class InvoiceBase(BaseModel):
    booking_id: str
    invoice_number: str
    invoice_date: date
    due_date: Optional[date] = None
    subtotal: Decimal
    tax_amount: Decimal
    discount_amount: Decimal
    total_amount: Decimal
    notes: Optional[str] = None
    terms: Optional[str] = None
    invoice_status: InvoiceStatus
    
    @field_validator("subtotal", "tax_amount", "discount_amount", "total_amount")
    @classmethod
    def validate_amounts(cls, v):
        if v < 0:
            raise ValueError("Amount cannot be negative")
        return v
    
    @field_validator("invoice_number")
    @classmethod
    def validate_invoice_number(cls, v):
        if not v or len(v.strip()) == 0:
            raise ValueError("Invoice number cannot be empty")
        return v

class InvoiceCreate(InvoiceBase):
    pass

class InvoiceUpdate(BaseModel):
    booking_id: Optional[str] = None
    invoice_number: Optional[str] = None
    invoice_date: Optional[date] = None
    due_date: Optional[date] = None
    subtotal: Optional[Decimal] = None
    tax_amount: Optional[Decimal] = None
    discount_amount: Optional[Decimal] = None
    total_amount: Optional[Decimal] = None
    notes: Optional[str] = None
    terms: Optional[str] = None
    invoice_status: Optional[InvoiceStatus] = None
    
    @field_validator("subtotal", "tax_amount", "discount_amount", "total_amount")
    @classmethod
    def validate_amounts(cls, v):
        if v is not None and v < 0:
            raise ValueError("Amount cannot be negative")
        return v
    
    @field_validator("invoice_number")
    @classmethod
    def validate_invoice_number(cls, v):
        if v is not None and (not v or len(v.strip()) == 0):
            raise ValueError("Invoice number cannot be empty")
        return v

class InvoiceResponse(InvoiceBase):
    id: str
    created_at: datetime
    updated_at: datetime
    
    model_config = ConfigDict(from_attributes=True)

class InvoiceDetailResponse(InvoiceResponse):
    invoice_line_items: List[InvoicelineitemResponse] = []
    
    model_config = ConfigDict(from_attributes=True)

# Discount schemas
class DiscountBase(BaseModel):
    code: str
    name: str
    description: Optional[str] = None
    discount_type: DiscountType
    discount_value: Decimal
    min_booking_amount: Optional[Decimal] = None
    max_discount_amount: Optional[Decimal] = None
    min_travelers: Optional[int] = None
    applicable_tour_package_id: Optional[str] = None
    valid_from: date
    valid_to: date
    usage_limit: Optional[int] = None
    usage_count: int = 0
    is_active: bool = True
    
    @field_validator("discount_value")
    @classmethod
    def validate_discount_value(cls, v):
        if v <= 0:
            raise ValueError("Discount value must be greater than zero")
        return v
    
    @field_validator("min_booking_amount", "max_discount_amount")
    @classmethod
    def validate_amounts(cls, v):
        if v is not None and v < 0:
            raise ValueError("Amount cannot be negative")
        return v
    
    @field_validator("min_travelers")
    @classmethod
    def validate_min_travelers(cls, v):
        if v is not None and v <= 0:
            raise ValueError("Minimum travelers must be greater than zero")
        return v
    
    @field_validator("usage_limit")
    @classmethod
    def validate_usage_limit(cls, v):
        if v is not None and v <= 0:
            raise ValueError("Usage limit must be greater than zero")
        return v
    
    @field_validator("code")
    @classmethod
    def validate_code(cls, v):
        if not v or len(v.strip()) == 0:
            raise ValueError("Discount code cannot be empty")
        return v.upper()
    
    @field_validator("valid_to")
    @classmethod
    def validate_dates(cls, v, info):
        if "valid_from" in info.data and v < info.data["valid_from"]:
            raise ValueError("valid_to must be after valid_from")
        return v

class DiscountCreate(DiscountBase):
    pass

class DiscountUpdate(BaseModel):
    code: Optional[str] = None
    name: Optional[str] = None
    description: Optional[str] = None
    discount_type: Optional[DiscountType] = None
    discount_value: Optional[Decimal] = None
    min_booking_amount: Optional[Decimal] = None
    max_discount_amount: Optional[Decimal] = None
    min_travelers: Optional[int] = None
    applicable_tour_package_id: Optional[str] = None
    valid_from: Optional[date] = None
    valid_to: Optional[date] = None
    usage_limit: Optional[int] = None
    usage_count: Optional[int] = None
    is_active: Optional[bool] = None
    
    @field_validator("discount_value")
    @classmethod
    def validate_discount_value(cls, v):
        if v is not None and v <= 0:
            raise ValueError("Discount value must be greater than zero")
        return v
    
    @field_validator("min_booking_amount", "max_discount_amount")
    @classmethod
    def validate_amounts(cls, v):
        if v is not None and v < 0:
            raise ValueError("Amount cannot be negative")
        return v
    
    @field_validator("min_travelers")
    @classmethod
    def validate_min_travelers(cls, v):
        if v is not None and v <= 0:
            raise ValueError("Minimum travelers must be greater than zero")
        return v
    
    @field_validator("usage_limit")
    @classmethod
    def validate_usage_limit(cls, v):
        if v is not None and v <= 0:
            raise ValueError("Usage limit must be greater than zero")
        return v
    
    @field_validator("code")
    @classmethod
    def validate_code(cls, v):
        if v is not None and (not v or len(v.strip()) == 0):
            raise ValueError("Discount code cannot be empty")
        return v.upper() if v else v

class DiscountResponse(DiscountBase):
    id: str
    created_at: datetime
    updated_at: datetime
    
    model_config = ConfigDict(from_attributes=True)