from __future__ import annotations

import numpy as np

from pythermalcomfort.classes_input import EPMVInputs
from pythermalcomfort.classes_return import EPMV
from pythermalcomfort.models.pmv_ppd_iso import pmv_ppd_iso
from pythermalcomfort.utilities import Models, Units


def pmv_e(
    tdb: float | list[float],
    tr: float | list[float],
    vr: float | list[float],
    rh: float | list[float],
    met: float | list[float],
    clo: float | list[float],
    e_coefficient: float | list[float],
    wme: float | list[float] = 0,
    units: str = Units.SI.value,
    limit_inputs: bool = True,
) -> EPMV:
    """Return Adjusted Predicted Mean Votes with Expectancy Factor (ePMV).
    This index was developed by Fanger, P. O. et al. (2002). In non-air-
    conditioned buildings in warm climates, occupants may sense the warmth as
    being less severe than the PMV predicts. The main reason is low
    expectations, but a metabolic rate that is estimated too high can also
    contribute to explaining the difference. An extension of the PMV model that
    includes an expectancy factor is introduced for use in non-air-conditioned
    buildings in warm climates [Fanger2002]_.

    Parameters
    ----------
    tdb : float or list of floats
        Dry bulb air temperature, default in [°C] in [°F] if `units` = 'IP'.
    tr : float or list of floats
        Mean radiant temperature, default in [°C] in [°F] if `units` = 'IP'.
    vr : float or list of floats
        Relative air speed, default in [m/s] in [fps] if `units` = 'IP'.

        .. note::
            vr is the relative air speed caused by body movement and not the air
            speed measured by the air speed sensor. The relative air speed is the sum of the
            average air speed measured by the sensor plus the activity-generated air speed
            (Vag). Where Vag is the activity-generated air speed caused by motion of
            individual body parts. vr can be calculated using the function
            :py:meth:`pythermalcomfort.utilities.v_relative`.

    rh : float or list of floats
        Relative humidity, [%].
    met : float or list of floats
        Metabolic rate, [met].
    clo : float or list of floats
        Clothing insulation, [clo].

        .. note::
            this is the basic insulation also known as the intrinsic clothing insulation value of the
            clothing ensemble (`I`:sub:`cl,r`), this is the thermal insulation from the skin
            surface to the outer clothing surface, including enclosed air layers, under actual
            environmental conditions. This value is not the total insulation (`I`:sub:`T,r`).
            The dynamic clothing insulation, clo, can be calculated using the function
            :py:meth:`pythermalcomfort.utilities.clo_dynamic_iso`.

    e_coefficient : float or list of floats
        Expectancy factor.
    wme : float or list of floats, optional
        External work, [met]. Defaults to 0.
    units : {'SI', 'IP'}, optional
        Select the SI (International System of Units) or the IP (Imperial Units) system.
        Supported values are 'SI' and 'IP'. Defaults to 'SI'.
    limit_inputs : bool, optional
        By default, if the inputs are outside the standard applicability limits the
        function returns nan. If False, returns pmv and ppd values even if input values are
        outside the applicability limits of the model. Defaults to True.

        .. note::
            The ISO 7730 2005 limits are 10 < tdb [°C] < 30, 10 < tr [°C] < 40,
            0 < vr [m/s] < 1, 0.8 < met [met] < 4, 0 < clo [clo] < 2, and -2 < PMV < 2.

    Returns
    -------
    EPMV
        A dataclass containing the Adjusted Predicted Mean Votes with Expectancy Factor. See :py:class:`~pythermalcomfort.classes_return.EPMV` for more details.
        To access the `e_pmv` value, use the `e_pmv` attribute of the returned `e_pmv` instance, e.g., `result.e_pmv`.

    Examples
    --------
    .. code-block:: python

        from pythermalcomfort.models import pmv_e
        from pythermalcomfort.utilities import v_relative, clo_dynamic_iso

        tdb = 28
        tr = 28
        rh = 50
        v = 0.1
        met = 1.4
        clo = 0.5
        # calculate relative air speed
        v_r = v_relative(v=v, met=met)
        # Calculate dynamic clothing
        clo_d = clo_dynamic_iso(clo=clo, met=met, v=v)
        results = pmv_e(tdb, tr, v_r, rh, met, clo_d, e_coefficient=0.6)
        print(results.e_pmv)  # 0.48

    """
    # Validate inputs using the EPMVInputs class
    EPMVInputs(
        tdb=tdb,
        tr=tr,
        vr=vr,
        rh=rh,
        met=met,
        clo=clo,
        e_coefficient=e_coefficient,
        wme=wme,
        units=units,
    )

    default_kwargs = {"units": units, "limit_inputs": limit_inputs}
    met = np.array(met)
    _pmv = pmv_ppd_iso(
        tdb=tdb,
        tr=tr,
        vr=vr,
        rh=rh,
        met=met,
        clo=clo,
        wme=wme,
        model=Models.iso_7730_2005.value,
        **default_kwargs,
    ).pmv
    met = np.where(_pmv > 0, met * (1 + _pmv * (-0.067)), met)
    _pmv = pmv_ppd_iso(
        tdb=tdb,
        tr=tr,
        vr=vr,
        rh=rh,
        met=met,
        clo=clo,
        wme=wme,
        model=Models.iso_7730_2005.value,
        **default_kwargs,
    ).pmv

    e_pmv_value = np.around(_pmv * e_coefficient, 2)

    return EPMV(e_pmv=e_pmv_value)
