from pydantic import BaseModel, ConfigDict, field_validator
from typing import Optional, List
from datetime import datetime, date


# CalculationSession Schemas
class CalculationsessionBase(BaseModel):
    session_id: str
    first_access_at: datetime
    last_access_at: datetime
    calculation_count: int
    user_agent: Optional[str] = None
    ip_address: Optional[str] = None


class CalculationsessionCreate(CalculationsessionBase):
    @field_validator("session_id")
    @classmethod
    def validate_session_id(cls, v: str) -> str:
        import uuid
        try:
            uuid.UUID(v)
        except ValueError:
            raise ValueError("Session ID must be valid UUID format")
        return v


class CalculationsessionUpdate(BaseModel):
    session_id: Optional[str] = None
    first_access_at: Optional[datetime] = None
    last_access_at: Optional[datetime] = None
    calculation_count: Optional[int] = None
    user_agent: Optional[str] = None
    ip_address: Optional[str] = None

    @field_validator("session_id")
    @classmethod
    def validate_session_id(cls, v: Optional[str]) -> Optional[str]:
        if v is not None:
            import uuid
            try:
                uuid.UUID(v)
            except ValueError:
                raise ValueError("Session ID must be valid UUID format")
        return v


class CalculationsessionResponse(CalculationsessionBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Calculation Schemas
class CalculationBase(BaseModel):
    session_id: str
    principal: float
    annual_interest_rate: float
    loan_term_months: int
    monthly_payment: float
    total_interest: float
    total_amount: float


class CalculationCreate(CalculationBase):
    @field_validator("principal")
    @classmethod
    def validate_principal(cls, v: float) -> float:
        if v < 1.00:
            raise ValueError("Principal must be greater than or equal to $1.00")
        if v > 100000000.00:
            raise ValueError("Principal must be less than or equal to $100,000,000.00")
        if v <= 0:
            raise ValueError("Principal must be positive")
        return round(v, 2)

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v: float) -> float:
        if v < 0.00:
            raise ValueError("Annual interest rate must be non-negative")
        if v > 99.99:
            raise ValueError("Annual interest rate must be less than or equal to 99.99%")
        return v

    @field_validator("loan_term_months")
    @classmethod
    def validate_loan_term_months(cls, v: int) -> int:
        if v < 1:
            raise ValueError("Loan term must be greater than or equal to 1 month")
        if v > 600:
            raise ValueError("Loan term must be less than or equal to 600 months")
        if v <= 0:
            raise ValueError("Loan term must be positive")
        return v


class CalculationUpdate(BaseModel):
    session_id: Optional[str] = None
    principal: Optional[float] = None
    annual_interest_rate: Optional[float] = None
    loan_term_months: Optional[int] = None
    monthly_payment: Optional[float] = None
    total_interest: Optional[float] = None
    total_amount: Optional[float] = None

    @field_validator("principal")
    @classmethod
    def validate_principal(cls, v: Optional[float]) -> Optional[float]:
        if v is not None:
            if v < 1.00:
                raise ValueError("Principal must be greater than or equal to $1.00")
            if v > 100000000.00:
                raise ValueError("Principal must be less than or equal to $100,000,000.00")
            if v <= 0:
                raise ValueError("Principal must be positive")
            return round(v, 2)
        return v

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v: Optional[float]) -> Optional[float]:
        if v is not None:
            if v < 0.00:
                raise ValueError("Annual interest rate must be non-negative")
            if v > 99.99:
                raise ValueError("Annual interest rate must be less than or equal to 99.99%")
        return v

    @field_validator("loan_term_months")
    @classmethod
    def validate_loan_term_months(cls, v: Optional[int]) -> Optional[int]:
        if v is not None:
            if v < 1:
                raise ValueError("Loan term must be greater than or equal to 1 month")
            if v > 600:
                raise ValueError("Loan term must be less than or equal to 600 months")
            if v <= 0:
                raise ValueError("Loan term must be positive")
        return v


class CalculationResponse(CalculationBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# AmortizationEntry Schemas
class AmortizationentryBase(BaseModel):
    calculation_id: str
    payment_number: int
    payment_date: date
    beginning_balance: float
    payment_amount: float
    principal_portion: float
    interest_portion: float
    ending_balance: float
    cumulative_interest: float
    cumulative_principal: float


class AmortizationentryCreate(AmortizationentryBase):
    @field_validator("payment_number")
    @classmethod
    def validate_payment_number(cls, v: int) -> int:
        if v <= 0:
            raise ValueError("Payment number must be positive integer")
        return v


class AmortizationentryUpdate(BaseModel):
    calculation_id: Optional[str] = None
    payment_number: Optional[int] = None
    payment_date: Optional[date] = None
    beginning_balance: Optional[float] = None
    payment_amount: Optional[float] = None
    principal_portion: Optional[float] = None
    interest_portion: Optional[float] = None
    ending_balance: Optional[float] = None
    cumulative_interest: Optional[float] = None
    cumulative_principal: Optional[float] = None

    @field_validator("payment_number")
    @classmethod
    def validate_payment_number(cls, v: Optional[int]) -> Optional[int]:
        if v is not None and v <= 0:
            raise ValueError("Payment number must be positive integer")
        return v


class AmortizationentryResponse(AmortizationentryBase):
    id: str
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


# Complex Response Schemas for Detail Endpoints
class CalculationDetailsResponse(CalculationResponse):
    amortization_entries: List[AmortizationentryResponse] = []

    model_config = ConfigDict(from_attributes=True)


# Calculation Input Schema
class CalculationInputSchema(BaseModel):
    principal: float
    annual_interest_rate: float
    loan_term_months: int
    session_id: str

    @field_validator("principal")
    @classmethod
    def validate_principal(cls, v: float) -> float:
        if v < 1.00:
            raise ValueError("Principal must be greater than or equal to $1.00")
        if v > 100000000.00:
            raise ValueError("Principal must be less than or equal to $100,000,000.00")
        if v <= 0:
            raise ValueError("Principal must be positive")
        return round(v, 2)

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v: float) -> float:
        if v < 0.00:
            raise ValueError("Annual interest rate must be non-negative")
        if v > 99.99:
            raise ValueError("Annual interest rate must be less than or equal to 99.99%")
        return v

    @field_validator("loan_term_months")
    @classmethod
    def validate_loan_term_months(cls, v: int) -> int:
        if v < 1:
            raise ValueError("Loan term must be greater than or equal to 1 month")
        if v > 600:
            raise ValueError("Loan term must be less than or equal to 600 months")
        if v <= 0:
            raise ValueError("Loan term must be positive")
        return v