from base64 import b64encode
import os
from helix.constants.system_type import SystemType
from helix.constants.version import version


class DocGenParamsBuilder(object):
    def __init__(self, user_values, system_type, calculator, image_presenter):
        self.user_values = user_values
        self.system_type = system_type
        self.calculator = calculator
        self.image_presenter = image_presenter

    def build(self):
        if self.user_values.system_type() == SystemType.singleTilt:
            template_name = 'Helix_Single_Tilt_Template'
        else:
            template_name = 'Helix_Dual_Tilt_Template'

        body = {
            **self.site_characterization(),
            **self.calculator.documentation_summary_values(),
            **self.panel_attributes(),
            **self.bom(),
            **self.power_stations(),
            **self.standalone_inverters(),
            **self.subarray_summary(),
        }

        image_dicts = self.array_image()

        list_params = self.convert_dict_params_to_list(body)
        images = self.convert_dict_params_to_list(image_dicts, key_name='imageKey', value_name='base64encodedImage')

        api_key = os.getenv('SP_DOCGEN_API_KEY', '')

        params = {
            "apiKey": api_key,
            "templateName": template_name,
            "nameValuePairs": list_params,
            "dynamicImages": images,
        }
        return params

    def site_characterization(self):
        panels = self.calculator.get_computed_csv_columns()

        return {
            'project_name': self.user_values.project_name(),
            'building_height': self.user_values.building_height(),
            'building_width': self.user_values.building_width(),
            'building_length': self.user_values.building_length(),
            'parapet_height': self.user_values.building_parapet_height(),
            'ballast_block_weight': self.user_values.ballast_block_weight(),
            'max_allowable_system_pressure': self.user_values.max_system_pressure(),
            'anchor_type': self.user_values.anchor_type().value,
            'exposure_category': self.user_values.exposure_category().value,
            'exposure_category_transition_distance': self.user_values.exposure_category_transition_distance(),
            'wind_speed': self.user_values.wind_speed(),
            'spectral_response': self.user_values.spectral_response(),
            'seismic_importance_factor': self.user_values.importance_factor(),
            'module_type': self.user_values.module_type().value,
            'system_type': self.user_values.system_type().display_name(),

            'total_modules': len(panels),
            'version': version(),
            'lb': round(self.calculator.L_B(), 2),
            'kz': round(self.calculator.k_z(), 2),
            'qz': round(self.calculator.q_z(), 2),
        }

    def panel_attributes(self):
        summary_table = self.calculator.summary_table()
        minimum_array_sizes = self.calculator.minimum_array_sizes()

        result = {}

        for panel_type, values in summary_table.items():
            ballast_blocks = values['ballast blocks']
            pressure = values['pressure']
            anchors = values['anchors']
            for wind_zone_index in range(len(ballast_blocks)):
                wind_zone = self.system_type.system_constants().wind_zones[wind_zone_index].lower()
                prefix = '%d_%s_' % (panel_type.number(), wind_zone)
                result[prefix + 'bb'] = ballast_blocks[wind_zone_index]
                result[prefix + 'psf'] = pressure[wind_zone_index]
                result[prefix + 'anc'] = anchors[wind_zone_index]

        for idx, array_size in enumerate(minimum_array_sizes):
            wind_zone = self.system_type.system_constants().wind_zones[idx].lower()
            result['min_' + wind_zone] = array_size

        return result

    def bom(self):
        bom_values = self.calculator.documentation_bom()
        result = {}

        for row in bom_values:
            result['total_' + row[0]] = row[1]
        return result

    def power_stations(self):
        power_station_string = ""
        power_stations = self.user_values.power_stations()
        if len(power_stations) > 0:
            power_station_string = "\n"
        for power_station in self.user_values.power_stations():
            power_station_string += "Description: %s\n" % power_station['power_station_description']
            power_station_string += "\tQuantity: %s\n" % power_station['power_station_quantity']
            power_station_string += "\tAC Run Length: %s\n" % power_station['ac_run_length']
            power_station_string += "\tInverters:\n"
            for inverter in power_station['inverters']:
                power_station_string += "\t\tModel: %s\n" % inverter['model'].label
                power_station_string += "\t\tStrings per inverter: %s\n" % inverter['strings_per_inverter']
                if inverter.get('sunshade'):
                    power_station_string += "\t\tSunShade: Yes\n"
                if inverter.get('dc_switch'):
                    power_station_string += "\t\tDC Switch: Yes\n"
                power_station_string += "\n"
        return {'power_station_string': power_station_string}

    def standalone_inverters(self):
        inverters_string = ""
        for inverter in self.user_values.standalone_inverters():
            inverters_string += "\n"
            inverters_string += "\tAC Run Length: %s\n" % inverter['ac_run_length']
            inverters_string += "\tAttachment Point: %s\n" % inverter['attachment_point'][0]
            inverters_string += "\tModel: %s\n" % inverter['model'].label
            inverters_string += "\tStrings per inverter: %s\n" % inverter['strings_per_inverter']
            if inverter.get('sunshade'):
                inverters_string += "\tSunShade: Yes\n"
            if inverter.get('dc_switch'):
                inverters_string += "\tDC Switch: Yes\n"
        return {'standalone_inverter_string': inverters_string}

    def subarray_summary(self):
        subarray_string = ""
        for subarray in self.calculator.subarray_summary():
            subarray_string += "\n"
            subarray_string += "\tSubarray: %d\n" % subarray.subarray_number
            subarray_string += "\tSeismic Anchors: %d\n" % subarray.required_seismic_anchors
            subarray_string += "\tWeight: %s lbs\n" % "{:,}".format(round(subarray.weight))
        return {'subarrays_string': subarray_string}

    def array_image(self):
        png_data = self.image_presenter.generate_image(self.calculator.get_computed_csv_columns(),
                                                       self.calculator.subarrays)
        return {'array_image': b64encode(png_data).decode('utf-8')}

    @staticmethod
    def convert_list_params_to_dict(list_params):
        dict_params = {}
        for datum in list_params:
            dict_params[datum['name']] = datum['value']
        return dict_params

    @staticmethod
    def convert_dict_params_to_list(dict_params, key_name='name', value_name='value'):
        list_params = []
        for k, v in dict_params.items():
            list_params.append({
                key_name: k,
                value_name: v
            })
        return sorted(list_params, key=lambda k: k[key_name])
