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


class LoancalculationBase(BaseModel):
    principal: Decimal = Field(..., gt=0, le=1000000000, description="The loan amount borrowed")
    annual_interest_rate: Decimal = Field(..., ge=0, lt=100, description="Annual interest rate as a percentage")
    loan_term_months: int = Field(..., gt=0, le=600, description="Duration of the loan in months")
    calculation_method: str = Field(default="standard-amortization", description="Method used for calculation")
    session_id: Optional[str] = Field(None, description="Reference to calculation session")

    @field_validator("principal")
    @classmethod
    def validate_principal(cls, v):
        if v <= 0:
            raise ValueError("Principal must be a positive number greater than 0")
        return v

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v):
        if v < 0 or v >= 100:
            raise ValueError("Annual interest rate must be between 0 and 100 (exclusive of 100)")
        return v

    @field_validator("loan_term_months")
    @classmethod
    def validate_loan_term_months(cls, v):
        if v < 1 or v > 600:
            raise ValueError("Loan term must be a positive integer between 1 and 600 months")
        return v


class LoancalculationCreate(LoancalculationBase):
    pass


class LoancalculationUpdate(BaseModel):
    principal: Optional[Decimal] = None
    annual_interest_rate: Optional[Decimal] = None
    loan_term_months: Optional[int] = None
    monthly_payment: Optional[Decimal] = None
    total_interest: Optional[Decimal] = None
    total_amount: Optional[Decimal] = None
    calculation_method: Optional[str] = None
    session_id: Optional[str] = None

    @field_validator("principal")
    @classmethod
    def validate_principal(cls, v):
        if v is not None and v <= 0:
            raise ValueError("Principal must be a positive number greater than 0")
        return v

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v):
        if v is not None and (v < 0 or v >= 100):
            raise ValueError("Annual interest rate must be between 0 and 100 (exclusive of 100)")
        return v

    @field_validator("loan_term_months")
    @classmethod
    def validate_loan_term_months(cls, v):
        if v is not None and (v < 1 or v > 600):
            raise ValueError("Loan term must be a positive integer between 1 and 600 months")
        return v


class LoancalculationResponse(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: str
    principal: Decimal
    annual_interest_rate: Decimal
    loan_term_months: int
    monthly_payment: Decimal
    total_interest: Decimal
    total_amount: Decimal
    calculation_method: str
    session_id: Optional[str]
    created_at: datetime
    updated_at: datetime


class AmortizationscheduleentryBase(BaseModel):
    loan_calculation_id: str
    payment_number: int
    payment_date: Optional[date] = None
    beginning_balance: Decimal
    payment_amount: Decimal
    principal_portion: Decimal
    interest_portion: Decimal
    ending_balance: Decimal
    cumulative_interest: Decimal
    cumulative_principal: Decimal


class AmortizationscheduleentryCreate(AmortizationscheduleentryBase):
    pass


class AmortizationscheduleentryUpdate(BaseModel):
    loan_calculation_id: Optional[str] = None
    payment_number: Optional[int] = None
    payment_date: Optional[date] = None
    beginning_balance: Optional[Decimal] = None
    payment_amount: Optional[Decimal] = None
    principal_portion: Optional[Decimal] = None
    interest_portion: Optional[Decimal] = None
    ending_balance: Optional[Decimal] = None
    cumulative_interest: Optional[Decimal] = None
    cumulative_principal: Optional[Decimal] = None


class AmortizationscheduleentryResponse(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: str
    loan_calculation_id: str
    payment_number: int
    payment_date: Optional[date]
    beginning_balance: Decimal
    payment_amount: Decimal
    principal_portion: Decimal
    interest_portion: Decimal
    ending_balance: Decimal
    cumulative_interest: Decimal
    cumulative_principal: Decimal
    created_at: datetime
    updated_at: datetime


class LoancalculationWithScheduleResponse(BaseModel):
    model_config = ConfigDict(from_attributes=True)

    id: str
    principal: Decimal
    annual_interest_rate: Decimal
    loan_term_months: int
    monthly_payment: Decimal
    total_interest: Decimal
    total_amount: Decimal
    calculation_method: str
    session_id: Optional[str]
    created_at: datetime
    updated_at: datetime
    amortization_schedule_entries: List[AmortizationscheduleentryResponse]


class LoanCalculationInput(BaseModel):
    principal: Decimal = Field(..., gt=0, le=1000000000, description="The loan amount borrowed")
    annual_interest_rate: Decimal = Field(..., ge=0, lt=100, description="Annual interest rate as a percentage")
    loan_term_months: int = Field(..., gt=0, le=600, description="Duration of the loan in months")
    session_id: Optional[str] = Field(None, description="Optional session identifier")

    @field_validator("principal")
    @classmethod
    def validate_principal(cls, v):
        if v <= 0:
            raise ValueError("Principal must be a positive number greater than 0")
        return v

    @field_validator("annual_interest_rate")
    @classmethod
    def validate_annual_interest_rate(cls, v):
        if v < 0 or v >= 100:
            raise ValueError("Annual interest rate must be between 0 and 100 (exclusive of 100)")
        return v

    @field_validator("loan_term_months")
    @classmethod
    def validate_loan_term_months(cls, v):
        if v < 1 or v > 600:
            raise ValueError("Loan term must be a positive integer between 1 and 600 months")
        return v