# scripts/helpers/features.py
import numpy as np
import math
import random
from typing import Union
from .utils import apply_noise, NoiseType, clip_value


# ---------------------------------------------
# Temperature
# ---------------------------------------------
def temperature(day_of_year: int, temp_shift: float, noise: NoiseType = None) -> float:
    """
    Generate a yearly temperature cycle based on a sinus curve.

    Parameters
    ----------
    day_of_year : int
        Day index within the year (1–365).
    temp_shift : float
        Regional temperature offset (e.g. +2 °C for warmer regions).
    noise : NoiseType
        Optional measurement noise.

    Returns
    -------
    float
        Simulated temperature value (°C).
    """
    base = 15 + 10 * math.sin(2 * math.pi * day_of_year / 365) + temp_shift
    return apply_noise(base, noise)


# ---------------------------------------------
# Humidity
# ---------------------------------------------
def humidity(temperature: float, noise: NoiseType = None) -> float:
    """
    Generate humidity inversely related to temperature.

    Warmer → lower humidity.

    Parameters
    ----------
    temperature : float
        Ambient temperature.
    noise : NoiseType
        Optional noise to simulate sensor inaccuracy.

    Returns
    -------
    float
        Relative humidity percentage (20–100 %).
    """
    base = 90 - (temperature - 15) * 2
    base = clip_value(base, 20, 100)
    return apply_noise(base, noise)


# ---------------------------------------------
# Rainfall
# ---------------------------------------------
def rain_mm(
    temperature: float,
    humidity: float,
    rain_factor: float,
    world_randomness: float = 1.0,
    noise: NoiseType = None,
) -> float:
    """
    Compute rainfall amount based on humidity, temperature, and stochastic events.

    - `rain_factor` influences regional rain tendency.
    - `world_randomness` controls how deterministic vs chaotic the world is.
      (0 = fully deterministic, 1 = normal, >1 = more chaotic)

    Parameters
    ----------
    temperature : float
        Ambient temperature.
    humidity : float
        Relative humidity.
    rain_factor : float
        Regional coefficient (higher = more rain).
    world_randomness : float
        Global randomness multiplier.
    noise : NoiseType
        Optional sensor measurement noise.

    Returns
    -------
    float
        Rain amount in millimeters (≥ 0).
    """
    # Deterministic rain probability
    rain_prob = np.clip((humidity - 60) / 40, 0, 1)
    if temperature < 5:
        rain_prob *= 1.2
    elif temperature > 25:
        rain_prob *= 0.5

    # Deterministic mode
    if world_randomness == 0:
        base = 3 * rain_factor * rain_prob
    # Stochastic world mode
    else:
        if random.random() < rain_prob * world_randomness:
            base = np.random.exponential(3 * rain_factor)
        else:
            base = 0

    return max(0, apply_noise(base, noise))


# ---------------------------------------------
# Air Pressure
# ---------------------------------------------
def pressure(rain_mm: float, noise: NoiseType = None) -> float:
    """
    Simulate air pressure decreasing with rain intensity.

    Parameters
    ----------
    rain_mm : float
        Rain amount.
    noise : NoiseType
        Optional noise to simulate measurement variability.

    Returns
    -------
    float
        Air pressure in hPa (clipped to 980–1040).
    """
    base = 1020 - 0.6 * rain_mm
    base = clip_value(base + np.random.normal(0, 1), 980, 1040)
    return apply_noise(base, noise)


# ---------------------------------------------
# Optional: Dew Point
# ---------------------------------------------
def dew_point(temperature: float, humidity: float, noise: NoiseType = None) -> float:
    """
    Calculate dew point from temperature and humidity.

    Simple empirical approximation:
        dew_point = T - ((100 - RH) / 5)

    Parameters
    ----------
    temperature : float
        Ambient temperature.
    humidity : float
        Relative humidity (0–100).
    noise : NoiseType
        Optional noise.

    Returns
    -------
    float
        Dew point temperature.
    """
    base = temperature - ((100 - humidity) / 5.0)
    return apply_noise(base, noise)
