import numpy as np
import pandas as pd
from pvlib.solarposition import sun_rise_set_transit_spa
from agroclimatic_indicators import (
    agro_indicators,
    animal_indicators,
    climatic_indicators,
)


## Compute Agronomics
def compute_vpd(df: pd.DataFrame):
    """
    Compute VPD.

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing sensor data.

    Returns
    -------
    arraylike
        VPD at df's timestep
    """
    return agro_indicators.vpd(df.air_temperature, df.relative_humidity)



def compute_et0(
    df: pd.DataFrame,
    latitude: float,
    longitude: float
):
    """
    Compute reference evapotranspiration.

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing sensor data.

    latitude : float
        Latitude of the location.
    longitude : float
        Longitude of the location

    Returns
    -------
    arraylike
        Daily reference evapotranspiration.
    """

    irradiance = (
        (df.photon_flux_density.resample("1h").mean() / 2.1).resample("1d").sum()
    )
    T = df.air_temperature.resample("1d").mean()
    Tmin = df.air_temperature.resample("1d").min()
    Tmax = df.air_temperature.resample("1d").max()
    RHmin = df.relative_humidity.resample("1d").min()
    RHmax = df.relative_humidity.resample("1d").max()
    WS = df.wind_speed.resample("1d").mean()
    JJulien = np.unique(df.index.day_of_year)

    l = [
        agro_indicators.et0(
            irradiance.iloc[i],
            T.iloc[i],
            Tmax.iloc[i],
            Tmin.iloc[i],
            RHmin.iloc[i],
            RHmax.iloc[i],
            WS.iloc[i],
            JJulien[i],
            latitude,
            np.array([longitude]),
        )
        for i in range(len(JJulien))
    ]

    if len(JJulien) == 1:
        et0 = l[0]
    else:
        et0 = l

    return et0


## Compute Climatics


def compute_frostday(df: pd.DataFrame):
    """
    Define if day is a frost day (min temperature below 0°C).

    Parameters
    ----------
    df : DataFrame
        Air sensors data.

    Returns
    -------
    bool
        True if day is a frost day, else False.
    """

    T = df.air_temperature.resample("1d").min()

    ind = climatic_indicators.frost_bool(T, 0)
    if ind.shape[0] == 1:
        ind = ind.iloc[0]
    return ind


def compute_strongfrostday(df: pd.DataFrame):
    """
    Define if day is a strong frost day (min temperature below -3°C).

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing temperature data.

    Returns
    -------
    bool
        True if day is a strong frost day, else False.
    """

    T = df.air_temperature.resample("1d").min()

    ind = climatic_indicators.frost_bool(T, -3)
    if ind.shape[0] == 1:
        ind = ind.iloc[0]

    return ind


def compute_thermalstressday(df: pd.DataFrame, stress_threshold: float = 35):
    """
    Define if daily temperature is a source of thermal stress (max temperature above stress threshold).

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing air temperature data.

    stress_threshold : float
        Threshold temperature of stress (degrees Celsius).

    Returns
    -------
    bool
        True if day is a day with thermal stress, else False.
    """

    T = df.air_temperature.resample("1d").max()

    ind = climatic_indicators.thermalstress_bool(T, stress_threshold)
    if ind.shape[0] == 1:
        ind = ind.iloc[0]

    return ind


def compute_summerday(df: pd.DataFrame):
    """
    Define if day is a summer day (max temperature above 25°C).

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing air temperature data.

    Returns
    -------
    bool
        True if day is a summer day, else False.
    """
    T = df.air_temperature.resample("1d").max()

    ind = climatic_indicators.summerday_bool(T)
    if ind.shape[0] == 1:
        ind = ind.iloc[0]

    return ind


def compute_scorchday(df: pd.DataFrame, scorch_threshold: float = 25):
    """
    Define if day is a scorching day (jour échaudant) (max temperature above scorch threshold).

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing air temperature data.

    scorch_threshold : float
        Temperature threshold above which the day is considered scorching (degrees Celsius).

    Returns
    -------
    bool
        True if day is a scorching day, else False.
    """
    T = df.air_temperature.resample("1d").max()

    ind = climatic_indicators.scorch_bool(T, scorch_threshold)

    if ind.shape[0] == 1:
        ind = ind.iloc[0]

    return ind


def compute_tropicalnight(df: pd.DataFrame, latitude: float, longitude: float):
    """
    Define if night is a tropical night (min temperature above 20°C).

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing air temperature data.

    latitude : float
        Latitude of the location.

    longitude : float
        Longitude of the location.

    Returns
    -------
    bool
        True if night is a tropical night, else False.
    """
    if len(df) == 0:
        return None

    df = sun_rise_set_transit_spa(
        times=df.index, latitude=latitude, longitude=longitude
    ).merge(df["air_temperature"], left_index=True, right_index=True)
    df = df.loc[(df.index < df["sunrise"]) | (df.index > df["sunset"])]

    df_minnight = df.resample("24h", offset="12h")["air_temperature"].min()

    tropicalnight = climatic_indicators.tropicalnight_bool(df_minnight)

    if tropicalnight.shape[0] == 1:
        tropicalnight = tropicalnight.iloc[0]

    return tropicalnight


def compute_mintempnight(df: pd.DataFrame, latitude: float, longitude: float):
    """
    Return minimal night temperature.

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing air temperature data.

    latitude : float
        Latitude of the location.

    longitude : float
        Longitude of the location.

    Returns
    -------
    float
        Min night temperature (degrees Celsius).
    """
    if len(df) == 0:
        return None

    df = sun_rise_set_transit_spa(df.index, latitude, longitude).merge(
        df["air_temperature"], left_index=True, right_index=True
    )
    df = df.loc[(df.index < df["sunrise"]) | (df.index > df["sunset"])]

    df_minnight = df.resample("24h", offset="12h")["air_temperature"].min()

    if df_minnight.shape[0] == 1:
        df_minnight = df_minnight.iloc[0]

    return df_minnight


def compute_hli(
    df: pd.DataFrame,
):
    """
    Computes HLI (heat load index).

    Parameters
    ----------
    df : DataFrame
        The input dataframe containing air temperature data.

    Returns
    -------
    float
        HLI value.
    """

    ind = animal_indicators.hli(
        irradiance=df.photon_flux_density,
        air_temperature=df.air_temperature,
        RH=df.relative_humidity,
        wind_speed=df.wind_speed,
    )

    if ind.size == 1:
        df_ind = float(ind)
    else:
        df_ind = pd.Series(ind, index=df.index)

    return df_ind