from collections import OrderedDict
import os
import unittest
from unittest.mock import MagicMock
from nose.tools import eq_
from helix.constants.anchor_type import AnchorType
from helix.constants.module_type import ModuleType
from helix.constants.panel_type import PanelType
from helix.constants.version import version

from helix.doc_gen_params_builder import DocGenParamsBuilder
from helix.models.coordinate import Coordinate
from helix.models.panel import Panel
from helix.models.sql.inverters import Inverter
from helix.models.sql.power_stations import PowerStation
from helix.models.sql.sites import Site
from helix.models.sql.standalone_inverters import StandaloneInverter
from helix.models.subarray import Subarray
from helix.user_values import UserValues


class DocGenParamsBuilderTest(unittest.TestCase):
    def setUp(self):
        self.site = Site(system_type='1')

        self.site.project_name = ''
        self.site.building_height = 0
        self.site.building_width = 0
        self.site.building_length = 0
        self.site.parapet_height = 0
        self.site.ballast_block_weight = 0
        self.site.max_psf = 0
        self.site.anchor_type = AnchorType.OMG_PowerGrip_Plus.value
        self.site.exposure_category = 'C'
        self.site.wind_speed = 0
        self.site.spectral_response = 1
        self.site.importance_factor = 1
        self.site.module_type = ModuleType.Cell96.value

        self.user_values = UserValues(None, self.site)
        self.calculator = MagicMock()
        self.image_presenter = MagicMock()
        os.environ['SP_DOCGEN_API_KEY'] = 'TestDocGenApiKey'

    def tearDown(self):
        os.environ['SP_DOCGEN_API_KEY'] = ''

    def test_build_single_tilt(self):
        self.site.system_type = '0'
        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)

        self.calculator.L_B.return_value = 0.001
        self.calculator.k_z.return_value = 0.949932
        self.calculator.q_z.return_value = 0.12453
        self.calculator.get_computed_csv_columns.return_value = []
        self.calculator.documentation_summary_values.return_value = {'summary': 'foo'}
        self.calculator.summary_table.return_value = OrderedDict({PanelType.Corner:
                                                                      {'anchors': [1],
                                                                       'ballast blocks': [6],
                                                                       'pressure': ['4.46']}})
        self.calculator.minimum_array_sizes.return_value = [123]
        self.calculator.documentation_bom.return_value = []
        self.calculator.documentation_bom.return_value = [
            ('88764', 9),
            ('modules', 0)
        ]
        self.image_presenter.generate_image.return_value = "".encode("utf-8")

        params = subject.build()

        expected = {
            'apiKey': 'TestDocGenApiKey',
            'templateName': 'Helix_Single_Tilt_Template',
            'nameValuePairs': [
                {'name': '1_a_anc', 'value': 1},
                {'name': '1_a_bb', 'value': 6},
                {'name': '1_a_psf', 'value': '4.46'},
                {'name': 'anchor_type', 'value': 'OMG PowerGrip Plus'},
                {'name': 'ballast_block_weight', 'value': 0.0},
                {'name': 'building_height', 'value': 0.0},
                {'name': 'building_length', 'value': 0.0},
                {'name': 'building_width', 'value': 0.0},
                {'name': 'exposure_category', 'value': 'C'},
                {'name': 'exposure_category_transition_distance', 'value': 0},
                {'name': 'kz', 'value': 0.95},
                {'name': 'lb', 'value': 0.0},
                {'name': 'max_allowable_system_pressure', 'value': 0.0},
                {'name': 'min_a', 'value': 123},
                {'name': 'module_type', 'value': '96 Cell'},
                {'name': 'parapet_height', 'value': 0.0},
                {'name': 'power_station_string', 'value': ''},
                {'name': 'project_name', 'value': ''},
                {'name': 'qz', 'value': 0.12},
                {'name': 'seismic_importance_factor', 'value': 1.0},
                {'name': 'spectral_response', 'value': 1.0},
                {'name': 'standalone_inverter_string', 'value': ''},
                {'name': 'subarrays_string', 'value': ''},
                {'name': 'summary', 'value': 'foo'},
                {'name': 'system_type', 'value': 'Single-Tilt'},
                {'name': 'total_88764', 'value': 9},
                {'name': 'total_modules', 'value': 0},
                {'name': 'version', 'value': version()},
                {'name': 'wind_speed', 'value': 0.0},
            ],
            'dynamicImages': [
                {'imageKey': 'array_image', 'base64encodedImage': ''}
            ]
        }

        eq_(params, expected)

    def test_build_dual_tilt(self):
        self.site.system_type = '1'
        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)

        self.calculator.L_B.return_value = 0
        self.calculator.k_z.return_value = 0
        self.calculator.q_z.return_value = 0
        self.calculator.get_computed_csv_columns.return_value = []
        self.calculator.documentation_summary_values.return_value = {'summary': 'foo'}
        self.image_presenter.generate_image.return_value = "".encode("utf-8")

        params = subject.build()

        eq_(sorted(params.keys()), ['apiKey', 'dynamicImages', 'nameValuePairs', 'templateName'])
        eq_(params['apiKey'], 'TestDocGenApiKey')
        eq_(params['templateName'], 'Helix_Dual_Tilt_Template')

    def test_site_characterization(self):
        self.site.system_type = '0'
        self.site.project_name = 'Test Project Name'
        self.site.building_height = 100
        self.site.building_width = 1000
        self.site.building_length = 1500
        self.site.parapet_height = 0
        self.site.ballast_block_weight = 20
        self.site.max_psf = 17
        self.site.anchor_type = AnchorType.EcoFasten.value
        self.site.exposure_category = 'D'
        self.site.wind_speed = 110
        self.site.spectral_response = 1
        self.site.importance_factor = 1
        self.site.module_type = ModuleType.Cell96.value

        self.calculator.L_B.return_value = 23
        self.calculator.k_z.return_value = 34
        self.calculator.q_z.return_value = 45
        self.calculator.get_computed_csv_columns.return_value = [
            Panel(),
            Panel()
        ]
        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)

        expected_site_characterization = {
            'project_name': 'Test Project Name',
            'building_height': 100.0,
            'building_width': 1000.0,
            'building_length': 1500.0,
            'parapet_height': 0.0,
            'ballast_block_weight': 20.0,
            'max_allowable_system_pressure': 17,
            'anchor_type': 'EcoFasten Eco 65',
            'exposure_category': 'D',
            'exposure_category_transition_distance': 0,
            'wind_speed': 110,
            'spectral_response': 1,
            'seismic_importance_factor': 1,
            'module_type': '96 Cell',
            'system_type': 'Single-Tilt',

            'total_modules': 2,
            'version': version(),
            'lb': 23,
            'kz': 34,
            'qz': 45
        }

        eq_(subject.site_characterization(), expected_site_characterization)

    def test_build_panel_attributes_dual_tilt(self):
        self.site.system_type = '1'
        self.calculator.summary_table.return_value = OrderedDict({
            PanelType.Corner:
              {'anchors': [1, 1, 0, 0, 0],
               'ballast blocks': [6, 0, 13, 5, 0],
               'pressure': ['4.46', '2.40', '6.86', '4.08', '2.40']},
             PanelType.NorthSouth:
              {'anchors': [1, 0, 0, 0, 0],
               'ballast blocks': [0, 19, 9, 3, 0],
               'pressure': ['2.40', '8.94', '5.47', '3.41', '2.40']},
             PanelType.EastWest:
              {'anchors': [1, 0, 0, 0, 0],
               'ballast blocks': [0, 19, 9, 3, 0],
               'pressure': ['2.26', '8.77', '5.35', '3.27', '2.26']},
             PanelType.Middle:
              {'anchors': [0, 0, 0, 0, 0],
               'ballast blocks': [16, 8, 3, 0, 0],
               'pressure': ['7.76', '5.01', '3.27', '2.26', '2.26']}
        })

        self.calculator.minimum_array_sizes.return_value = [22, 19, 23, 19, 6]

        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)

        expected = {
            '1_a_bb': 6,
            '1_b_bb': 0,
            '1_c_bb': 13,
            '1_d_bb': 5,
            '1_e_bb': 0,

            '1_a_anc': 1,
            '1_b_anc': 1,
            '1_c_anc': 0,
            '1_d_anc': 0,
            '1_e_anc': 0,

            '1_a_psf': '4.46',
            '1_b_psf': '2.40',
            '1_c_psf': '6.86',
            '1_d_psf': '4.08',
            '1_e_psf': '2.40',

            '2_a_bb': 0,
            '2_b_bb': 19,
            '2_c_bb': 9,
            '2_d_bb': 3,
            '2_e_bb': 0,

            '2_a_anc': 1,
            '2_b_anc': 0,
            '2_c_anc': 0,
            '2_d_anc': 0,
            '2_e_anc': 0,

            '2_a_psf': '2.40',
            '2_b_psf': '8.94',
            '2_c_psf': '5.47',
            '2_d_psf': '3.41',
            '2_e_psf': '2.40',

            '3_a_bb': 0,
            '3_b_bb': 19,
            '3_c_bb': 9,
            '3_d_bb': 3,
            '3_e_bb': 0,

            '3_a_anc': 1,
            '3_b_anc': 0,
            '3_c_anc': 0,
            '3_d_anc': 0,
            '3_e_anc': 0,

            '3_a_psf': '2.26',
            '3_b_psf': '8.77',
            '3_c_psf': '5.35',
            '3_d_psf': '3.27',
            '3_e_psf': '2.26',

            '4_a_bb': 16,
            '4_b_bb': 8,
            '4_c_bb': 3,
            '4_d_bb': 0,
            '4_e_bb': 0,

            '4_a_anc': 0,
            '4_b_anc': 0,
            '4_c_anc': 0,
            '4_d_anc': 0,
            '4_e_anc': 0,

            '4_a_psf': '7.76',
            '4_b_psf': '5.01',
            '4_c_psf': '3.27',
            '4_d_psf': '2.26',
            '4_e_psf': '2.26',

            'min_a': 22,
            'min_b': 19,
            'min_c': 23,
            'min_d': 19,
            'min_e': 6
        }

        eq_(subject.panel_attributes(), expected)

    def test_build_bom(self):
        self.calculator.documentation_bom.return_value = [
            ('512200', 480),
            ('513832', 0),
            ('513833', 790),
            ('513836', 0),
            ('521794', 196),

            ('anchors', 262),
            ('ballast', 6786),
            ('modules', 1726)
        ]

        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)

        expected = {
            'total_512200': 480,
            'total_513832': 0,
            'total_513833': 790,
            'total_513836': 0,
            'total_521794': 196,
            'total_anchors': 262,
            'total_ballast': 6786,
            'total_modules': 1726
        }

        eq_(subject.bom(), expected)

    def test_build_power_station(self):
        power_station_1 = PowerStation(
            description='1',
            quantity=1,
            ac_run_length=55,
            inverters=[
                Inverter(
                    model='4',
                    strings_per_inverter=2,
                    sunshade=True,
                    dc_switch=False
                ),
                Inverter(
                    model='5',
                    strings_per_inverter=5,
                    sunshade=False,
                    dc_switch=False
                )
            ]
        )

        power_station_2 = PowerStation(
            description='2',
            quantity=3,
            ac_run_length=89,
            inverters=[
                Inverter(
                    model='6',
                    strings_per_inverter=6,
                    sunshade=False,
                    dc_switch=True
                ),
                Inverter(
                    model='8',
                    strings_per_inverter=8,
                    sunshade=False,
                    dc_switch=False
                )
            ]
        )

        self.site.power_stations = [power_station_1, power_station_2]

        expected = {
            'power_station_string': """
Description: 1
\tQuantity: 1
\tAC Run Length: 55
\tInverters:
\t\tModel: 12kW SMA Tripower - 514686
\t\tStrings per inverter: 2
\t\tSunShade: Yes

\t\tModel: 15kW SMA Tripower - 514687
\t\tStrings per inverter: 5

Description: 2
\tQuantity: 3
\tAC Run Length: 89
\tInverters:
\t\tModel: 20kW SMA Tripower - 512676
\t\tStrings per inverter: 6
\t\tDC Switch: Yes

\t\tModel: 24kW SMA Tripower - 514685
\t\tStrings per inverter: 8

"""
        }

        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)
        eq_(subject.power_stations(), expected)

    def test_standalone_inverters(self):
        power_station = PowerStation(
            description='My Power Station',
            quantity=1,
            ac_run_length=45,
            inverters=[
                Inverter(
                    model='8',
                    strings_per_inverter=7,
                    sunshade=True,
                    dc_switch=True
                ),
                Inverter(
                    model='4',
                    strings_per_inverter=2,
                    sunshade=False,
                    dc_switch=False
                )
            ]
        )

        standalone_inverter_1 = StandaloneInverter(
            inverter=[Inverter(model='6', strings_per_inverter='5', sunshade=True, dc_switch=False)],
            ac_run_length=45,
            attachment_point=power_station,
        )

        standalone_inverter_2 = StandaloneInverter(
            inverter=[Inverter(model='4', strings_per_inverter='3', sunshade=False, dc_switch=True)],
            ac_run_length=45,
            attachment_point=None,
        )

        self.site.power_stations = [power_station]
        self.site.standalone_inverters = [standalone_inverter_1, standalone_inverter_2]

        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)
        """
    AC Run Length: 3
    Attachment Point: Switch Gear
    Model: 20kW SMA Tripower - 512676
    Strings per inverter: 6
    Sunshade: Yes
    DC Switch: Yes
        """

        expected = {
            'standalone_inverter_string': """
\tAC Run Length: 45
\tAttachment Point: My Power Station
\tModel: 20kW SMA Tripower - 512676
\tStrings per inverter: 5
\tSunShade: Yes

\tAC Run Length: 45
\tAttachment Point: Switch Gear
\tModel: 12kW SMA Tripower - 514686
\tStrings per inverter: 3
\tDC Switch: Yes
"""
        }
        eq_(subject.standalone_inverters(), expected)

    def test_subarray_summary(self):
        self.calculator.subarray_summary.return_value = [
            Subarray(subarray_number=1, origin=Coordinate(0, 0), required_seismic_anchors=11, weight=23782.67, start_row=0,
                     size=192),
            Subarray(subarray_number=2, origin=Coordinate(0, 0), required_seismic_anchors=0, weight=13113.05,
                     start_row=192, size=96)
        ]

        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)

        expected = {
            'subarrays_string': """
\tSubarray: 1
\tSeismic Anchors: 11
\tWeight: 23,783 lbs

\tSubarray: 2
\tSeismic Anchors: 0
\tWeight: 13,113 lbs
"""
        }
        eq_(subject.subarray_summary(), expected)

    def test_array_image(self):
        panels = [
            Panel(id=1)
        ]
        subarrays = [
            Subarray(subarray_number=1)
        ]
        self.calculator.get_computed_csv_columns.return_value = panels
        self.calculator.subarrays = subarrays
        self.image_presenter.generate_image.return_value = "hello world".encode("utf-8")

        subject = DocGenParamsBuilder(self.user_values, self.user_values.system_type(), self.calculator, self.image_presenter)

        expected = {'array_image': 'aGVsbG8gd29ybGQ='}

        eq_(subject.array_image(), expected)

        self.image_presenter.generate_image.assert_called_once_with(panels, subarrays)

    def test_convert_list_params_to_dict(self):
        list_params = [
            {
                'name': 'foo',
                'value': 'bar'
            },
            {
                'name': 'cat',
                'value': 'dog'
            }
        ]

        expected = {
            'foo': 'bar',
            'cat': 'dog'
        }

        eq_(DocGenParamsBuilder.convert_list_params_to_dict(list_params), expected)

    def test_convert_dict_params_to_list(self):
        dict_params = {
            'foo': 'bar',
            'cat': 'dog',
            'fast': 'slow',
        }

        expected = sorted([
            {
                'name': 'foo',
                'value': 'bar'
            },
            {
                'name': 'cat',
                'value': 'dog'
            },
            {
                'name': 'fast',
                'value': 'slow'
            }
        ], key=lambda k: k['name'])

        eq_(DocGenParamsBuilder.convert_dict_params_to_list(dict_params), expected)

    def test_convert_dict_parms_to_list_other_names(self):
        dict_params = {
            'foo': 'bar',
            'cat': 'dog',
            'fast': 'slow',
        }

        expected = sorted([
            {
                'baz': 'foo',
                'qux': 'bar'
            },
            {
                'baz': 'cat',
                'qux': 'dog'
            },
            {
                'baz': 'fast',
                'qux': 'slow'
            }
        ], key=lambda k: k['baz'])

        eq_(DocGenParamsBuilder.convert_dict_params_to_list(dict_params, key_name='baz', value_name='qux'), expected)
