import time
from flask.ext.testing import LiveServerTestCase
import mockredis
from splinter import Browser

from helix import main
from helix.constants import redis_constant
from helix.constants import sql_constant
from helix.constants.inverter_type import InverterType
from helix.constants.system_type import SystemType
from helix.db.sql_manager import SQLManager
from helix.models.sql.power_monitors import PowerMonitor
from helix.models.sql.power_stations import PowerStation
from helix.models.sql.standalone_inverters import StandaloneInverter
from test.integration.integration_test_helpers import *
from test.test_helpers import test_db_session, reset_db_session


class PowerStationConfigurationDynamicFormTestCase(LiveServerTestCase):
    def create_app(self):
        redis_constant.redis_store = mockredis.mock_redis_client()
        sql_constant.sql_session_maker = lambda: SQLManager.get_sql_session_maker('postgres://pivotal:@localhost/test')

        app = main.app
        app.config['TESTING'] = True
        app.config['LIVESERVER_PORT'] = 8943
        return app

    @classmethod
    def setUpClass(cls):
        cls.test_db_session = test_db_session()
        cls.browser = Browser('phantomjs')

    @classmethod
    def tearDownClass(cls):
        cls.test_db_session.close_all()
        cls.browser.quit()

    def setUp(self):
        self.fill_in_site_characterization_data()
        self.browser.visit(self.get_server_url() + '/power_station_configuration/')

    def tearDown(self):
        reset_db_session(self.test_db_session)
        self.browser.cookies.delete()

    def fill_in_site_characterization_data(self, system_type=SystemType.singleTilt):
        self.browser.visit(self.get_server_url() + '/site_characterization/')
        self.browser.fill('project_name', 'Test Project Name')
        self.browser.fill('building_width', '200')
        self.browser.fill('building_height', '30')
        self.browser.fill('building_length', '75.5')
        self.browser.fill('wind_speed', '125')
        self.browser.fill('ballast_block_weight', '13')
        self.browser.fill('building_parapet_height', '0')
        self.browser.fill('max_system_pressure', '10')
        self.browser.fill('design_spectral_response', '1.02')
        self.browser.select('system_type', system_type.value)
        self.browser.find_by_value('Next').first.click()

    def expect_power_station_input_field(self, name='Test Power Station Name', uuid=None, count=2, run_length=None,
                                         inverter_count=3):
        eq_(self.browser.find_by_name('power_station_description').first.value, name)
        if uuid:
            eq_(self.browser.find_by_name('power_station_id').first.value, uuid)
        eq_(self.browser.find_by_name('power_station_quantity').first.value, str(count))
        if run_length:
            eq_(self.browser.find_by_name('ac_run_length').first.value, str(run_length))

        eq_(self.browser.find_by_name('inverter_quantity').first.value, str(inverter_count))

        eq_(self.browser.find_by_name('inverter_1-model').first.value, str(InverterType.SMA.MODEL_15KW.value))
        eq_(self.browser.find_by_name('inverter_1-strings_per_inverter').first.value, '3')
        eq_(self.browser.find_by_name('inverter_1-sunshade').first.checked, False)
        eq_(self.browser.find_by_name('inverter_1-dc_switch').first.checked, False)

    def test_switching_inverter_brand_removes_power_station_section(self):
        select_inverter_brand(self.browser, brand=InverterBrand.DELTA.value)
        assert self.browser.is_element_not_present_by_css('#add_new_power_station')
        assert self.browser.is_element_not_present_by_css('form[name=power_station_form]')

    def test_switching_inverter_brand_shows_different_inverter_models(self):
        show_standalone_inverter_form(self.browser)
        eq_(len(self.browser.find_by_css('#inverter-model option')), 4)
        eq_(self.browser.find_by_name('inverter-model').first.value, str(InverterType.SMA.default_value()))

        select_inverter_brand(self.browser, brand=InverterBrand.DELTA.value)
        time.sleep(1)

        show_standalone_inverter_form(self.browser)
        eq_(len(self.browser.find_by_css('#inverter-model option')), 3)
        eq_(self.browser.find_by_name('inverter-model').first.value, str(InverterType.DELTA.default_value()))

    def test_switching_inverter_brand_hides_attachment_point(self):
        show_power_station_form(self.browser)
        fill_in_power_station(self.browser, name='Test Power Station Name', count=1, inverter_count=3)
        submit_power_station_form(self.browser)

        attachment_point_options = self.browser.find_by_css('#attachment_point option')
        eq_(len(attachment_point_options), 2)
        attachment_options_as_list = list(map(lambda x: x.html, attachment_point_options))
        eq_(attachment_options_as_list, ['Switch Gear', 'Test Power Station Name'])

        select_inverter_brand(self.browser, brand=InverterBrand.DELTA.value)

        show_standalone_inverter_form(self.browser)

        assert self.browser.is_element_not_present_by_css('#attachment_point')

    def test_inverters_without_string_range_hides_string_select(self):
        show_standalone_inverter_form(self.browser)
        assert self.browser.is_element_visible_by_css('#inverter-strings_per_inverter_row')

        select_inverter_brand(self.browser, brand=InverterBrand.DELTA.value)

        show_standalone_inverter_form(self.browser)
        assert not self.browser.is_element_visible_by_css('#inverter-strings_per_inverter_row')

    def test_inverter_input_is_based_on_number_selected(self):
        show_power_station_form(self.browser)

        assert self.browser.is_text_present('Inverter 1')
        assert self.browser.is_text_present('Inverter 2')
        assert self.browser.is_text_present('Inverter 3')
        assert self.browser.is_text_present('Inverter 4')

        self.browser.select('inverter_quantity', '2')
        assert self.browser.is_text_present('Inverter 1')
        assert self.browser.is_text_present('Inverter 2')
        assert not self.browser.is_text_present('Inverter 3')
        assert not self.browser.is_text_present('Inverter 4')

        self.browser.select('inverter_quantity', '3')
        assert self.browser.is_text_present('Inverter 1')
        assert self.browser.is_text_present('Inverter 2')
        assert self.browser.is_text_present('Inverter 3')
        assert not self.browser.is_text_present('Inverter 4')

    def test_data_deleted_when_switching_brand(self):
        show_power_station_form(self.browser)
        fill_in_power_station(self.browser, name='Test Power Station Name', count=1, inverter_count=3)
        submit_power_station_form(self.browser)

        show_standalone_inverter_form(self.browser)
        fill_in_standalone_inverter(self.browser, model=InverterType.SMA.MODEL_20KW)
        submit_standalone_inverter_form(self.browser)

        show_standalone_inverter_form(self.browser)
        fill_in_standalone_inverter(self.browser, model=InverterType.SMA.MODEL_15KW)
        submit_standalone_inverter_form(self.browser)

        show_monitoring_form(self.browser)
        submit_monitoring_form(self.browser)

        select_inverter_brand(self.browser, brand=InverterBrand.DELTA.value)

        assert_number_of_power_station_table_rows(self.browser, -1)
        assert_number_of_standalone_inverter_table_rows(self.browser, -1)
        assert_number_of_supervisor_monitor_table_rows(self.browser, -1)

    def test_inverter_strings_options_same_for_all_inverter_capacity_options_single_tilt(self):
        show_power_station_form(self.browser)

        inverter_strings_field = self.browser.find_by_name('inverter_1-strings_per_inverter').first
        inverter_strings_options = inverter_strings_field.find_by_css('option:enabled')

        eq_(list(map(lambda x: x.value, inverter_strings_options)), ['2', '3', '4', '5', '6', '7', '8'])
        eq_(inverter_strings_field.value, '8')

        string_counts = [4, 5, 6, 8]
        for idx, inverter_type in enumerate(InverterType.SMA.all()):
            self.browser.select('inverter_1-model', inverter_type.value)

            inverter_strings_field = self.browser.find_by_name('inverter_1-strings_per_inverter').first
            eq_(inverter_strings_field.value, str(string_counts[idx]))

            inverter_strings_options = inverter_strings_field.find_by_css('option:enabled')
            eq_(list(map(lambda x: x.value, inverter_strings_options)), ['2', '3', '4', '5', '6', '7', '8'])

    def test_inverter_strings_options_same_for_all_inverter_capacity_options_dual_tilt(self):
        self.fill_in_site_characterization_data(system_type=SystemType.dualTilt)
        self.browser.visit(self.get_server_url() + '/power_station_configuration/')
        show_power_station_form(self.browser)

        inverter_strings_field = self.browser.find_by_name('inverter_1-strings_per_inverter').first
        inverter_strings_options = inverter_strings_field.find_by_css('option:enabled')

        eq_(list(map(lambda x: x.value, inverter_strings_options)), ['2', '4', '6', '8'])
        eq_(inverter_strings_field.value, '8')

        string_counts = [4, 4, 6, 8]
        for idx, inverter_type in enumerate(InverterType.SMA.all()):
            self.browser.select('inverter_1-model', inverter_type.value)

            inverter_strings_field = self.browser.find_by_name('inverter_1-strings_per_inverter').first
            eq_(inverter_strings_field.value, str(string_counts[idx]))

            inverter_strings_options = inverter_strings_field.find_by_css('option:enabled')
            eq_(list(map(lambda x: x.value, inverter_strings_options)), ['2', '4', '6', '8'])

    def test_add_power_station_button_shows_power_station_input_form(self):
        show_power_station_form(self.browser)
        fill_in_power_station(self.browser, name='Test Power Station Name', count=2)
        submit_power_station_form(self.browser)

        assert not self.browser.find_by_css('#power_station_form').first.visible
        assert not self.browser.find_by_css('#add_new_power_station').first.has_class('disabled')
        show_power_station_form(self.browser)
        assert self.browser.find_by_css('#power_station_form').first.visible
        assert self.browser.find_by_css('#add_new_power_station').first.has_class('disabled')

    def test_editing_power_stations(self):
        show_power_station_form(self.browser)
        fill_in_power_station(self.browser,
                              name='Test Power Station Name',
                              count=2,
                              run_length=102,
                              inverter_count=3,
                              inverters=[
                                  {
                                      'model': InverterType.SMA.MODEL_15KW,
                                      'strings': 3,
                                  }
                              ])
        submit_power_station_form(self.browser)

        saved_id = str(self.test_db_session.query(PowerStation).first().id)

        show_power_station_form(self.browser)
        fill_in_power_station(self.browser,
                              count=3,
                              inverter_count=2,
                              inverters=[{'model': InverterType.SMA.MODEL_20KW}])
        submit_power_station_form(self.browser)

        edit_power_station(self.browser, 1)

        assert self.browser.find_by_css('#power_station_form').first.visible
        eq_(self.browser.find_by_name('power_station_description').first.value, 'Test Power Station Name')
        eq_(self.browser.find_by_name('power_station_id').first.value, saved_id)
        eq_(self.browser.find_by_name('power_station_quantity').first.value, '2')
        eq_(self.browser.find_by_name('ac_run_length').first.value, '102')
        eq_(self.browser.find_by_name('inverter_quantity').first.value, '3')
        assert self.browser.is_text_present('Inverter 3')
        eq_(self.browser.find_by_name('inverter_1-model').first.value, str(InverterType.SMA.MODEL_15KW.value))
        eq_(self.browser.find_by_name('inverter_1-strings_per_inverter').first.value, '3')
        eq_(self.browser.find_by_name('inverter_1-sunshade').first.checked, False)
        eq_(self.browser.find_by_name('inverter_1-dc_switch').first.checked, False)

        fill_in_power_station(self.browser, name='Extra Special Power Station', inverter_count=4,
                                   inverters=[{'model': InverterType.SMA.MODEL_12KW}])
        self.browser.find_by_value('Save').first.click()

        assert_number_of_power_station_table_rows(self.browser, 2)

        models = ['12kW SMA Tripower - 514686', '24kW SMA Tripower - 514685', '24kW SMA Tripower - 514685', '24kW SMA Tripower - 514685']
        strings = ['4', '8', '8', '8']
        assert_power_station_table_row(self.browser, 1,
                                       name='Extra Special Power Station',
                                       models=models,
                                       strings=strings,
                                       quantity=2)

    def test_editing_power_station_styles_current_row_as_selected(self):
        show_power_station_form(self.browser)
        submit_power_station_form(self.browser)

        show_power_station_form(self.browser)
        submit_power_station_form(self.browser)

        edit_power_station(self.browser, 1)
        assert self.browser.find_by_css('.power_station_table tr:nth-child(2)').has_class('selected')
        assert not self.browser.find_by_css('.power_station_table tr:nth-child(3)').has_class('selected')

        edit_power_station(self.browser, 2)
        assert not self.browser.find_by_css('.power_station_table tr:nth-child(2)').has_class('selected')
        assert self.browser.find_by_css('.power_station_table tr:nth-child(3)').has_class('selected')

    def test_cancelling_editing_power_stations(self):
        show_power_station_form(self.browser)
        fill_in_power_station(self.browser, name='Test Power Station Name')
        submit_power_station_form(self.browser)

        saved_id = str(self.test_db_session.query(PowerStation).first().id)

        edit_power_station(self.browser, 1)

        fill_in_power_station(self.browser, name='Extra Special Power Station')
        self.browser.click_link_by_text('Cancel')

        assert not self.browser.find_by_css('#power_station_form').first.visible
        assert not self.browser.find_by_name('power_station_id').first.value == saved_id
        eq_(self.browser.find_by_name('power_station_description').first.value, 'Power Station 2')

        assert_number_of_power_station_table_rows(self.browser, 1)
        assert_power_station_table_row(self.browser, 1, name='Test Power Station Name')

    def test_deleting_power_station_from_edit_position(self):
        show_power_station_form(self.browser)
        assert not self.browser.is_text_present('Delete')
        submit_power_station_form(self.browser)

        errors = self.browser.find_by_css('.error_message')
        assert len(errors) == 0, "expected no errors, got %s errors" % str([error.value for error in errors])
        assert_number_of_power_station_table_rows(self.browser, 1)

        edit_power_station(self.browser, 1)

        assert self.browser.is_text_present('Delete')
        self.browser.click_link_by_text('Delete')

        eq_(len(self.browser.find_by_css('.power_station_table')), 0)

    def test_able_to_toggle_between_adding_standalone_inverter_and_power_station_and_monitoring(self):
        assert not self.browser.find_by_css('#add_standalone_inverter').first.has_class('disabled')
        show_standalone_inverter_form(self.browser)
        assert self.browser.find_by_css('#add_standalone_inverter').first.has_class('disabled')
        assert not self.browser.find_by_css('#add_new_power_station').first.has_class('disabled')
        assert not self.browser.find_by_css('#add_supervisor_monitor').first.has_class('disabled')
        assert not self.browser.is_text_present('Power Station Description')
        assert not self.browser.is_text_present('Power Source')
        assert self.browser.is_text_present('Attachment Point')

        show_power_station_form(self.browser)
        assert not self.browser.find_by_css('#add_standalone_inverter').first.has_class('disabled')
        assert not self.browser.find_by_css('#add_supervisor_monitor').first.has_class('disabled')
        assert self.browser.find_by_css('#add_new_power_station').first.has_class('disabled')
        assert self.browser.is_text_present('Power Station Description')
        assert not self.browser.is_text_present('Power Source')
        assert not self.browser.is_text_present('Attachment Point')

        show_monitoring_form(self.browser)
        assert self.browser.find_by_css('#add_supervisor_monitor').first.has_class('disabled')
        assert not self.browser.find_by_css('#add_standalone_inverter').first.has_class('disabled')
        assert not self.browser.find_by_css('#add_standalone_inverter').first.has_class('disabled')
        assert not self.browser.is_text_present('Power Station Description')
        assert not self.browser.is_text_present('Attachment Point')
        assert self.browser.is_text_present('Power Source')

    def test_adding_stand_alone_inverter(self):
        show_standalone_inverter_form(self.browser)
        fill_in_standalone_inverter(self.browser, model=InverterType.SMA.MODEL_20KW)
        eq_(self.browser.find_by_name('inverter-strings_per_inverter').first.value, '6')
        attachment_point_field = self.browser.find_by_name('attachment_point').first
        attachment_point_options = attachment_point_field.find_by_css('option')
        eq_(len(attachment_point_options), 1)

    def test_adding_stand_alone_inverter_shows_quantity(self):
        show_standalone_inverter_form(self.browser)
        assert self.browser.is_element_visible_by_css('#inverter-quantity')

    def test_editing_stand_alone_inverter_hides_quantity(self):
        show_standalone_inverter_form(self.browser)
        fill_in_standalone_inverter(self.browser)
        submit_standalone_inverter_form(self.browser)

        edit_standalone_inverter(self.browser, 1)
        assert self.browser.is_element_not_visible_by_css('#inverter-quantity')

    def test_adding_stand_alone_inverter_allows_previously_added_power_stations_as_attachment_points(self):
        show_power_station_form(self.browser)
        fill_in_power_station(self.browser, name='Test Power Station Name', count=1, inverter_count=4)
        submit_power_station_form(self.browser)

        show_power_station_form(self.browser)
        fill_in_power_station(self.browser, name='Test Power Station Name 2', count=1, inverter_count=3)
        submit_power_station_form(self.browser)

        show_standalone_inverter_form(self.browser)
        attachment_point_field = self.browser.find_by_name('attachment_point').first
        attachment_point_options = attachment_point_field.find_by_css('option')
        eq_(len(attachment_point_options), 2)
        eq_(attachment_point_options.first.text, 'Switch Gear')
        eq_(attachment_point_options.last.text, 'Test Power Station Name 2')

    def test_editing_sma_standalone_inverters(self):
        show_standalone_inverter_form(self.browser)
        fill_in_standalone_inverter(self.browser, model=InverterType.SMA.MODEL_12KW, strings=3, dc_switch=True, ac_run_length=453)
        submit_standalone_inverter_form(self.browser)
        assert_number_of_standalone_inverter_table_rows(self.browser, 1)
        saved_id = str(self.test_db_session.query(StandaloneInverter).first().id)

        show_standalone_inverter_form(self.browser)
        fill_in_standalone_inverter(self.browser, sunshade=True)
        submit_standalone_inverter_form(self.browser)
        assert_number_of_standalone_inverter_table_rows(self.browser, 2)

        edit_standalone_inverter(self.browser, 1)
        assert self.browser.find_by_css('#standalone_inverter_form').first.visible
        eq_(self.browser.find_by_name('standalone_inverter_id').first.value, saved_id)
        eq_(self.browser.find_by_name('standalone_ac_run_length').first.value, '453')
        eq_(self.browser.find_by_name('inverter-model').first.value, str(InverterType.SMA.MODEL_12KW.value))
        eq_(self.browser.find_by_name('inverter-strings_per_inverter').first.value, '3')
        eq_(self.browser.find_by_name('inverter-sunshade').first.checked, False)
        eq_(self.browser.find_by_name('inverter-dc_switch').first.checked, True)

        self.browser.check('inverter-sunshade')
        self.browser.select('inverter-model', InverterType.SMA.MODEL_15KW.value)
        self.browser.find_by_value('Save').first.click()

        assert_number_of_standalone_inverter_table_rows(self.browser, 2)
        assert_standalone_inverter_row(self.browser, 1,
                                       model=InverterType.SMA.MODEL_15KW,
                                       interconnect='Switch Gear',
                                       strings=5,
                                       sunshade=True,
                                       dc_switch=True)

    def test_editing_delta_standalone_inverters(self):
        select_inverter_brand(self.browser, brand=InverterBrand.DELTA.value)

        show_standalone_inverter_form(self.browser)
        fill_in_standalone_inverter(self.browser, model=InverterType.DELTA.MODEL_36KW, ac_run_length=453)
        submit_standalone_inverter_form(self.browser)

        assert_standalone_inverter_row(self.browser, 1,
                                       model=InverterType.DELTA.MODEL_36KW,
                                       interconnect='Switch Gear',
                                       splice_box=True)

        saved_id = str(self.test_db_session.query(StandaloneInverter).first().id)

        show_standalone_inverter_form(self.browser)
        fill_in_standalone_inverter(self.browser, model=InverterType.DELTA.MODEL_60KW, splice_box=False)
        submit_standalone_inverter_form(self.browser)
        assert_number_of_standalone_inverter_table_rows(self.browser, 2)

        edit_standalone_inverter(self.browser, 1)
        assert self.browser.find_by_css('#standalone_inverter_form').first.visible
        eq_(self.browser.find_by_name('standalone_inverter_id').first.value, saved_id)
        eq_(self.browser.find_by_name('standalone_ac_run_length').first.value, '453')
        eq_(self.browser.find_by_name('inverter-model').first.value, str(InverterType.DELTA.MODEL_36KW.value))
        eq_(self.browser.find_by_name('inverter-splice_box').first.checked, True)

        self.browser.uncheck('inverter-splice_box')
        self.browser.find_by_value('Save').first.click()

        assert_number_of_standalone_inverter_table_rows(self.browser, 2)
        assert_standalone_inverter_row(self.browser, 1,
                                       model=InverterType.DELTA.MODEL_36KW,
                                       interconnect='Switch Gear',
                                       splice_box=False)

    def test_delta_standalone_inverters_defaults_with_splice_box(self):
        pass

    def test_deleting_standalone_inverters_from_edit(self):
        show_standalone_inverter_form(self.browser)
        submit_standalone_inverter_form(self.browser)

        edit_standalone_inverter(self.browser, 1)
        self.browser.is_text_present('Delete')
        self.browser.click_link_by_partial_href('delete_standalone_inverter')

        eq_(len(self.browser.find_by_css('.standalone_inverter_table')), 0)

    def test_editing_power_monitors(self):
        show_power_station_form(self.browser)
        fill_in_power_station(self.browser, name='Test Power Station Name', count=1, inverter_count=4)
        submit_power_station_form(self.browser)
        power_station_id = str(self.test_db_session.query(PowerStation).first().id)

        show_monitoring_form(self.browser)
        self.browser.select('power_source', power_station_id)
        submit_monitoring_form(self.browser)
        saved_id = str(self.test_db_session.query(PowerMonitor).first().id)

        show_monitoring_form(self.browser)
        submit_monitoring_form(self.browser)

        assert_supervisor_monitor_row(self.browser, 1, 'Test Power Station Name')
        edit_supervisor_monitor(self.browser, 1)
        assert self.browser.find_by_css('#supervisor_monitor_form').first.visible
        eq_(self.browser.find_by_name('power_source').first.value, power_station_id)
        eq_(self.browser.find_by_css('#supervisor_monitor_form #monitor_id').first.value, saved_id)
        self.browser.find_by_value('Save').first.click()

        assert_number_of_supervisor_monitor_table_rows(self.browser, 2)
        assert_supervisor_monitor_row(self.browser, 1, 'Test Power Station Name')

    def test_editing_power_station_after_assigning_supervisor_monitor(self):
        show_power_station_form(self.browser)
        fill_in_power_station(self.browser, name='Test Power Station Name', count=1, inverter_count=4)
        submit_power_station_form(self.browser)
        power_station_id = str(self.test_db_session.query(PowerStation).first().id)

        show_monitoring_form(self.browser)
        self.browser.select('power_source', power_station_id)
        submit_monitoring_form(self.browser)

        edit_power_station(self.browser, 1)
        self.browser.find_by_value('Save').first.click()

        row = self.browser.find_by_css('.power_station_table tr:nth-child(2) td:nth-child(1)').first
        assert "Monitoring" in row.text

    def test_deleting_power_monitors_from_edit(self):
        show_monitoring_form(self.browser)
        submit_monitoring_form(self.browser)

        edit_supervisor_monitor(self.browser, 1)
        self.browser.is_text_present('Delete')
        self.browser.click_link_by_partial_href('delete_supervisor_monitor')

        eq_(len(self.browser.find_by_css('.supervisor_monitor_table')), 0)
