from __future__ import annotations

from datetime import date, datetime
from typing import TYPE_CHECKING

from sqlalchemy import (
    Date,
    DateTime,
    ForeignKey,
    Integer,
    Numeric,
    String,
    Text,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship, validates

from sqlalchemy.sql import func

from config.database import Base
from .enums import CostBucket, FinancialCategory, sql_enum
from services.currency import normalise_currency

if TYPE_CHECKING:  # pragma: no cover
    from .scenario import Scenario


class FinancialInput(Base):
    """Line-item financial assumption attached to a scenario."""

    __tablename__ = "financial_inputs"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    scenario_id: Mapped[int] = mapped_column(
        ForeignKey("scenarios.id", ondelete="CASCADE"), nullable=False, index=True
    )
    name: Mapped[str] = mapped_column(String(255), nullable=False)
    category: Mapped[FinancialCategory] = mapped_column(
        sql_enum(FinancialCategory, name="financialcategory"), nullable=False
    )
    cost_bucket: Mapped[CostBucket | None] = mapped_column(
        sql_enum(CostBucket, name="costbucket"), nullable=True
    )
    amount: Mapped[float] = mapped_column(Numeric(18, 2), nullable=False)
    currency: Mapped[str | None] = mapped_column(String(3), nullable=True)
    effective_date: Mapped[date | None] = mapped_column(Date, nullable=True)
    notes: Mapped[str | None] = mapped_column(Text, nullable=True)
    created_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )
    updated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now(), onupdate=func.now()
    )

    scenario: Mapped["Scenario"] = relationship(
        "Scenario", back_populates="financial_inputs")

    @validates("currency")
    def _validate_currency(self, key: str, value: str | None) -> str | None:
        return normalise_currency(value)

    def __repr__(self) -> str:  # pragma: no cover
        return f"FinancialInput(id={self.id!r}, scenario_id={self.scenario_id!r}, name={self.name!r})"
