from typing import Annotated, Optional, List, Dict, Any
from pydantic import BaseModel, Field, ConfigDict, BeforeValidator, field_validator, model_validator
from decimal import Decimal
from datetime import datetime

StrId = Annotated[str, BeforeValidator(str)]


class CalculationBase(BaseModel):
    monthly_investment: Decimal = Field(..., max_digits=15, decimal_places=2, gt=0, le=10000000)
    investment_period_years: int = Field(..., ge=1, le=40)
    expected_return_rate: Decimal = Field(..., max_digits=5, decimal_places=2, ge=1, le=30)
    session_id: Optional[str] = Field(None, max_length=255)

    @field_validator("monthly_investment", "expected_return_rate")
    @classmethod
    def round_to_two_decimals(cls, v):
        if v is None:
            return v
        return v.quantize(Decimal("0.01"))


class CalculationCreate(CalculationBase):
    total_investment: Decimal = Field(..., max_digits=15, decimal_places=2)
    estimated_returns: Decimal = Field(..., max_digits=15, decimal_places=2)
    maturity_value: Decimal = Field(..., max_digits=15, decimal_places=2)

    @field_validator("total_investment", "estimated_returns", "maturity_value")
    @classmethod
    def round_computed_values(cls, v):
        if v is None:
            return v
        return v.quantize(Decimal("0.01"))


class CalculationUpdate(BaseModel):
    monthly_investment: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2, gt=0, le=10000000)
    investment_period_years: Optional[int] = Field(None, ge=1, le=40)
    expected_return_rate: Optional[Decimal] = Field(None, max_digits=5, decimal_places=2, ge=1, le=30)
    total_investment: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2)
    estimated_returns: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2)
    maturity_value: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2)
    session_id: Optional[str] = Field(None, max_length=255)

    @field_validator("monthly_investment", "expected_return_rate", "total_investment", "estimated_returns", "maturity_value")
    @classmethod
    def round_decimals(cls, v):
        if v is None:
            return v
        return v.quantize(Decimal("0.01"))


class CalculationResponse(BaseModel):
    id: StrId
    monthly_investment: Decimal
    investment_period_years: int
    expected_return_rate: Decimal
    total_investment: Decimal
    estimated_returns: Decimal
    maturity_value: Decimal
    session_id: Optional[str]
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


class CalculationbreakdownBase(BaseModel):
    period: int = Field(..., ge=1)
    invested_amount: Decimal = Field(..., max_digits=15, decimal_places=2)
    interest_earned: Decimal = Field(..., max_digits=15, decimal_places=2)
    total_value: Decimal = Field(..., max_digits=15, decimal_places=2)

    @field_validator("invested_amount", "interest_earned", "total_value")
    @classmethod
    def round_breakdown_values(cls, v):
        if v is None:
            return v
        return v.quantize(Decimal("0.01"))


class CalculationbreakdownCreate(CalculationbreakdownBase):
    calculation_id: str = Field(..., max_length=36)


class CalculationbreakdownUpdate(BaseModel):
    period: Optional[int] = Field(None, ge=1)
    invested_amount: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2)
    interest_earned: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2)
    total_value: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2)

    @field_validator("invested_amount", "interest_earned", "total_value")
    @classmethod
    def round_decimals(cls, v):
        if v is None:
            return v
        return v.quantize(Decimal("0.01"))


class CalculationbreakdownResponse(BaseModel):
    id: StrId
    calculation_id: StrId
    period: int
    invested_amount: Decimal
    interest_earned: Decimal
    total_value: Decimal
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


class CalculationDetailsResponse(BaseModel):
    id: StrId
    monthly_investment: Decimal
    investment_period_years: int
    expected_return_rate: Decimal
    total_investment: Decimal
    estimated_returns: Decimal
    maturity_value: Decimal
    session_id: Optional[str]
    created_at: datetime
    updated_at: datetime
    calculation_breakdowns: List[CalculationbreakdownResponse]

    model_config = ConfigDict(from_attributes=True)


from typing import Generic, TypeVar

T = TypeVar("T")


class PaginatedResponse(BaseModel, Generic[T]):
    items: List[T]
    total: int
    limit: int
    offset: int

    model_config = ConfigDict(from_attributes=True)