from typing import List

import numpy as np

from backend.Property import Property
from recommendations.Costs import Costs
from recommendations.recommendation_utils import override_costs


class WindowsRecommendations:
    # If the property has existing glazing, we scale down the number of windows that need to be glazed
    COVERAGE_MAP = {
        # If most of the windows have already been glazed, we assume that 2/3 are glazed and 1/2 are remaining to be
        # glazed
        "most": 0.33,
        # If glazing is partial, we assume 50/50 split between glazed and unglazed
        "partial": 0.5,
    }

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

        self.recommendation = []

        self.glazing_material = [
            material for material in materials if material["type"] == "windows_glazing"
        ]

        if len(self.glazing_material) != 1:
            raise ValueError("There should only be one window glazing material")
        self.glazing_material = self.glazing_material[0]

    def recommend(self, phase=0):
        """
        This method will recommend the best possible glazing options for a property.

        In order to do this, we need to estimate the number of windows that the home has. This information will be
        stored in the property object, under property.number_of_windows
        :return:
        """

        # If the property is in a conservation area or is a listed building, it becomes more difficult to install
        # double glazing. Therefore, we don't recommend it. It is still possible but is not practical as it
        # requires planning permission and might require a more expensive window type, such as timber.

        number_of_windows = self.property.number_of_windows
        is_secondary_glazing = self.property.restricted_measures or (
            self.property.windows["glazing_type"] == "secondary"
        )

        if not number_of_windows:
            raise ValueError("Number of windows not specified")

        if self.property.windows["has_glazing"] & (
            self.property.windows["glazing_coverage"] == "full"
        ):
            return

        # We scale the number of windows based on the proportion of existing glazing
        if self.property.data["multi-glaze-proportion"] != "":
            n_windows_scalar = 1 - (
                int(self.property.data["multi-glaze-proportion"]) / 100
            )
        else:
            n_windows_scalar = self.COVERAGE_MAP.get(
                self.property.windows["glazing_coverage"], 1
            )

        number_of_windows *= n_windows_scalar
        number_of_windows = np.ceil(number_of_windows)

        # We then price the job based on the number of windows that there are
        cost_result = self.costs.window_glazing(
            number_of_windows=number_of_windows,
            material=self.glazing_material,
            is_secondary_glazing=is_secondary_glazing,
        )

        already_installed = "windows_glazing" in self.property.already_installed
        if already_installed:
            cost_result = override_costs(cost_result)
            description = "The property already has double glazing installed. No further action is required."
        else:
            glazing_type = (
                "secondary glazing" if is_secondary_glazing else "double glazing"
            )
            if self.property.windows["glazing_coverage"] in ["partial", "most"]:
                description = f"Install {glazing_type} to the remaining windows"
            else:
                description = f"Install {glazing_type} to all windows"

            if self.property.is_listed:
                description += (
                    ". Secondary glazing recommended due to listed building status"
                )
            elif self.property.is_heritage:
                description += (
                    ". Secondary glazing recommended due to herigate building status"
                )
            elif self.property.in_conservation_area:
                description += (
                    ". Secondary glazing recommended due to conservation area status"
                )

        self.recommendation = [
            {
                "phase": phase,
                "parts": [],
                "type": "windows_glazing",
                "description": description,
                "starting_u_value": None,
                "new_u_value": None,
                "sap_points": None,
                "already_installed": already_installed,
                **cost_result,
                "is_secondary_glazing": is_secondary_glazing,
            }
        ]
