from __future__ import annotations

from datetime import datetime
from typing import TYPE_CHECKING

from sqlalchemy import JSON, DateTime, ForeignKey, Integer, Numeric, String
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql import func

from config.database import Base

if TYPE_CHECKING:  # pragma: no cover
    from .project import Project
    from .scenario import Scenario
    from .user import User


class ProjectCapexSnapshot(Base):
    """Snapshot of aggregated capex metrics at the project level."""

    __tablename__ = "project_capex_snapshots"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    project_id: Mapped[int] = mapped_column(
        ForeignKey("projects.id", ondelete="CASCADE"), nullable=False, index=True
    )
    created_by_id: Mapped[int | None] = mapped_column(
        ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
    )
    calculation_source: Mapped[str | None] = mapped_column(
        String(64), nullable=True)
    calculated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )
    currency_code: Mapped[str | None] = mapped_column(String(3), nullable=True)
    total_capex: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    contingency_pct: Mapped[float | None] = mapped_column(
        Numeric(12, 6), nullable=True)
    contingency_amount: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    total_with_contingency: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    component_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
    payload: Mapped[dict | None] = mapped_column(JSON, 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()
    )

    project: Mapped[Project] = relationship(
        "Project", back_populates="capex_snapshots"
    )
    created_by: Mapped[User | None] = relationship("User")

    def __repr__(self) -> str:  # pragma: no cover
        return (
            "ProjectCapexSnapshot(id={id!r}, project_id={project_id!r}, total_capex={total_capex!r})".format(
                id=self.id, project_id=self.project_id, total_capex=self.total_capex
            )
        )


class ScenarioCapexSnapshot(Base):
    """Snapshot of capex metrics for an individual scenario."""

    __tablename__ = "scenario_capex_snapshots"

    id: Mapped[int] = mapped_column(Integer, primary_key=True)
    scenario_id: Mapped[int] = mapped_column(
        ForeignKey("scenarios.id", ondelete="CASCADE"), nullable=False, index=True
    )
    created_by_id: Mapped[int | None] = mapped_column(
        ForeignKey("users.id", ondelete="SET NULL"), nullable=True, index=True
    )
    calculation_source: Mapped[str | None] = mapped_column(
        String(64), nullable=True)
    calculated_at: Mapped[datetime] = mapped_column(
        DateTime(timezone=True), nullable=False, server_default=func.now()
    )
    currency_code: Mapped[str | None] = mapped_column(String(3), nullable=True)
    total_capex: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    contingency_pct: Mapped[float | None] = mapped_column(
        Numeric(12, 6), nullable=True)
    contingency_amount: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    total_with_contingency: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    component_count: Mapped[int | None] = mapped_column(Integer, nullable=True)
    payload: Mapped[dict | None] = mapped_column(JSON, 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="capex_snapshots"
    )
    created_by: Mapped[User | None] = relationship("User")

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