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

StrId = Annotated[str, BeforeValidator(str)]


class CalculationinputBase(BaseModel):
    monthly_investment: Decimal = Field(..., max_digits=12, decimal_places=2, ge=Decimal("500"), le=Decimal("100000"))
    expected_return_rate: Decimal = Field(..., max_digits=5, decimal_places=2, ge=Decimal("1"), le=Decimal("30"))
    time_period: int = Field(..., ge=1, le=40)

    @field_validator("monthly_investment")
    @classmethod
    def validate_monthly_investment(cls, v):
        if v <= 0:
            raise ValueError("monthly_investment must be a positive non-zero value")
        return v

    @field_validator("expected_return_rate")
    @classmethod
    def validate_expected_return_rate(cls, v):
        if v <= 0:
            raise ValueError("expected_return_rate must be a positive non-zero value")
        return v

    @field_validator("time_period")
    @classmethod
    def validate_time_period(cls, v):
        if v <= 0:
            raise ValueError("time_period must be a positive non-zero value")
        return v


class CalculationinputCreate(CalculationinputBase):
    pass


class CalculationinputUpdate(BaseModel):
    monthly_investment: Optional[Decimal] = Field(None, max_digits=12, decimal_places=2, ge=Decimal("500"), le=Decimal("100000"))
    expected_return_rate: Optional[Decimal] = Field(None, max_digits=5, decimal_places=2, ge=Decimal("1"), le=Decimal("30"))
    time_period: Optional[int] = Field(None, ge=1, le=40)

    @field_validator("monthly_investment")
    @classmethod
    def validate_monthly_investment(cls, v):
        if v is not None and v <= 0:
            raise ValueError("monthly_investment must be a positive non-zero value")
        return v

    @field_validator("expected_return_rate")
    @classmethod
    def validate_expected_return_rate(cls, v):
        if v is not None and v <= 0:
            raise ValueError("expected_return_rate must be a positive non-zero value")
        return v

    @field_validator("time_period")
    @classmethod
    def validate_time_period(cls, v):
        if v is not None and v <= 0:
            raise ValueError("time_period must be a positive non-zero value")
        return v


class CalculationinputResponse(BaseModel):
    id: StrId
    monthly_investment: Decimal
    expected_return_rate: Decimal
    time_period: int
    timestamp: datetime
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


class CalculationresultBase(BaseModel):
    calculation_input_id: str = Field(..., max_length=36)
    total_investment: Decimal = Field(..., max_digits=15, decimal_places=2, ge=Decimal("0"))
    estimated_returns: Decimal = Field(..., max_digits=15, decimal_places=2)
    maturity_value: Decimal = Field(..., max_digits=15, decimal_places=2, ge=Decimal("0"))


class CalculationresultCreate(CalculationresultBase):
    pass


class CalculationresultUpdate(BaseModel):
    calculation_input_id: Optional[str] = Field(None, max_length=36)
    total_investment: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2, ge=Decimal("0"))
    estimated_returns: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2)
    maturity_value: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2, ge=Decimal("0"))


class CalculationresultResponse(BaseModel):
    id: StrId
    calculation_input_id: StrId
    total_investment: Decimal
    estimated_returns: Decimal
    maturity_value: Decimal
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


class ChartdatapointBase(BaseModel):
    calculation_input_id: str = Field(..., max_length=36)
    year: int = Field(..., ge=1)
    invested_amount: Decimal = Field(..., max_digits=15, decimal_places=2, ge=Decimal("0"))
    projected_value: Decimal = Field(..., max_digits=15, decimal_places=2, ge=Decimal("0"))

    @field_validator("year")
    @classmethod
    def validate_year(cls, v):
        if v <= 0:
            raise ValueError("year must be a positive non-zero value")
        return v

    @field_validator("invested_amount")
    @classmethod
    def validate_invested_amount(cls, v):
        if v < 0:
            raise ValueError("invested_amount must be >= 0")
        return v

    @field_validator("projected_value")
    @classmethod
    def validate_projected_value(cls, v):
        if v < 0:
            raise ValueError("projected_value must be >= 0")
        return v


class ChartdatapointCreate(ChartdatapointBase):
    pass


class ChartdatapointUpdate(BaseModel):
    calculation_input_id: Optional[str] = Field(None, max_length=36)
    year: Optional[int] = Field(None, ge=1)
    invested_amount: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2, ge=Decimal("0"))
    projected_value: Optional[Decimal] = Field(None, max_digits=15, decimal_places=2, ge=Decimal("0"))

    @field_validator("year")
    @classmethod
    def validate_year(cls, v):
        if v is not None and v <= 0:
            raise ValueError("year must be a positive non-zero value")
        return v

    @field_validator("invested_amount")
    @classmethod
    def validate_invested_amount(cls, v):
        if v is not None and v < 0:
            raise ValueError("invested_amount must be >= 0")
        return v

    @field_validator("projected_value")
    @classmethod
    def validate_projected_value(cls, v):
        if v is not None and v < 0:
            raise ValueError("projected_value must be >= 0")
        return v


class ChartdatapointResponse(BaseModel):
    id: StrId
    calculation_input_id: StrId
    year: int
    invested_amount: Decimal
    projected_value: Decimal
    created_at: datetime
    updated_at: datetime

    model_config = ConfigDict(from_attributes=True)


class CalculationDetailsResponse(BaseModel):
    id: StrId
    monthly_investment: Decimal
    expected_return_rate: Decimal
    time_period: int
    timestamp: datetime
    created_at: datetime
    updated_at: datetime
    calculation_result: Optional[CalculationresultResponse] = None
    chart_data_points: List[ChartdatapointResponse] = []

    model_config = ConfigDict(from_attributes=True)


class CalculationWithResultAndChartResponse(BaseModel):
    calculation_input: CalculationinputResponse
    calculation_result: CalculationresultResponse
    chart_data_points: List[ChartdatapointResponse]

    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)