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 ProjectProfitability(Base):
    """Snapshot of aggregated profitability metrics at the project level."""

    __tablename__ = "project_profitability_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)
    npv: Mapped[float | None] = mapped_column(Numeric(18, 2), nullable=True)
    irr_pct: Mapped[float | None] = mapped_column(
        Numeric(12, 6), nullable=True)
    payback_period_years: Mapped[float | None] = mapped_column(
        Numeric(12, 4), nullable=True
    )
    margin_pct: Mapped[float | None] = mapped_column(
        Numeric(12, 6), nullable=True)
    revenue_total: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    opex_total: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True
    )
    sustaining_capex_total: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True
    )
    capex: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    net_cash_flow_total: Mapped[float | None] = mapped_column(
        Numeric(18, 2), 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="profitability_snapshots")
    created_by: Mapped[User | None] = relationship("User")

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


class ScenarioProfitability(Base):
    """Snapshot of profitability metrics for an individual scenario."""

    __tablename__ = "scenario_profitability_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)
    npv: Mapped[float | None] = mapped_column(Numeric(18, 2), nullable=True)
    irr_pct: Mapped[float | None] = mapped_column(
        Numeric(12, 6), nullable=True)
    payback_period_years: Mapped[float | None] = mapped_column(
        Numeric(12, 4), nullable=True
    )
    margin_pct: Mapped[float | None] = mapped_column(
        Numeric(12, 6), nullable=True)
    revenue_total: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    opex_total: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True
    )
    sustaining_capex_total: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True
    )
    capex: Mapped[float | None] = mapped_column(
        Numeric(18, 2), nullable=True)
    net_cash_flow_total: Mapped[float | None] = mapped_column(
        Numeric(18, 2), 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="profitability_snapshots")
    created_by: Mapped[User | None] = relationship("User")

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