import pandas as pd

from backend.Property import Property
from typing import List
from recommendations.Costs import Costs
from recommendations.recommendation_utils import override_costs, check_use_survey
from backend.ml_models.AnnualBillSavings import AnnualBillSavings


class LightingRecommendations:
    # We introduce a SAP limit to lighting, which is based on empirical findings. We do see cases where lighting is
    # worth more than 2 points, but this is unlikely in the context of other upgrades that can be made to the property
    SAP_LIMIT = 2

    # If more than 50% of the lighting is LEDs already, the limit is 1 SAP point
    SAP_LOWER_LIMIT = 1

    def __init__(self, property_instance: Property, materials: List):
        """
        :param property_instance: Instance of the Property class, for the home associated to property_id
        :param materials: List of materials to be used in the recommendations
        """

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

        material = [
            material for material in materials if material["type"] == "low_energy_lighting_installation"
        ]
        if len(material) != 1:
            raise ValueError("Incorrect number of low energy lighting materials specified")

        self.material = material[0]
        self.recommendation = []

    @classmethod
    def get_sap_limit(cls, lighting_energy_efficiency: str, lighting_proportion: float):
        """
        Lighting seems to be a more straight forward measure to estimate SAP points for, based on the starting
        energy efficiency rating.

        We seem to have the following brackes based on % of LEDs in outlets
        Very poor: 0 - 9%
        Poor: 10 - 24%
        Average: 25 - 44%
        Good: 45 - 69%
        Very good: 70 - 100%
        :return:
        """

        if lighting_energy_efficiency == "Very Good":
            return 0

        if lighting_energy_efficiency in ["Good", "Average"]:
            return cls.SAP_LOWER_LIMIT

        # If lighting_energy_efficiency is missing, we'll use the proportion of low energy lighting
        if not lighting_energy_efficiency or pd.isnull(lighting_energy_efficiency):
            if lighting_proportion >= 0.7:
                return 0
            if lighting_proportion >= 0.25:
                return cls.SAP_LOWER_LIMIT
            return cls.SAP_LIMIT

        return cls.SAP_LIMIT

    @staticmethod
    def estimate_lighting_impact(number_of_bulbs: int):
        """
        Placeholder function to estimate the actual energy savings of LEDs vs traditional lighting
        :return:
        """

        wattage_incandescent = 60  # wattage of typical incandescent bulb in watts
        wattage_led = 10  # wattage of typical LED bulb in watts
        hours_per_day = 3  # average usage in hours per day
        days_per_year = 365  # days in a year
        national_grid_carbon_intensity = 162  # gCO2/kWh, average for 2023 in the UK

        # Energy usage per year for incandescent and LED bulbs (in kWh)
        energy_usage_incandescent_per_year = (wattage_incandescent / 1000) * hours_per_day * days_per_year
        energy_usage_led_per_year = (wattage_led / 1000) * hours_per_day * days_per_year

        # Energy savings per bulb per year
        energy_savings_per_bulb_per_year = energy_usage_incandescent_per_year - energy_usage_led_per_year

        # Total energy savings for all bulbs
        total_energy_savings_per_year = energy_savings_per_bulb_per_year * number_of_bulbs

        carbon_reduction_grams = total_energy_savings_per_year * national_grid_carbon_intensity
        carbon_reduction_tonnes = carbon_reduction_grams / 1_000_000  # converting grams to tonnes

        return total_energy_savings_per_year, carbon_reduction_tonnes

    def recommend(self, phase=0):
        """
        This method will check if there are any lighting fittings that aren't low energy.

        If there are, the will recommend fitting the rest of the outlets with low energy lighting fittings
        :return:
        """

        if "sap05" in self.property.lighting.get("clean_description", "").lower():
            return

        if self.property.lighting["low_energy_proportion"] >= 1:
            return

        leds_recommendation_config = next(
            (r for r in self.property.non_invasive_recommendations if r["type"] == "low_energy_lighting"),
            {}
        )

        number_lighting_outlets = self.property.number_lighting_outlets

        # Number non lel outlets
        number_non_lel_outlets = number_lighting_outlets - (
            self.property.lighting["low_energy_proportion"] * number_lighting_outlets
        )

        number_non_lel_outlets = round(number_non_lel_outlets)

        if number_non_lel_outlets == 0:
            return

        # Get the cost of the fittings
        if leds_recommendation_config.get("cost"):
            raise NotImplementedError("Costs from for low energy lighting have not been implemented")

        cost_result = self.costs.low_energy_lighting(
            number_of_lights=number_non_lel_outlets,
            material=self.material
        )

        if number_non_lel_outlets == 1:
            description = "Install low energy lighting in 1 remaining outlet"
        else:
            description = "Install low energy lighting in %s outlets" % str(number_non_lel_outlets)

        heat_demand_change, carbon_change = self.estimate_lighting_impact(number_non_lel_outlets)

        already_installed = "low_energy_lighting" in self.property.already_installed
        if already_installed:
            cost_result = override_costs(cost_result)
            description = "Low energy lighting has already been installed, no further action required"

        if leds_recommendation_config.get("sap_points") is not None:
            # This could be zero points
            sap_points = leds_recommendation_config["sap_points"]
        else:
            sap_points = round(2 * (number_non_lel_outlets / number_lighting_outlets), 2)

        self.recommendation = [
            {
                "phase": phase,
                "parts": [],
                "type": "low_energy_lighting",
                "measure_type": "low_energy_lighting",
                "description": description,
                "starting_u_value": None,
                "new_u_value": None,
                "already_installed": already_installed,
                # For SAP points, we use the fact that lighting is usually worth 2 points and we scale this to
                # the proportion of lights that will be set to low energy
                "sap_points": sap_points,
                "kwh_savings": heat_demand_change,
                "energy_cost_savings": heat_demand_change * AnnualBillSavings.ELECTRICITY_PRICE_CAP,
                "co2_equivalent_savings": carbon_change,
                "description_simulation": {
                    "lighting-energy-eff": "Very Good",
                    "lighting-description": "Low energy lighting in all fixed outlets",
                    "low-energy-lighting": 100,
                },
                **cost_result,
                "survey": check_use_survey(
                    leds_recommendation_config, self.property.epc_record.has_been_remodelled
                ),
                "innovation_rate": self.material["innovation_rate"],
            }
        ]
