from typing import Dict, Union
from BaseUtility import Definitions
from etl.epc_clean.epc_attributes.attribute_utils import clean_description, find_keyword, handle_mixed_translation


class HotWaterAttributes(Definitions):
    # HEATER_TYPES refer to the main devices used for heating water. These devices can be powered by different energy
    # sources.
    HEATER_TYPES = [
        'gas instantaneous',  # A water heater that provides hot water only as it is needed
        'electric heat pump',
        # A device that transfers heat from a source (like the ground or air) into a building to provide hot water
        'electric immersion',  # An electric device that heats water using electric resistance
        'gas boiler',  # A boiler that uses gas as fuel to heat water
        'oil boiler',  # A boiler that uses oil as fuel to heat water
        'electric instantaneous',  # Similar to gas instantaneous, but uses electricity as its energy source
        'gas multipoint',  # A gas water heater that can supply hot water to multiple points of use at once
        'heat pump',  # A general category for heat pumps, regardless of the energy source
        'solid fuel boiler',  # burns solid materials to generate heat for water heating and/or space heating
        'solid fuel range cooker',
        'room heaters',  # Generic/unspecified category
        'electric multipoint',
        'single-point gas',
    ]

    # SYSTEM_TYPES refer to the larger system within which the heater operates.
    SYSTEM_TYPES = [
        'from main system',  # The hot water is provided by the main heating system of the building
        'from secondary system',
        # The hot water is provided by a secondary (or supplementary) heating system in the building
        'from second main heating system',  # Same as 'from secondary system'
        'community scheme',  # The hot water is provided by a community heating system
        "water heater",
    ]

    # THERMOSTAT_CHARACTERISTICS refer to features related to temperature control in the system.
    THERMOSTAT_CHARACTERISTICS = [
        'no cylinder thermostat',  # Indicates that the hot water cylinder does not have a thermostat
    ]

    # HEATING_SCOPE refers to the specific role of the heater in the heating system.
    HEATING_SCOPE = [
        'water heating only',  # Indicates that the heater is used only for water heating, not space heating
    ]

    # ENERGY_RECOVERY refers to systems or epc_attributes that recover and utilize waste energy.
    ENERGY_RECOVERY = [
        'waste water heat recovery',
        # A system that recovers heat from waste hot water (e.g., from showers) to preheat incoming cold water
        'flue gas heat recovery',  # A system that recovers heat from the flue gases of a boiler or furnace
    ]

    # TARIFF_TYPE relates to the energy pricing structure or tariff that applies to the system.
    TARIFF_TYPE = [
        'standard tariff',  # The energy used by the system is billed at a standard rate
        'off-peak',  # The energy used by the system is primarily used during off-peak hours when rates are lower
    ]

    # EXTRA_FEATURES are additional features or systems that enhance the efficiency or capability of the hot water
    # system.
    EXTRA_FEATURES = [
        'plus solar',  # Indicates that the system includes solar thermal panels to assist in water heating
    ]

    # CHP_SYSTEMS is specifically for the Combined Heat and Power systems.
    CHP_SYSTEMS = [
        'chp',
        # Combined Heat and Power system, a system that simultaneously produces heat and electricity from the same
        # energy source
    ]

    # NO_SYSTEM_PRESENT_KEYWORDS indicates there is no specific hot water system present.
    NO_SYSTEM_PRESENT_KEYWORDS = [
        'no system present',
    ]

    # DISTRIBUTION_SYSTEM_KEYWORDS refers to components that assist in the distribution of the heated water.
    DISTRIBUTION_SYSTEM_KEYWORDS = [
        'circulator',  # A pump used to circulate hot water in the system
    ]

    # Indicate if the information is assumed
    ASSUMED = [
        'assumed'
    ]

    # in some rare instances, especially in older homes, a range cooker can be part of a larger system that also
    # includes a boiler for heating water. In these cases, the cooker may help to heat the water, but this setup is
    # not common, especially in modern homes.
    APPLIANCE_SYSTEMS = [
        'gas range cooker',  # A gas-powered range cooker
        'oil range cooker'
    ]

    # Descriptions which represent the same thing
    SYNONYMS = {
        'from second main heating system': 'from secondary system',
    }

    WELSH_TEXT = {
        "ogçör brif system": "from main system",
        "o r brif system": "from main system",
        "o’r brif system": "from main system",
        "ogçör brif system, adfer gwres nwyon ffliw": "from main system, flue gas heat recovery",
        "bwyler/cylchredydd nwy": "gas boiler/circulator",
        "ogçör brif system, dim thermostat ar y silindr": "from main system, no cylinder thermostat",
        "o r brif system, dim thermostat ar y silindr": "from main system, no cylinder thermostat",
        "twymwr tanddwr, an-frig": "electric immersion, off-peak",
        "ogçör brif system, gydag ynnigçör haul": "from main system, plus solar",
        "twymwr tanddwr, tarriff safonol": "electric immersion, standard tariff",
        "trydan ar unwaith yn y fan lle maegçön cael ei ddefnyddio": 'electric instantaneous at point of use',
        "o gynllun cymunedol": "community scheme",
        "o'r brif system": "from main system",
        "trydan ar unwaith yn y fan lle mae'n cael ei ddefnyddio": 'electric instantaneous at point of use',
        "popty estynedig olew, dim thermostat ar y silindr": "oil range cooker, no cylinder thermostat",
        "cynllun cymunedol": "community scheme",
        "nwy wrth fwy nag un pwynt": "gas multipoint",
        "popty estynedig olew": "oil range cooker",
        "dim system ar gael rhagdybir bod twymwr tanddwr trydan": "no system present electric immersion assumed",
        "o'r brif system, dim thermostat ar y silindr": "from main system, no cylinder thermostat",
        "trydan ar unwaith yn y fan lle maegçön cael ei ddefnyddio, adfer gwres d+¦r gwastraff": "electric "
                                                                                                 "instantaneous at "
                                                                                                 "point of use, "
                                                                                                 "waste water heat "
                                                                                                 "recovery",
        "ogçör brif system, adfer gwres d+¦r gwastraff": "from main system, waste water heat recovery",
        "twymwr tanddwr, tarriff safonol, adfer gwres d+¦r gwastraff": "electric immersion, standard tariff, waste "
                                                                       "water heat recovery",
        "ogçör brif system, dim thermostat ar y silindr, adfer gwres nwyon ffliw": "from main system, no cylinder "
                                                                                   "thermostat, flue gas heat recovery",
        "ogçör brif system, gydag ynnigçör haul, adfer gwres nwyon ffliw": "from main system, plus solar, flue gas "
                                                                           "heat recovery",
        "o r brif system, gydag ynni r haul, dim thermostat ar y silindr": "from main system, plus solar, no cylinder "
                                                                           "thermostat",
        "o r brif system, gydag ynni r haul": "from main system, plus solar",
        "pwmp gwres": "heat pump"
    }

    NODATA_DESCRIPTIONS = [
        "sap05 hot-water",
        "sap hot-water"
    ]

    def __init__(self, description: str):
        self.description: str = clean_description(description.lower()).strip()

        self.nodata = not self.description or description in self.DATA_ANOMALY_MATCHES or (
            self.description in self.NODATA_DESCRIPTIONS
        )

        translation = self.WELSH_TEXT.get(self.description)

        if translation:
            self.nodata = False
            self.description = translation

        # We handle seemind occurances of mixed translations
        self.description = handle_mixed_translation(self.description)

        if not self.nodata and not any(
            self._keyword_in_description(keywords)
            for keywords in [
                self.HEATER_TYPES,
                self.SYSTEM_TYPES,
                self.THERMOSTAT_CHARACTERISTICS,
                self.HEATING_SCOPE,
                self.ENERGY_RECOVERY,
                self.TARIFF_TYPE,
                self.EXTRA_FEATURES,
                self.CHP_SYSTEMS,
                self.NO_SYSTEM_PRESENT_KEYWORDS,
                self.APPLIANCE_SYSTEMS,
                self.DISTRIBUTION_SYSTEM_KEYWORDS
            ]
        ):
            raise ValueError('Invalid description')

    def _keyword_in_description(self, keywords):
        return any(keyword in self.description for keyword in keywords)

    def process(self) -> Dict[str, Union[str, bool]]:

        if self.nodata:
            return {
                "heater_type": None,
                "system_type": None,
                "thermostat_characteristics": None,
                "heating_scope": None,
                "energy_recovery": None,
                "tariff_type": None,
                "extra_features": None,
                "chp_systems": None,
                "distribution_system": None,
                "no_system_present": None,
                "assumed": None,
                "appliance": None,
            }

        result: Dict[str, Union[str, bool]] = {
            "heater_type": find_keyword(self.description, self.HEATER_TYPES),
            "system_type": find_keyword(self.description, self.SYSTEM_TYPES, self.SYNONYMS),
            "thermostat_characteristics": find_keyword(self.description, self.THERMOSTAT_CHARACTERISTICS),
            "heating_scope": find_keyword(self.description, self.HEATING_SCOPE),
            "energy_recovery": find_keyword(self.description, self.ENERGY_RECOVERY),
            "tariff_type": find_keyword(self.description, self.TARIFF_TYPE),
            "extra_features": find_keyword(self.description, self.EXTRA_FEATURES),
            "chp_systems": find_keyword(self.description, self.CHP_SYSTEMS),
            "distribution_system": find_keyword(self.description, self.DISTRIBUTION_SYSTEM_KEYWORDS),
            "no_system_present": find_keyword(self.description, self.NO_SYSTEM_PRESENT_KEYWORDS),
            "appliance": find_keyword(self.description, self.APPLIANCE_SYSTEMS),
        }

        assumed_found = find_keyword(self.description, self.ASSUMED)
        result["assumed"] = True if assumed_found else False

        return result
