import numpy as np
from recommendations.Costs import Costs
from recommendations.recommendation_utils import override_costs


class SolarPvRecommendations:
    # Solar panel specs based on Eurener 400s solar panels
    # https://midsummerwholesale.co.uk/buy/eurener/eurener-400w-mepv-zebra-ab-half-cut-mono
    # Approximate area of the solar panels
    SOLAR_PANEL_AREA = 1.79
    # Wattage per panel - this is based on the average wattage of a solar panel being between 250w and 420w
    # This was previously set to 250w, but has been upped to 400 based on the systems used by Cotswolrd Energy Group
    SOLAR_PANEL_WATTAGE = 400

    MAX_SYSTEM_WATTAGE = 6000
    MIN_SYSTEM_WATTAGE = 1000

    def __init__(self, property_instance):
        """
        :param property_instance: Instance of the Property class, for the home associated to property_id
        """

        self.property = property_instance
        self.costs = Costs(self.property)

        self.recommendation = []

    @staticmethod
    def trim_solar_wattage_options(scenarios_with_wattage):
        # Initialize the list with the first element, assuming the list is not empty
        trimmed_list = [scenarios_with_wattage[0]]

        # Iterate over the list starting from the second element
        for scenario in scenarios_with_wattage[1:]:
            # Compare the second element (index 1) of the current tuple with the last tuple in the trimmed list
            if scenario[1] > trimmed_list[-1][1]:
                trimmed_list.append(scenario)

        return trimmed_list

    def mds_recommend(self, phase=None, solar_pv_percentage=0.5):
        # For specific usage within the mds report

        solar_pv_roof_area = self.property.get_solar_pv_roof_area(solar_pv_percentage)

        number_solar_panels = np.floor(solar_pv_roof_area / self.SOLAR_PANEL_AREA)
        solar_panel_wattage = number_solar_panels * self.SOLAR_PANEL_WATTAGE

        solar_panel_wattage = np.clip(
            a=solar_panel_wattage, a_min=self.MIN_SYSTEM_WATTAGE, a_max=self.MAX_SYSTEM_WATTAGE
        )

        # We now have a property which is potentially suitable for solar PV
        roof_coverage_percent = round(solar_pv_percentage * 100)
        # Given the wattage, we estimate the cost of the solar PV system. This is based on the MCS database
        # of solar PV installations
        cost_result = self.costs.solar_pv(wattage=solar_panel_wattage, has_battery=False)
        kw = np.floor(solar_panel_wattage / 100) / 10

        description = (f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) p"
                       f"anel system on {round(roof_coverage_percent)}% the roof.")

        return [
            {
                "phase": phase,
                "parts": [],
                "type": "solar_pv",
                "description": description,
                "starting_u_value": None,
                "new_u_value": None,
                "sap_points": None,
                "already_installed": False,
                **cost_result,
                # This is required for simulating the SAP impact. solar_pv_percentage is between 0 & 1 so we scale
                # back up here
                "photo_supply": roof_coverage_percent,
                "has_battery": False
            }
        ]

    def is_solar_pv_valid(self):
        is_valid_property_type = self.property.data["property-type"] in ["House", "Bungalow", "Maisonette"]
        is_valid_roof_type = (
            self.property.roof["is_flat"] or self.property.roof["is_pitched"] or self.property.roof["is_roof_room"]
        )
        # If there is no existing solar PV, the photo-supply field will be None or a missing value
        has_no_existing_solar_pv = self.property.data["photo-supply"] in [
            None, 0, self.property.DATA_ANOMALY_MATCHES
        ]

        return is_valid_property_type and is_valid_roof_type and has_no_existing_solar_pv

    def recommend(self, phase):
        """
        We check if a property is potentially suitable for solar PV based on the following criteria:
        - The property is a house or bungalow
        - The property has a flat or pitched roof
        - The property does not have existing solar pv
        :return:
        """

        if not self.is_solar_pv_valid():
            return

        solar_pv_percentage = self.property.solar_pv_percentage
        # We round up to the neaest 10%
        solar_pv_percentage = np.ceil(solar_pv_percentage * 10) / 10

        # For the solar recommendations, we produce the following scenarios:
        # 1) Solar panels only, we present a high, medium and low coverage
        # 2) With and without battery
        roof_coverage_scenarios = [
            solar_pv_percentage - 0.1, solar_pv_percentage,
        ]
        if solar_pv_percentage <= 0.4:
            roof_coverage_scenarios.append(solar_pv_percentage + 0.1)
        # We make sure we haven't gone too low or high - we allow no more than 60% coverage
        roof_coverage_scenarios = [v for v in roof_coverage_scenarios if 0 <= v <= 0.6]
        # If we only have two scenarios, we add a coverage scenario 10% less than the smallest
        if len(roof_coverage_scenarios) == 2:
            roof_coverage_scenarios.insert(0, roof_coverage_scenarios[0] - 0.1)
        battery_scenarios = [False, True]

        scenarios_with_wattage = []
        for roof_coverage in roof_coverage_scenarios:
            # We now have a property which is potentially suitable for solar PV
            solar_pv_roof_area = self.property.get_solar_pv_roof_area(roof_coverage)

            number_solar_panels = np.floor(solar_pv_roof_area / self.SOLAR_PANEL_AREA)
            solar_panel_wattage = number_solar_panels * self.SOLAR_PANEL_WATTAGE

            if solar_panel_wattage < self.MIN_SYSTEM_WATTAGE:
                continue

            solar_panel_wattage = np.clip(
                a=solar_panel_wattage, a_min=self.MIN_SYSTEM_WATTAGE, a_max=self.MAX_SYSTEM_WATTAGE
            )
            scenarios_with_wattage.append((roof_coverage, solar_panel_wattage))

        # We trim the scenarios, so that we don't have duplicate wattages
        scenarios_with_wattage = self.trim_solar_wattage_options(scenarios_with_wattage)

        # Produce the cross product of the scenarios
        scenarios = [
            (roof, wattage, battery) for roof, wattage in scenarios_with_wattage for battery in battery_scenarios
        ]
        # We deduce the wattage of the solar panels based on the roof coverage

        for roof_coverage, solar_panel_wattage, has_battery in scenarios:
            # We now have a property which is potentially suitable for solar PV
            roof_coverage_percent = round(roof_coverage * 100)
            # Given the wattage, we estimate the cost of the solar PV system. This is based on the MCS database
            # of solar PV installations
            cost_result = self.costs.solar_pv(wattage=solar_panel_wattage, has_battery=has_battery)
            kw = np.floor(solar_panel_wattage / 100) / 10

            if has_battery:
                description = (f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) panel system on "
                               f"{round(roof_coverage_percent)}% the roof, with a battery storage system.")
            else:
                description = (f"Install a {kw} kilowatt-peak (kWp) solar photovoltaic (PV) p"
                               f"anel system on {round(roof_coverage_percent)}% the roof.")

            already_installed = "solar_pv" in self.property.already_installed
            if already_installed:
                cost_result = override_costs(cost_result)

            self.recommendation.append(
                {
                    "phase": phase,
                    "parts": [],
                    "type": "solar_pv",
                    "description": description,
                    "starting_u_value": None,
                    "new_u_value": None,
                    "sap_points": None,
                    "already_installed": already_installed,
                    **cost_result,
                    # This is required for simulating the SAP impact. solar_pv_percentage is between 0 & 1 so we scale
                    # back up here
                    "photo_supply": 100 * roof_coverage,
                    "has_battery": has_battery
                }
            )
