from typing import Callable, Union
from collections.abc import Mapping
from datatypes.epc.walls import EpcWallDescriptions
from datatypes.epc.construction_age_band import EpcConstructionAgeBand
from datatypes.epc.efficiency import EpcEfficiency

# Unique combinations
wall_map = {
    # Cavity walls
    ('Cavity', 'FilledCavity'): EpcWallDescriptions.cavity_filled_cavity,
    ('Cavity', 'Internal'): EpcWallDescriptions.cavity_internal_insulation,
    ('Cavity', 'External'): EpcWallDescriptions.cavity_external_insulation,
    ('Cavity', 'FilledCavityPlusInternal'): EpcWallDescriptions.cavity_filled_plus_internal,
    ('Cavity', 'FilledCavityPlusExternal'): EpcWallDescriptions.cavity_filled_plus_external,
    ('Cavity', 'AsBuilt'): None,  # To be classified
    ('Cavity', 'Unknown'): None,  # To be classified

    # System built walls
    ('System', 'External'): EpcWallDescriptions.system_external_insulation,
    ('System', 'Internal'): EpcWallDescriptions.system_internal_insulation,
    ('System', 'AsBuilt'): None,  # To be classified
    ('System', 'Unknown'): None,

    # Timber Frame walls
    ('Timber Frame', 'Internal'): EpcWallDescriptions.timber_frame_internal_insulation,
    ('Timber Frame', 'External'): EpcWallDescriptions.timber_frame_external_insulation,
    ('Timber Frame', 'AsBuilt'): None,  # To be classified
    ('Timber Frame', 'Unknown'): None,

    # Solid Brick walls
    ('Solid Brick', 'External'): EpcWallDescriptions.solid_brick_external_insulation,
    ('Solid Brick', 'Internal'): EpcWallDescriptions.solid_brick_internal_insulation,
    ('Solid Brick', 'AsBuilt'): None,  # To be classified
    ('Solid Brick', 'Unknown'): None,

    # Granite walls
    ('Granite', 'External'): EpcWallDescriptions.granite_whinstone_external_insulation,
    ("Granite", 'Internal'): EpcWallDescriptions.granite_whinstone_internal_insulation,
    ('Granite', 'AsBuilt'): None,
    ('Granite', 'Unknown'): None,

    # Sandstone walls
    ('Sandstone', 'Internal'): EpcWallDescriptions.sandstone_limestone_internal_insulation,
    ('Sandstone', 'External'): EpcWallDescriptions.sandstone_limestone_external_insulation,
    ('Sandstone', 'Unknown'): None,
    ('Sandstone', 'AsBuilt'): None,

    # Cob walls
    ('Cob', 'AsBuilt'): None,
}

wall_unknown_age_fallback = {
    "Cavity": EpcWallDescriptions.cavity_as_built_unknown,
    "Solid Brick": EpcWallDescriptions.solid_brick_as_built_unknown,
    "Timber Frame": EpcWallDescriptions.timber_frame_as_built_unknown,
    "System": EpcWallDescriptions.system_as_built_unknown,
    "Granite": EpcWallDescriptions.granite_as_built_unknown,
    "Sandstone": EpcWallDescriptions.sandstone_as_built_unknown,
    "Cob": EpcWallDescriptions.cob_as_built_unknown,
}


def cavity_filled_efficiency(age_band: EpcConstructionAgeBand) -> EpcEfficiency:
    """"
    Maps cavity filled to efficiency based on construction age band.
    :param age_band: EpcConstructionAgeBand
    :return: EpcEfficiency
    """
    if age_band in {
        EpcConstructionAgeBand.from_2023_onwards
    }:
        return EpcEfficiency.VERY_GOOD

    return EpcEfficiency.GOOD


def internal_external_insulation_efficiency(
    age_band: EpcConstructionAgeBand,
) -> EpcEfficiency:
    """
    Maps:
    - cavity unfilled with internal/external insulation to efficiency based on construction age band. We assumed
    based on 100mm insulation
    - solid brick with internal/external insulation to efficiency based on construction age band. We assumed
    based on 100mm insulation
    - system built with internal/external insulation to efficiency based on construction age band. We assumed
    based on 100mm insulation

    All of these wall types have the same behaviour in elmhurst
    :param age_band: EpcConstructionAgeBand
    :return: EpcEfficiency
    """
    if age_band in {
        EpcConstructionAgeBand.from_1983_to_1990,
        EpcConstructionAgeBand.from_1991_to_1995,
        EpcConstructionAgeBand.from_1996_to_2002,
        EpcConstructionAgeBand.from_2003_to_2006,
        EpcConstructionAgeBand.from_2007_to_2011,
        EpcConstructionAgeBand.from_2012_to_2022,
        EpcConstructionAgeBand.from_2023_onwards,
    }:
        return EpcEfficiency.VERY_GOOD

    return EpcEfficiency.GOOD


def timber_granite_sandstone_internal_external_efficiency(age_band: EpcConstructionAgeBand) -> EpcEfficiency:
    """"
    Maps:
    - timber frame with internal/external wall insulation to efficiency based on construction age band.
    - sandstone/limestone with internal/external wall insulation to efficiency based on construction age band.
    - granite/whinstone with internal/external wall insulation to efficiency based on construction age band.
    :param age_band: EpcConstructionAgeBand
    :return: EpcEfficiency
    """
    if age_band in {
        EpcConstructionAgeBand.from_2023_onwards
    }:
        return EpcEfficiency.VERY_GOOD

    return EpcEfficiency.GOOD


