from recommendations.Costs import Costs
from recommendations.recommendation_utils import check_simulation_difference, override_costs
from backend.Property import Property
from etl.epc_clean.epc_attributes.MainheatControlAttributes import MainheatControlAttributes


class HeatingControlRecommender:

    def __init__(self, property_instance: Property):
        self.property = property_instance
        self.costs = Costs(self.property)

        self.recommendation = []

    def recommend(self, heating_description, phase, description_prefix="", description_suffix=""):

        # TODO: Many of these functions are quite similar. We can possibly create a single wrapper function that
        #  takes in the heating description and the description prefix/suffix, and then creates the appropriate
        #  output
        # Reset the recommendations
        self.recommendation = []

        # This first iteration of the recommender will provide very basic recommendation
        # We recommend heating controls based on the main heating system
        if heating_description in ["Room heaters, electric"]:
            self.recommend_room_heaters_electric_controls(phase=phase)
            return

        if heating_description in ["Electric storage heaters", "Electric storage heaters, radiators"]:
            self.recommend_high_heat_retention_controls(description_prefix=description_prefix, phase=phase)
            return

        if heating_description in ["Boiler and radiators, mains gas"]:
            # We can recommend roomstat programmer trvs
            self.recommend_roomstat_programmer_trvs(description_suffix=description_suffix, phase=phase)
            # We can also recommend time and temperature zone controls
            self.recommend_time_temperature_zone_controls(description_suffix=description_suffix, phase=phase)

            return

        if heating_description in ["Boiler and radiators, electric"]:
            self.recommend_roomstat_programmer_trvs(phase=phase)
            return

        if heating_description in ["Air source heat pump, radiators, electric"]:
            # For an ASHP, we can recommend time and temperature zone controls, as well as programmer, trvs and a bypass
            # which are common configurations for ASHPs
            self.recommend_time_temperature_zone_controls(phase=phase)
            # self.recommend_programmer_trvs_bypass()

    def recommend_room_heaters_electric_controls(self, phase):
        """
        If the home has Room heaters, electric, we start by identifying potential heating controls that could
        be upgraded, that would provide a practical impact. This will be the least invasive improvement.

        We can then consider the heating system itself
        :return:
        """
        if (self.property.epc_record.mainheatc_energy_eff in ["Poor", "Very Poor", "Average"]) or (
            self.property.main_heating_controls["clean_description"] in ["Programmer and room thermostat"]
        ):
            # We recommend Programmer and appliance thermostats as the heating control. This has an average energy
            # efficiency rating, and is likely to be more efficient than the current heating controls. if the
            # rating is poor or very poor, the home may have a Programmer and room thermostat, which is less efficient
            # than a Programmer and appliance thermostats, because it allows for much more granular control at not
            # just a room level but individual heater/appliance level

            # Note: A room thermostat is commonly placed in a hallway, and it measures the temperature of the air
            # surrounding it. It then sends a signal to the heating system to turn on or off, depending on the
            # temperature. An appliance thermostat, on the other hand, is placed on the heater/appliance itself, and
            # measures the temperature of the heater/appliance. This allows for much more granular control, and
            # prevents overheating.

            # In order to cost, we check if the property already has a programmer, and therefor we will just need to
            # add the cost of the appliance thermostats

            has_programmer = self.property.main_heating_controls["switch_system"] == "programmer"

            ending_config = MainheatControlAttributes("Programmer and appliance thermostats").process()
            # We look at what has changed in the ending config, and compare it to the current config

            # We use this to determine how we should be updating the config
            simulation_config = check_simulation_difference(
                new_config=ending_config, old_config=self.property.main_heating_controls
            )
            # This upgrade will only take the heating system to average energy efficiency
            simulation_config["mainheatc_energy_eff_ending"] = "Good"

            self.recommendation.append(
                {
                    "phase": phase,
                    "type": "heating",
                    "measure_type": "programmer_appliance_thermostat",
                    "description": "upgrade heating controls to Programmer and Appliance or Smart Thermostats",
                    **self.costs.programmer_and_appliance_thermostat(has_programmer=has_programmer),
                    "simulation_config": simulation_config,
                    "innovation_rate": 0.0,
                }
            )

        # We don't implement any other recommendations right now
        return

    def recommend_high_heat_retention_controls(self, phase, description_prefix=""):
        """
        When applicable, we recommend upgrading the heating controls to high heat retention controls. This is a
        specific type of control system that is designed to work with electric storage heaters. It is a more
        efficient control system than the standard controls that come with electric storage heaters.

        We can then consider the heating system itself

        If there is a description prefix, this means there is a dual heating system and so we need to add this to the
        description

        :return:
        """
        new_description = "Controls for high heat retention storage heaters"
        if description_prefix:
            new_description = f"{description_prefix}, {new_description}"

        # We recommend upgrading to Celect type controls
        ending_config = MainheatControlAttributes(new_description).process()
        # We look at what has changed in the ending config, and compare it to the current config
        simulation_config = check_simulation_difference(
            new_config=ending_config, old_config=self.property.main_heating_controls
        )
        # This upgrade will only take the heating system to average energy efficiency
        if self.property.epc_record.mainheatc_energy_eff in ["Poor", "Very Poor", "Average"]:
            simulation_config["mainheatc_energy_eff_ending"] = "Good"
        else:
            simulation_config["mainheatc_energy_eff_ending"] = self.property.epc_record.mainheatc_energy_eff

        description_simulation = {
            "mainheatcont-description": new_description,
            "mainheatc-energy-eff": simulation_config["mainheatc_energy_eff_ending"]
        }

        self.recommendation.append(
            {
                "phase": phase,
                "type": "heating",
                "measure_type": "celect_type_controls",
                "description": "Upgrade heating controls to High Heat Retention Storage Heater Controls",
                **self.costs.celect_type_controls(),
                "simulation_config": simulation_config,
                "description_simulation": description_simulation,
                "innovation_rate": 0.0,
            }
        )

        # We don't implement any other recommendations right now
        return

    def recommend_roomstat_programmer_trvs(self, phase, description_suffix=""):
        """
        If the home has a boiler and radiators, mains gas, we start by identifying potential heating controls that could
        be upgraded, that would provide a practical impact.

        The criteria for recommending an upgrade to heating controls are (one of these must be true)
        1) There are no controls
        2) No programmer
        3) No room thermostat
        4) No TRVs


        :return:
        """

        # We check if we have the conditions to recommend this upgrade

        needs_programmer = self.property.main_heating_controls["switch_system"] is None
        needs_room_thermostat = self.property.main_heating_controls["thermostatic_control"] is None
        needs_trvs = self.property.main_heating_controls["trvs"] is None

        can_recommend = (
            (self.property.main_heating_controls["no_control"] is not None) or
            needs_programmer or
            needs_room_thermostat or
            needs_trvs
        )

        if not can_recommend:
            return

        new_controls_description = "Programmer, room thermostat and TRVS"
        if description_suffix:
            new_controls_description = f"{new_controls_description}, {description_suffix}"

        ending_config = MainheatControlAttributes(new_controls_description).process()
        # We use this to determine how we should be updating the config
        simulation_config = check_simulation_difference(
            new_config=ending_config, old_config=self.property.main_heating_controls
        )
        # This upgrade will only take the heating system to average energy efficiency
        # If the current system is below good, we make it good
        if self.property.epc_record.mainheatc_energy_eff in ["Poor", "Very Poor", "Average"]:
            simulation_config["mainheatc_energy_eff_ending"] = "Good"
        else:
            simulation_config["mainheatc_energy_eff_ending"] = self.property.epc_record.mainheatc_energy_eff

        description_simulation = {
            "mainheatcont-description": new_controls_description,
            "mainheatc-energy-eff": simulation_config["mainheatc_energy_eff_ending"]
        }

        has_programmer = not needs_programmer
        has_room_thermostat = not needs_room_thermostat
        has_trvs = not needs_trvs

        cost_result = self.costs.roomstat_programmer_trvs(
            number_heated_rooms=self.property.epc_record.number_heated_rooms,
            has_programmer=has_programmer,
            has_room_thermostat=has_room_thermostat,
            has_trvs=has_trvs
        )

        description = "Upgrade heating controls to Room thermostat, programmer and TRVs"

        already_installed = "roomstat_programmer_trvs" in self.property.already_installed
        if already_installed:
            cost_result = override_costs(cost_result)
            description = "Heating controls have already been upgraded, no further action needed."

        self.recommendation.append(
            {
                "type": "heating",
                "measure_type": "roomstat_programmer_trvs",
                "phase": phase,
                "parts": [],
                "description": description,
                **cost_result,
                "starting_u_value": None,
                "new_u_value": None,
                "sap_points": None,
                "already_installed": already_installed,
                "simulation_config": simulation_config,
                "description_simulation": description_simulation,
                "innovation_rate": 0.0
            }
        )

        return

    def recommend_time_temperature_zone_controls(self, phase, description_suffix=""):
        """
        If the home has a boiler, we can recommend time and temperature zone controls. This is a more advanced
        and more efficient control system than the standard controls that come with a boiler. However, it may come
        with a higher cost and more involved usage
        :return:
        """

        # We check if the efficiency of the current heating controls is good or below, and

        # Conditions for installation are as follows:
        # 1) The current heating controls are not time and temperature zone controls
        # 2) The current heating controls are not already at 'Very Good' or above

        if (
            (self.property.main_heating_controls["thermostatic_control"] == "time and temperature zone control") or
            (self.property.epc_record.mainheatc_energy_eff in ["Very Good"])
        ):
            # No recommendation needed
            return

        new_controls_description = "Time and temperature zone control"
        if description_suffix:
            new_controls_description = f"{new_controls_description}, {description_suffix}"

        ending_config = MainheatControlAttributes(new_controls_description).process()

        # We use this to determine how we should be updating the config
        simulation_config = check_simulation_difference(
            new_config=ending_config, old_config=self.property.main_heating_controls
        )

        # If the current system is below very good, we make it very good
        if self.property.epc_record.mainheatc_energy_eff in ["Poor", "Very Poor", "Average", "Good"]:
            simulation_config["mainheatc_energy_eff_ending"] = "Very Good"
        else:
            simulation_config["mainheatc_energy_eff_ending"] = self.property.epc_record.mainheatc_energy_eff

        description_simulation = {
            "mainheatcont-description": new_controls_description,
            "mainheatc-energy-eff": simulation_config["mainheatc_energy_eff_ending"]
        }
        cost_result = self.costs.time_and_temperature_zone_control(
            number_heated_rooms=self.property.epc_record.number_heated_rooms
        )

        description = (
            "Upgrade heating controls to Smart Thermostats, room sensors and smart radiator valves (time & "
            "temperature zone control)"
        )

        already_installed = "time_temperature_zone_control" in self.property.already_installed
        if already_installed:
            cost_result = override_costs(cost_result)
            description = "Heating controls have already been upgraded, no further action needed."

        self.recommendation.append(
            {
                "type": "heating",
                "phase": phase,
                "measure_type": "time_temperature_zone_control",
                "parts": [],
                "description": description,
                **cost_result,
                "starting_u_value": None,
                "new_u_value": None,
                "sap_points": None,
                "already_installed": already_installed,
                "simulation_config": simulation_config,
                "description_simulation": description_simulation,
                "innovation_rate": 0.0
            }
        )

    def recommend_programmer_trvs_bypass(self):

        # We don't perform any checks here - this is likely to be used in conjunction with an ASHP recommendation
        new_controls_description = "Programmer, TRVs and bypass"
        ending_config = MainheatControlAttributes(new_controls_description).process()
        simulation_config = check_simulation_difference(
            new_config=ending_config, old_config=self.property.main_heating_controls
        )
        # Only adjust if the current system is below good
        if self.property.epc_record.mainheatc_energy_eff in ["Poor", "Very Poor"]:
            simulation_config["mainheatc_energy_eff_ending"] = "Average"
        else:
            simulation_config["mainheatc_energy_eff_ending"] = self.property.epc_record.mainheatc_energy_eff

        description_simulation = {
            "mainheatcont-description": new_controls_description,
            "mainheatc-energy-eff": simulation_config["mainheatc_energy_eff_ending"]
        }

        has_programmer = self.property.main_heating_controls["switch_system"] == "programmer"
        has_trvs = self.property.main_heating_controls["trvs"] is not None
        has_bypass = self.property.main_heating_controls["auxiliary_systems"] == "bypass"

        cost_result = self.costs.programmer_trvs_bypass(
            number_heated_rooms=self.property.epc_record.number_heated_rooms,
            has_trvs=has_trvs,
            has_programmer=has_programmer,
            has_bypass=has_bypass
        )

        description = "Install a Bypass valve, TRVs and a Programmer"

        already_installed = "programmer_trvs_bypass" in self.property.already_installed
        if already_installed:
            cost_result = override_costs(cost_result)
            description = "Heating controls have already been upgraded, no further action needed."

        self.recommendation.append(
            {
                "type": "heating",
                "measure_type": "programmer_trvs_bypass",
                "parts": [],
                "description": description,
                **cost_result,
                "starting_u_value": None,
                "new_u_value": None,
                "sap_points": None,
                "already_installed": already_installed,
                "simulation_config": simulation_config,
                "description_simulation": description_simulation
            }
        )
