import unittest
import mockredis
from nose.tools import eq_
from splinter import Browser
from helix import main
from helix.constants import redis_constant
from helix.constants import sql_constant
from helix.constants.anchor_type import AnchorType
from helix.constants.system_type import SystemType
from helix.constants.module_type import ModuleType
from helix.models.sql.inverters import Inverter
from helix.models.sql.power_monitors import PowerMonitor
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.sql.users import User
from test.test_helpers import assert_error, assert_no_error, test_db_session, reset_db_session


class SiteCharacterizationTest(unittest.TestCase):
    @classmethod
    def setUpClass(cls):
        cls.test_db_session = test_db_session()

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

    def setUp(self):
        redis_constant.redis_store = mockredis.mock_redis_client()
        sql_constant.sql_session_maker = lambda: self.test_db_session
        self.app = main.app.test_client()
        self.browser = Browser('flask', app=main.app)
        self.browser.visit('/site_characterization')

    def tearDown(self):
        self.test_db_session.rollback()
        reset_db_session(self.test_db_session)

    def fill_in_site_characterization_data(self, module_type=ModuleType.Cell96):
        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('exposure_category', 'C')
        self.browser.fill('ballast_block_weight', '13')
        self.browser.fill('building_parapet_height', '0')
        self.browser.fill('max_system_pressure', '10')
        self.browser.fill('module_type', module_type.value)
        self.browser.fill('system_type', SystemType.singleTilt.value)
        self.browser.fill('anchor_type', AnchorType.EcoFasten.value)
        self.browser.fill('importance_factor', '1.5')
        self.browser.fill('design_spectral_response', '1')

    def test_page_displays_welcome_text(self):
        assert self.browser.is_text_present("Helix Calculator")

    def test_navigation_header_shows_user_on_first_step(self):
        assert self.browser.find_by_css('.navigation_header #step_1').has_class('active')
        assert not self.browser.find_by_css('.navigation_header #step_1').has_class('completed')

    def test_completing_site_characterization_step_updates_navigation_header(self):
        self.fill_in_site_characterization_data()
        self.browser.find_by_value('Next').first.click()

        assert self.browser.find_by_css('.navigation_header #step_2').has_class('active')
        assert not self.browser.find_by_css('.navigation_header #step_1').has_class('active')
        assert self.browser.find_by_css('.navigation_header #step_1').has_class('completed')
        self.browser.click_link_by_partial_text("Back")
        assert not self.browser.find_by_css('.navigation_header #step_2').has_class('active')
        assert self.browser.find_by_css('.navigation_header #step_1').has_class('active')
        assert self.browser.find_by_css('.navigation_header #step_1').has_class('completed')

    def test_entering_site_characterization_data_saves_entered_values(self):
        self.fill_in_site_characterization_data() # TODO: Fill in to change to 128
        self.browser.find_by_value('Next').first.click()
        self.browser.click_link_by_partial_text("Back")
        eq_(self.browser.find_by_name('project_name').first.value, 'Test Project Name')
        eq_(self.browser.find_by_name('building_width').first.value, '200.0')
        eq_(self.browser.find_by_name('building_height').first.value, '30.0')
        eq_(self.browser.find_by_name('building_length').first.value, '75.5')
        eq_(self.browser.find_by_name('wind_speed').first.value, '125')
        eq_(self.browser.find_by_name('exposure_category').first.value, 'C')
        eq_(self.browser.find_by_name('ballast_block_weight').first.value, '13.0')
        eq_(self.browser.find_by_name('building_parapet_height').first.value, '0.0')
        eq_(self.browser.find_by_name('max_system_pressure').first.value, '10.0')
        eq_(self.browser.find_by_name('module_type').first.value, ModuleType.Cell96.value)
        eq_(self.browser.find_by_name('system_type').first.value, SystemType.singleTilt.value)
        eq_(self.browser.find_by_name('anchor_type').first.value, AnchorType.EcoFasten.value)
        eq_(self.browser.find_by_name('importance_factor').first.value, '1.5')
        eq_(self.browser.find_by_name('design_spectral_response').first.value, '1.0')

    def test_form_shows_wind_speed_link(self):
        info_link = self.browser.find_by_css('#wind_speed_row a').first
        eq_(info_link.text, 'Look up')
        eq_(info_link['href'], 'http://windspeed.atcouncil.org/')

    def test_form_shows_seismic_link(self):
        info_link = self.browser.find_by_css('#design_spectral_response_row a').first
        eq_(info_link.text, 'Look up')
        eq_(info_link['href'], 'http://earthquake.usgs.gov/designmaps/us/application.php')

    def test_form_shows_exposure_category_link(self):
        info_link = self.browser.find_by_css('#exposure_category_row a').first
        eq_(info_link.text, 'More info')
        info_link.click()
        assert self.browser.is_text_present("Urban and suburban areas, wooded areas")

    def test_all_site_charactarization_values_are_required(self):
        self.browser.fill('ballast_block_weight', '')
        self.browser.fill('max_system_pressure', '')
        self.browser.find_by_value('Next').first.click()
        assert_error(self.browser, 'project_name', 'Project Name is required.')
        assert_error(self.browser, 'building_height', 'Not a valid decimal value')
        assert_error(self.browser, 'building_width', 'Not a valid decimal value')
        assert_error(self.browser, 'building_length', 'Not a valid decimal value')
        assert_error(self.browser, 'wind_speed', 'Not a valid integer value')
        assert_error(self.browser, 'ballast_block_weight', 'Not a valid decimal value')
        assert_error(self.browser, 'building_parapet_height', 'Not a valid decimal value')
        assert_error(self.browser, 'max_system_pressure', 'Not a valid decimal value')
        assert_error(self.browser, 'design_spectral_response', 'Not a valid decimal value')

    def test_characterization_values_not_overwritten_when_Nextting_invalid_request(self):
        self.browser.fill('project_name', '')
        self.browser.fill('building_width', '-1')
        self.browser.fill('building_height', '-10')
        self.browser.fill('building_length', 'hi')
        self.browser.fill('wind_speed', '400')
        self.browser.fill('exposure_category', 'D')
        self.browser.fill('ballast_block_weight', '40')
        self.browser.fill('building_parapet_height', '-1')
        self.browser.fill('max_system_pressure', '0')
        self.browser.fill('system_type', SystemType.dualTilt.value)
        self.browser.fill('module_type', ModuleType.Cell128.value)
        self.browser.fill('anchor_type', AnchorType.OMG_PowerGrip.value)
        self.browser.fill('design_spectral_response', '-2')
        self.browser.find_by_value('Next').first.click()

        eq_(self.browser.find_by_name('project_name').first.value, '')
        eq_(self.browser.find_by_name('building_width').first.value, '-1')
        eq_(self.browser.find_by_name('building_height').first.value, '-10')
        eq_(self.browser.find_by_name('building_length').first.value, 'hi')
        eq_(self.browser.find_by_name('wind_speed').first.value, '400')
        eq_(self.browser.find_by_name('exposure_category').first.value, 'D')
        eq_(self.browser.find_by_name('ballast_block_weight').first.value, '40')
        eq_(self.browser.find_by_name('building_parapet_height').first.value, '-1')
        eq_(self.browser.find_by_name('max_system_pressure').first.value, '0')
        eq_(self.browser.find_by_name('system_type').first.value, SystemType.dualTilt.value)
        eq_(self.browser.find_by_name('module_type').first.value, ModuleType.Cell128.value)
        eq_(self.browser.find_by_name('anchor_type').first.value, AnchorType.OMG_PowerGrip.value)
        eq_(self.browser.find_by_name('design_spectral_response').first.value, '-2')


    def test_wind_speed_value_must_be_in_range(self):
        self.browser.fill('wind_speed', '90')
        self.browser.find_by_value('Next').first.click()
        assert_error(self.browser, 'wind_speed', 'Number must be between 100 and 200.')

    def test_ballast_block_weight_value_must_be_in_range(self):
        self.browser.fill('ballast_block_weight', '90')
        self.browser.find_by_value('Next').first.click()
        assert_error(self.browser, 'ballast_block_weight', 'Number must be between 12 and 20.')

    def test_building_dimensions_must_be_non_negative(self):
        self.browser.fill('building_width', '-4')
        self.browser.fill('building_height', '-10')
        self.browser.fill('building_length', '-1')
        self.browser.fill('building_parapet_height', '-3')
        self.browser.find_by_value('Next').first.click()

        assert_error(self.browser, 'building_height', 'Number must be at least 0.')
        assert_error(self.browser, 'building_width', 'Number must be at least 0.')
        assert_error(self.browser, 'building_length', 'Number must be at least 0.')
        assert_error(self.browser, 'building_parapet_height', 'Number must be at least 0.')

    def test_transition_distance_must_be_filled_out_for_interpolating_exposure_categories(self):
        self.browser.fill('exposure_category', 'B to C')
        self.browser.find_by_value('Next').first.click()

        assert_error(self.browser, 'exposure_category_transition_distance', 'Number must be at least 1.')

    def test_transition_distance_must_be_greater_than_0_for_interpolating_exposure_categories(self):
        self.browser.fill('exposure_category', 'B to C')
        self.browser.fill('exposure_category_transition_distance', '-10')
        self.browser.find_by_value('Next').first.click()

        assert_error(self.browser, 'exposure_category_transition_distance', 'Number must be at least 1.')

    def test_transition_distance_standard_categories_does_not_need_to_be_filled_out(self):
        self.browser.fill('exposure_category', 'B')
        self.browser.find_by_value('Next').first.click()

        assert_no_error(self.browser, 'exposure_category_transition_distance')

    def test_max_system_pressure_must_be_non_negative(self):
        self.browser.fill('max_system_pressure', '-2')
        self.browser.find_by_value('Next').first.click()
        assert_error(self.browser, 'max_system_pressure', 'Number must be at least 0.')

    def test_design_spectral_response_must_be_in_range(self):
        self.browser.fill('design_spectral_response', '-1')
        self.browser.find_by_value('Next').first.click()
        assert_error(self.browser, 'design_spectral_response', 'Number must be between 0 and 5.')

    def test_building_dimensions_allow_zero(self):
        self.browser.fill('building_width', '0')
        self.browser.fill('building_height', '0')
        self.browser.fill('building_length', '0')
        self.browser.find_by_value('Next').first.click()

        assert_no_error(self.browser, 'building_width')
        assert_no_error(self.browser, 'building_height')
        assert_no_error(self.browser, 'building_length')

    def test_ballast_block_weight_defaults_to_14(self):
        eq_(self.browser.find_by_name('ballast_block_weight').first.value, '14.0')

    def test_max_system_pressure_defaults_to_10(self):
        eq_(self.browser.find_by_name('max_system_pressure').first.value, '12.0')

    def test_system_type_defaults_to_dual_tilt(self):
        eq_(self.browser.find_by_name('system_type').first.value, SystemType.dualTilt.value)

    def test_module_type_defaults_to_96_cell(self):
        eq_(self.browser.find_by_name('module_type').first.value, ModuleType.Cell96.value)

    def test_exposure_transition_distance_defaults_to_zero(self):
        eq_(self.browser.find_by_name('exposure_category_transition_distance').first.value, '0')