WallEfficiencyRule = Union[
    EpcEfficiency,
    Callable[[EpcConstructionAgeBand, int | None], EpcEfficiency],
]

WALL_DESCRIPTION_EFFICIENCIES: Mapping[EpcWallDescriptions, WallEfficiencyRule] = {
    # Note: all function mappings have been defined based on Elmhurst
    # Cavity
    # value mappings
    EpcWallDescriptions.cavity_no_insulation_assumed: EpcEfficiency.POOR,
    EpcWallDescriptions.cavity_partial_insulated_assumed: EpcEfficiency.AVERAGE,
    EpcWallDescriptions.cavity_insulated_assumed: EpcEfficiency.GOOD,
    EpcWallDescriptions.cavity_filled_plus_internal: EpcEfficiency.VERY_GOOD,
    EpcWallDescriptions.cavity_filled_plus_external: EpcEfficiency.VERY_GOOD,
    # function mappings
    EpcWallDescriptions.cavity_filled_cavity: cavity_filled_efficiency,
    EpcWallDescriptions.cavity_internal_insulation: internal_external_insulation_efficiency,
    EpcWallDescriptions.cavity_external_insulation: internal_external_insulation_efficiency,

    # Solid brick
    # value mappings
    EpcWallDescriptions.solid_brick_no_insulation_assumed: EpcEfficiency.POOR,
    EpcWallDescriptions.solid_brick_partial_insulated_assumed: EpcEfficiency.AVERAGE,
    EpcWallDescriptions.solid_brick_insulated_assumed: EpcEfficiency.GOOD,
    # function mappings
    EpcWallDescriptions.solid_brick_internal_insulation: internal_external_insulation_efficiency,
    EpcWallDescriptions.solid_brick_external_insulation: internal_external_insulation_efficiency,

    # System
    # value mappings
    EpcWallDescriptions.system_no_insulation_assumed: EpcEfficiency.POOR,
    EpcWallDescriptions.system_partial_insulated_assumed: EpcEfficiency.AVERAGE,
    EpcWallDescriptions.system_insulated_assumed: EpcEfficiency.GOOD,
    # function mappings
    EpcWallDescriptions.system_internal_insulation: internal_external_insulation_efficiency,
    EpcWallDescriptions.system_external_insulation: internal_external_insulation_efficiency,

    # Timber frame
    # value mappings
    EpcWallDescriptions.timber_frame_no_insulation_assumed: EpcEfficiency.POOR,
    EpcWallDescriptions.timber_frame_partial_insulated_assumed: EpcEfficiency.AVERAGE,
    EpcWallDescriptions.timber_frame_insulated_assumed: EpcEfficiency.GOOD,
    # function mappings
    EpcWallDescriptions.timber_frame_internal_insulation: timber_granite_sandstone_internal_external_efficiency,
    EpcWallDescriptions.timber_frame_external_insulation: timber_granite_sandstone_internal_external_efficiency,

    # Granite / whinstone
    EpcWallDescriptions.granite_whinstone_no_insulation_assumed: EpcEfficiency.VERY_POOR,
    EpcWallDescriptions.granite_whinstone_partial_insulated_assumed: EpcEfficiency.AVERAGE,
    EpcWallDescriptions.granite_whinestone_insulated_assumed: EpcEfficiency.GOOD,
    # function mappings
    EpcWallDescriptions.granite_whinstone_internal_insulation: timber_granite_sandstone_internal_external_efficiency,
    EpcWallDescriptions.granite_whinstone_external_insulation: timber_granite_sandstone_internal_external_efficiency,

    # Sandstone / limestone
    EpcWallDescriptions.sandstone_limestone_no_insulation_assumed: EpcEfficiency.VERY_POOR,
    EpcWallDescriptions.sandstone_limestone_partial_insulated_assumed: EpcEfficiency.AVERAGE,
    EpcWallDescriptions.sandstone_limestone_insulated_assumed: EpcEfficiency.GOOD,
    # function mappings
    EpcWallDescriptions.sandstone_limestone_internal_insulation: timber_granite_sandstone_internal_external_efficiency,
    EpcWallDescriptions.sandstone_limestone_external_insulation: timber_granite_sandstone_internal_external_efficiency,

    # Cob (special case)
    EpcWallDescriptions.cob_as_built_average: EpcEfficiency.AVERAGE,
    EpcWallDescriptions.cob_as_built_good: EpcEfficiency.GOOD,

    # Unknown mappings which are unhandled
    EpcWallDescriptions.cavity_as_built_unknown: EpcEfficiency.NA,
    EpcWallDescriptions.solid_brick_as_built_unknown: EpcEfficiency.NA,
    EpcWallDescriptions.system_as_built_unknown: EpcEfficiency.NA,
    EpcWallDescriptions.timber_frame_as_built_unknown: EpcEfficiency.NA,
    EpcWallDescriptions.granite_as_built_unknown: EpcEfficiency.NA,
    EpcWallDescriptions.sandstone_as_built_unknown: EpcEfficiency.NA,
    EpcWallDescriptions.cob_as_built_unknown: EpcEfficiency.NA,

}


def resolve_wall_efficiency(
    description: EpcWallDescriptions,
    age_band: EpcConstructionAgeBand,
) -> EpcEfficiency:
    rule = WALL_DESCRIPTION_EFFICIENCIES[description]

    if isinstance(rule, EpcEfficiency):
        return rule

    return rule(age_band)
