from pydantic import BaseModel, ConfigDict, field_validator
from typing import Optional
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("calculation_count")
    @classmethod
    def validate_calculation_count(cls, v):
        if v < 0:
            raise ValueError("Calculation count must be non-negative")
        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("calculation_count")
    @classmethod
    def validate_calculation_count(cls, v):
        if v is not None and v < 0:
            raise ValueError("Calculation count must be non-negative")
        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):
        if v <= 0:
            raise ValueError("Principal must be positive and greater than 0")
        if v > 100_000_000:
            raise ValueError("Principal must be less than or equal to 100,000,000")
        return round(v, 2)

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v):
        if v < 0:
            raise ValueError("Annual interest rate must be non-negative and greater than or equal to 0")
        if v >= 100:
            raise ValueError("Annual interest rate must be less than 100")
        return v

    @field_validator("loan_term_months")
    @classmethod
    def validate_loan_term_months(cls, v):
        if v <= 0:
            raise ValueError("Loan term months must be positive and greater than 0")
        if v > 600:
            raise ValueError("Loan term months must be less than or equal to 600")
        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):
        if v is not None:
            if v <= 0:
                raise ValueError("Principal must be positive and greater than 0")
            if v > 100_000_000:
                raise ValueError("Principal must be less than or equal to 100,000,000")
            return round(v, 2)
        return v

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v):
        if v is not None:
            if v < 0:
                raise ValueError("Annual interest rate must be non-negative and greater than or equal to 0")
            if v >= 100:
                raise ValueError("Annual interest rate must be less than 100")
        return v

    @field_validator("loan_term_months")
    @classmethod
    def validate_loan_term_months(cls, v):
        if v is not None:
            if v <= 0:
                raise ValueError("Loan term months must be positive and greater than 0")
            if v > 600:
                raise ValueError("Loan term months must be less than or equal to 600")
        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):
        if v <= 0:
            raise ValueError("Payment number must be positive and greater than 0")
        return v

    @field_validator("beginning_balance", "ending_balance")
    @classmethod
    def validate_balance(cls, v):
        if v < 0:
            raise ValueError("Balance must be non-negative (>= 0)")
        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):
        if v is not None and v <= 0:
            raise ValueError("Payment number must be positive and greater than 0")
        return v

    @field_validator("beginning_balance", "ending_balance")
    @classmethod
    def validate_balance(cls, v):
        if v is not None and v < 0:
            raise ValueError("Balance must be non-negative (>= 0)")
        return v


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

    model_config = ConfigDict(from_attributes=True)


# Complex response schema for calculation details endpoint
class CalculationDetailsResponse(CalculationResponse):
    amortization_entries: list[AmortizationentryResponse] = []

    model_config = ConfigDict(from_attributes=True)


# Schema for loan calculation input
class LoanCalculationInput(BaseModel):
    session_id: str
    principal: float
    annual_interest_rate: float
    loan_term_months: int

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

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v):
        if v < 0:
            raise ValueError("Annual interest rate must be non-negative and greater than or equal to 0")
        if v >= 100:
            raise ValueError("Annual interest rate must be less than 100")
        return v

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


# Schema for session tracking
class SessionTrackInput(BaseModel):
    session_id: str
    user_agent: Optional[str] = None
    ip_address: Optional[str] = None