from typing import Optional, List, Annotated
from pydantic import BaseModel, Field, ConfigDict, field_validator, model_validator, BeforeValidator
from decimal import Decimal
from datetime import date, datetime
from utils.utils import ServiceType, InvoiceStatus, PaymentMethod

StrId = Annotated[str, BeforeValidator(str)]


# Service schemas
class ServiceBase(BaseModel):
    name: str = Field(..., max_length=200)
    description: Optional[str] = None
    service_type: ServiceType
    default_price: Decimal = Field(..., max_digits=10, decimal_places=2)
    default_duration_minutes: Optional[int] = None
    department_id: Optional[str] = Field(None, max_length=36)
    is_active: bool = True


class ServiceCreate(ServiceBase):
    @field_validator("default_price")
    @classmethod
    def validate_price_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("default_price must be greater than zero")
        return v
    
    @field_validator("default_duration_minutes")
    @classmethod
    def validate_duration_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("default_duration_minutes must be greater than zero")
        return v


class ServiceUpdate(BaseModel):
    name: Optional[str] = Field(None, max_length=200)
    description: Optional[str] = None
    service_type: Optional[ServiceType] = None
    default_price: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    default_duration_minutes: Optional[int] = None
    department_id: Optional[str] = Field(None, max_length=36)
    is_active: Optional[bool] = None
    
    @field_validator("default_price")
    @classmethod
    def validate_price_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("default_price must be greater than zero")
        return v
    
    @field_validator("default_duration_minutes")
    @classmethod
    def validate_duration_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("default_duration_minutes must be greater than zero")
        return v


class ServiceResponse(ServiceBase):
    id: StrId
    created_at: datetime
    updated_at: datetime
    model_config = ConfigDict(from_attributes=True)


class ServiceListResponse(BaseModel):
    items: List[ServiceResponse]
    total: int
    limit: int
    offset: int
    model_config = ConfigDict(from_attributes=True)


# Invoice schemas
class InvoiceBase(BaseModel):
    invoice_number: str = Field(..., max_length=50)
    patient_id: str = Field(..., max_length=36)
    appointment_id: Optional[str] = Field(None, max_length=36)
    invoice_date: date
    due_date: Optional[date] = None
    subtotal: Decimal = Field(..., max_digits=10, decimal_places=2)
    tax_amount: Decimal = Field(default=Decimal("0.00"), max_digits=10, decimal_places=2)
    discount_amount: Decimal = Field(default=Decimal("0.00"), max_digits=10, decimal_places=2)
    total_amount: Decimal = Field(..., max_digits=10, decimal_places=2)
    amount_paid: Decimal = Field(default=Decimal("0.00"), max_digits=10, decimal_places=2)
    balance_due: Decimal = Field(..., max_digits=10, decimal_places=2)
    status: InvoiceStatus
    notes: Optional[str] = None
    created_by_user_id: Optional[str] = Field(None, max_length=36)


class InvoiceCreate(InvoiceBase):
    @field_validator("subtotal", "total_amount")
    @classmethod
    def validate_amounts_nonnegative(cls, v):
        if v is not None and v < 0:
            raise ValueError("Amount cannot be negative")
        return v
    
    @field_validator("tax_amount", "discount_amount", "amount_paid", "balance_due")
    @classmethod
    def validate_amounts_nonnegative_optional(cls, v):
        if v is not None and v < 0:
            raise ValueError("Amount cannot be negative")
        return v
    
    @model_validator(mode="after")
    def validate_discount_and_totals(self):
        if self.discount_amount and self.subtotal and self.discount_amount > self.subtotal:
            raise ValueError("discount_amount cannot exceed subtotal")
        if self.due_date and self.invoice_date and self.due_date < self.invoice_date:
            raise ValueError("due_date cannot be before invoice_date")
        return self


class InvoiceUpdate(BaseModel):
    invoice_number: Optional[str] = Field(None, max_length=50)
    patient_id: Optional[str] = Field(None, max_length=36)
    appointment_id: Optional[str] = Field(None, max_length=36)
    invoice_date: Optional[date] = None
    due_date: Optional[date] = None
    subtotal: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    tax_amount: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    discount_amount: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    total_amount: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    amount_paid: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    balance_due: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    status: Optional[InvoiceStatus] = None
    notes: Optional[str] = None
    created_by_user_id: Optional[str] = Field(None, max_length=36)
    
    @field_validator("subtotal", "total_amount", "tax_amount", "discount_amount", "amount_paid", "balance_due")
    @classmethod
    def validate_amounts_nonnegative(cls, v):
        if v is not None and v < 0:
            raise ValueError("Amount cannot be negative")
        return v


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


class InvoiceListResponse(BaseModel):
    items: List[InvoiceResponse]
    total: int
    limit: int
    offset: int
    model_config = ConfigDict(from_attributes=True)


# InvoiceLineItem schemas
class InvoicelineitemBase(BaseModel):
    invoice_id: str = Field(..., max_length=36)
    service_id: Optional[str] = Field(None, max_length=36)
    description: str = Field(..., max_length=500)
    quantity: int = Field(default=1)
    unit_price: Decimal = Field(..., max_digits=10, decimal_places=2)
    line_total: Decimal = Field(..., max_digits=10, decimal_places=2)


