from abc import ABC, abstractmethod

import numpy

from simses.analysis.data.energy_management import EnergyManagementData
from simses.analysis.data.system import SystemData
from simses.analysis.evaluation.result import EvaluationResult
from simses.commons.config.analysis.economic import EconomicAnalysisConfig


class RevenueStream(ABC):
    """Calculates the cashflow for each project year that is generated by the given RevenueStream.
    A RevenueStream can be seen as the financial impact of the various applications of a BESS such as peak shaving,
    self-consumption, frequency containment reserve, etc.. """

    def __init__(self, energy_management_data: EnergyManagementData, system_data: SystemData,
                 economic_analysis_config: EconomicAnalysisConfig):
        self._energy_management_data: EnergyManagementData = energy_management_data
        self._system_data: SystemData = system_data
        self._economic_analysis_config: EconomicAnalysisConfig = economic_analysis_config
        self._investment_cost: float = 0.0
        self._seconds_per_year = 24 * 60 * 60 * 365

    @abstractmethod
    def get_cashflow(self) -> numpy.ndarray:
        """
        Returns non-discounted cashflow for every project year for the given RevenueStream.

        Returns
        -------
        list of float
            List of cashflow for every project year
        """
        pass

    @abstractmethod
    def get_evaluation_results(self) -> [EvaluationResult]:
        """
        Returns list of EvaluationResults for the given RevenueStream.

        Returns
        -------
        list of EvaluationResult
            List of EvaluationResults
        """
        pass

    @abstractmethod
    def get_assumptions(self) -> [EvaluationResult]:
        """
        Returns list of assumptions for the given RevenueStream. Assumptions are handled with the
        EvaluationResults class.

        Returns
        -------
        list of EvaluationResult
            List of EvaluationResults, representing assumptions
        """
        pass

    def set_investment_cost(self, value: float) -> None:
        self._investment_cost = value

    def cash_time_series_to_project_years(self, cashflows: [float], time: [float]) -> [float]:
        """
        Converts a cashflow time series for every timestep to a cashflow time series for every project year.
        A project year is assumed to be 365 days long. A full leap year would therefore be one project year and one day.

        Parameters
        ----------
        cashflows :
            List of float, representing cashflow for each timestep
        time :
            List of float, unix timestamps for each simulation step

        Returns
        -------
        list of float
            List of float, representing cashflow for each project year.
        """
        cashflow_project_years = []
        start_time = time[0]
        cashflow_sum = 0
        add_last = True
        for (t, c) in zip(time, cashflows):
            if t < start_time + self._seconds_per_year:
                cashflow_sum += c
                add_last = True
            else:
                cashflow_project_years.append(cashflow_sum)
                cashflow_sum = 0
                cashflow_sum += c
                start_time = t
                add_last = False
        if add_last:
            cashflow_project_years.append(cashflow_sum)
        return cashflow_project_years

    @abstractmethod
    def close(self):
        pass
