from __future__ import annotations

from datetime import datetime
from enum import Enum
from typing import TYPE_CHECKING

from sqlalchemy import (
    JSON,
    DateTime,
    Enum as SQLEnum,
    ForeignKey,
    Integer,
    Numeric,
    String,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.sql import func

from config.database import Base
from .metadata import ResourceType, StochasticVariable

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


class DistributionType(str, Enum):
    """Supported stochastic distribution families for simulations."""

    NORMAL = "normal"
    TRIANGULAR = "triangular"
    UNIFORM = "uniform"
    LOGNORMAL = "lognormal"
    CUSTOM = "custom"


class SimulationParameter(Base):
    """Probability distribution settings for scenario simulations."""

    __tablename__ = "simulation_parameters"

    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)
    distribution: Mapped[DistributionType] = mapped_column(
        SQLEnum(DistributionType), nullable=False
    )
    variable: Mapped[StochasticVariable | None] = mapped_column(
        SQLEnum(StochasticVariable), nullable=True
    )
    resource_type: Mapped[ResourceType | None] = mapped_column(
        SQLEnum(ResourceType), nullable=True
    )
    mean_value: Mapped[float | None] = mapped_column(
        Numeric(18, 4), nullable=True)
    standard_deviation: Mapped[float | None] = mapped_column(
        Numeric(18, 4), nullable=True)
    minimum_value: Mapped[float | None] = mapped_column(
        Numeric(18, 4), nullable=True)
    maximum_value: Mapped[float | None] = mapped_column(
        Numeric(18, 4), nullable=True)
    unit: Mapped[str | None] = mapped_column(String(32), nullable=True)
    configuration: 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="simulation_parameters"
    )

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