class InvoicelineitemCreate(InvoicelineitemBase):
    @field_validator("quantity")
    @classmethod
    def validate_quantity_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("quantity must be greater than zero")
        return v
    
    @field_validator("unit_price", "line_total")
    @classmethod
    def validate_amounts_nonnegative(cls, v):
        if v is not None and v < 0:
            raise ValueError("Amount cannot be negative")
        return v


class InvoicelineitemUpdate(BaseModel):
    invoice_id: Optional[str] = Field(None, max_length=36)
    service_id: Optional[str] = Field(None, max_length=36)
    description: Optional[str] = Field(None, max_length=500)
    quantity: Optional[int] = None
    unit_price: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    line_total: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    
    @field_validator("quantity")
    @classmethod
    def validate_quantity_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("quantity must be greater than zero")
        return v
    
    @field_validator("unit_price", "line_total")
    @classmethod
    def validate_amounts_nonnegative(cls, v):
        if v is not None and v < 0:
            raise ValueError("Amount cannot be negative")
        return v


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


class InvoicelineitemListResponse(BaseModel):
    items: List[InvoicelineitemResponse]
    total: int
    limit: int
    offset: int
    model_config = ConfigDict(from_attributes=True)


# Payment schemas
class PaymentBase(BaseModel):
    invoice_id: str = Field(..., max_length=36)
    patient_id: str = Field(..., max_length=36)
    payment_date: datetime
    amount: Decimal = Field(..., max_digits=10, decimal_places=2)
    payment_method: PaymentMethod
    transaction_reference: Optional[str] = Field(None, max_length=100)
    notes: Optional[str] = None
    processed_by_user_id: Optional[str] = Field(None, max_length=36)


class PaymentCreate(PaymentBase):
    @field_validator("amount")
    @classmethod
    def validate_amount_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("amount must be greater than zero")
        return v


class PaymentUpdate(BaseModel):
    invoice_id: Optional[str] = Field(None, max_length=36)
    patient_id: Optional[str] = Field(None, max_length=36)
    payment_date: Optional[datetime] = None
    amount: Optional[Decimal] = Field(None, max_digits=10, decimal_places=2)
    payment_method: Optional[PaymentMethod] = None
    transaction_reference: Optional[str] = Field(None, max_length=100)
    notes: Optional[str] = None
    processed_by_user_id: Optional[str] = Field(None, max_length=36)
    
    @field_validator("amount")
    @classmethod
    def validate_amount_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("amount must be greater than zero")
        return v


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


class PaymentListResponse(BaseModel):
    items: List[PaymentResponse]
    total: int
    limit: int
    offset: int
    model_config = ConfigDict(from_attributes=True)


# Detail endpoint schemas
class ServiceDetailResponse(BaseModel):
    id: StrId
    name: str
    model_config = ConfigDict(from_attributes=True)


class InvoiceLineItemDetailResponse(BaseModel):
    id: StrId
    service_id: Optional[StrId] = None
    description: str
    quantity: int
    unit_price: Decimal
    line_total: Decimal
    service: Optional[ServiceDetailResponse] = None
    model_config = ConfigDict(from_attributes=True)


class PaymentDetailResponse(BaseModel):
    id: StrId
    amount: Decimal
    payment_method: PaymentMethod
    payment_date: datetime
    model_config = ConfigDict(from_attributes=True)


class PatientSummaryResponse(BaseModel):
    id: StrId
    first_name: str
    last_name: str
    patient_number: str
    model_config = ConfigDict(from_attributes=True)


class AppointmentSummaryResponse(BaseModel):
    id: StrId
    appointment_date: date
    model_config = ConfigDict(from_attributes=True)


class InvoiceDetailResponse(BaseModel):
    id: StrId
    invoice_number: str
    patient_id: StrId
    appointment_id: Optional[StrId] = None
    invoice_date: date
    due_date: Optional[date] = None
    subtotal: Decimal
    tax_amount: Decimal
    discount_amount: Decimal
    total_amount: Decimal
    amount_paid: Decimal
    balance_due: Decimal
    status: InvoiceStatus
    notes: Optional[str] = None
    created_by_user_id: Optional[StrId] = None
    created_at: datetime
    updated_at: datetime
    patient: PatientSummaryResponse
    appointment: Optional[AppointmentSummaryResponse] = None
    line_items: List[InvoiceLineItemDetailResponse]
    payments: List[PaymentDetailResponse]
    model_config = ConfigDict(from_attributes=True)


# Process payment request schema
class ProcessPaymentRequest(BaseModel):
    invoice_id: str = Field(..., max_length=36)
    patient_id: str = Field(..., max_length=36)
    amount: Decimal = Field(..., max_digits=10, decimal_places=2)
    payment_method: PaymentMethod
    transaction_reference: Optional[str] = Field(None, max_length=100)
    notes: Optional[str] = None
    processed_by_user_id: Optional[str] = Field(None, max_length=36)
    
    @field_validator("amount")
    @classmethod
    def validate_amount_positive(cls, v):
        if v is not None and v <= 0:
            raise ValueError("amount must be greater than zero")
        return v


# Issue invoice request schema
class IssueInvoiceRequest(BaseModel):
    pass