import json
import uuid

import os

from helix.constants.anchor_type import AnchorType
from helix.constants.module_type import ModuleType
from helix.constants.system_type import SystemType
from helix.models.sql.inverter_brands import InverterBrand
from helix.models.sql.inverters import Inverter
from helix.models.sql.power_monitors import PowerMonitor
from helix.models.sql.inverter_brands import InverterBrand
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 helix.store import Store
from helix.user_values import UserValues


class SessionManager(object):
    def __init__(self, session, raw_redis_store, db_session):
        self.session = session
        if 'id' not in session:
            session['id'] = uuid.uuid4().hex
        self.db_session = db_session
        self.user = self.db_session.query(User).filter_by(username=session['id']).first()
        if not self.user:
            self.user = User(username=session['id'], password_hash='blah')
            self.db_session.add(self.user)
            self.db_session.commit()
            self.site = None
        else:
            self.site = self.db_session.query(Site).filter_by(user_id=self.user.id).first()

        self.store = Store(raw_redis_store, session['id'])

    def context(self):
        if self.site:
            inverter_brands = [inverter_brands.to_json() for inverter_brands in self.db_session.query(InverterBrand).filter_by(site_id=self.site.id).order_by(InverterBrand.id).all()]
            power_stations = [power_station.to_json() for power_station in self.db_session.query(PowerStation).filter_by(site_id=self.site.id).order_by(PowerStation.id).all()]
            standalone_inverters = [standalone_inverter.to_json() for standalone_inverter in self.db_session.query(StandaloneInverter).filter_by(site_id=self.site.id).order_by(StandaloneInverter.id).all()]
            monitors = [monitor.to_json() for monitor in self.db_session.query(PowerMonitor).filter_by(site_id=self.site.id).order_by(PowerMonitor.id).all()]
        else:
            inverter_brands = []
            power_stations = []
            standalone_inverters = []
            monitors = []

        site_data_available = self.site is not None
        csv_available = self.site.cad_file is not None if self.site else False

        return {
            'id': self.session['id'],
            'google_analytics_token': os.getenv('GOOGLE_ANALYTICS', ''),
            'csv_available': csv_available,
            'site_data_available': site_data_available,
            'inverter_brands': inverter_brands,
            'power_stations': power_stations,
            'standalone_inverters': standalone_inverters,
            'power_monitors': monitors,
            'system_type': SystemType(self.site.system_type) if self.site else SystemType.dualTilt,
            'steps': [
                (1, 'Site Characterization', '/site_characterization/', site_data_available),
                (2, 'Site Summary', '/summary/', site_data_available),
                (3, 'Array Summary', '/array_summary/', csv_available),
                (4, 'Power Station Configuration', '/power_station_configuration/',
                 True if power_stations or standalone_inverters else False),
                (5, 'Download', '/download/', None)
            ]
        }

    def user_values(self):
        return UserValues(self.store, self.site)

    def save_form_submission(self, form_data):
        if not self.site:
            self.site = Site(
                user_id=self.user.id,
                project_name=form_data.get('project_name', ''),
                building_height=form_data.get('building_height', 0),
                building_width=form_data.get('building_width', 0),
                building_length=form_data.get('building_length', 0),
                parapet_height=form_data.get('building_parapet_height', 0),
                wind_speed=form_data.get('wind_speed', 0),
                exposure_category=form_data.get('exposure_category', 'C'),
                exposure_transition_distance=form_data.get('exposure_category_transition_distance', 0),
                ballast_block_weight=form_data.get('ballast_block_weight', 0),
                max_psf=form_data.get('max_system_pressure', 0),
                system_type=form_data.get('system_type', SystemType.dualTilt.value),
                module_type=form_data.get('module_type', ModuleType.Cell96.value),
                anchor_type=form_data.get('anchor_type', AnchorType.OMG_PowerGrip_Plus.value),
                spectral_response=form_data.get('design_spectral_response', 1),
                seismic_importance_factor=form_data.get('importance_factor', 1)
            )
            self.db_session.add(self.site)
            self.db_session.commit()
        else:
            old_system_type = self.site.system_type
            old_module_type = self.site.module_type
            self.site.project_name = form_data.get('project_name', self.site.project_name)
            self.site.building_height = form_data.get('building_height', self.site.building_height)
            self.site.building_width = form_data.get('building_width', self.site.building_width)
            self.site.building_length = form_data.get('building_length', self.site.building_length)
            self.site.parapet_height = form_data.get('building_parapet_height', self.site.parapet_height)
            self.site.wind_speed = form_data.get('wind_speed', self.site.wind_speed)
            self.site.exposure_category = form_data.get('exposure_category', self.site.exposure_category)
            self.site.exposure_transition_distance = form_data.get('exposure_category_transition_distance',
                                                                   self.site.exposure_transition_distance)
            self.site.ballast_block_weight = form_data.get('ballast_block_weight', self.site.ballast_block_weight)
            self.site.max_psf = form_data.get('max_system_pressure', self.site.max_psf)
            self.site.system_type = form_data.get('system_type', self.site.system_type)
            self.site.module_type = form_data.get('module_type', self.site.module_type)
            self.site.anchor_type = form_data.get('anchor_type', self.site.anchor_type)
            self.site.spectral_response = form_data.get('design_spectral_response', self.site.spectral_response)
            self.site.seismic_importance_factor = form_data.get('importance_factor', self.site.seismic_importance_factor)
            if old_system_type != self.site.system_type or old_module_type != self.site.module_type:
                self.site.cad_file = None
                self.site.cad_file_name = None
                self.site.dxf_file = None
                self.site.dxf_file_name = None
            self.db_session.commit()

    def save_or_update_power_station(self, form_data):
        power_station = None
        if form_data['power_station_id']:
            power_station = self.db_session.query(PowerStation).get(int(form_data['power_station_id']))

        if not power_station:
            power_station = PowerStation(
                site_id=self.site.id,
                quantity=int(form_data.get('power_station_quantity', 0)),
                ac_run_length=int(form_data.get('ac_run_length', 0)),
                description=form_data.get('power_station_description', ''),
            )
            self.db_session.add(power_station)

        else:
            power_station.quantity = int(form_data.get('power_station_quantity', power_station.quantity))
            power_station.ac_run_length = int(form_data.get('ac_run_length', power_station.ac_run_length))
            power_station.description = form_data.get('power_station_description', power_station.description)

            # Delete all inverters for now in order to create new ones on update
            for inverter in power_station.inverters:
                self.db_session.delete(inverter)

        self.db_session.commit()

        inverter_quantity = int(form_data['inverter_quantity'])

        if inverter_quantity == 0:
            return

        for index in range(inverter_quantity):
            prefix = 'inverter_' + str(index + 1)

            inverter = Inverter(
                model=str(form_data[prefix + '-model']),
                strings_per_inverter=int(form_data[prefix + '-strings_per_inverter']),
                sunshade=bool(form_data.get(prefix + '-sunshade', False)),
                dc_switch=bool(form_data.get(prefix + '-dc_switch', False)),
                power_station_id=power_station.id,
                standalone_inverter_id=None,
            )
            self.db_session.add(inverter)
        self.db_session.commit()

    def delete_power_station(self, power_station_id):
        power_station = self.db_session.query(PowerStation).get(power_station_id)
        if power_station:
            attached_standalone_inverters = self.db_session.query(StandaloneInverter).filter(StandaloneInverter.attachment_point_id==power_station.id).all()
            for standalone_inverter in attached_standalone_inverters:
                standalone_inverter.attachment_point_id = None

            attached_power_monitors = self.db_session.query(PowerMonitor).filter(PowerMonitor.power_station_id==power_station.id).all()
            for power_monitor in attached_power_monitors:
                power_monitor.power_station_id = None

            self.db_session.delete(power_station)
            self.db_session.commit()

    def delete_power_station_config_data(self):
        site_id = self.site.id
        power_stations = self.db_session.query(PowerStation).filter(PowerStation.site_id==site_id).all()

        for power_station in power_stations:
            self.db_session\
                .query(StandaloneInverter)\
                .filter(StandaloneInverter.attachment_point_id==power_station.id)\
                .delete()
            self.db_session\
                .query(PowerMonitor)\
                .filter(PowerMonitor.power_station_id==power_station.id)\
                .delete()
            self.db_session.delete(power_station)

        self.db_session.query(StandaloneInverter).filter(StandaloneInverter.site_id==site_id).delete()
        self.db_session.query(PowerMonitor).filter(PowerMonitor.site_id==site_id).delete()
        self.db_session.commit()

    def save_or_update_inverter_brands(self, form_data):
        inverter_brand = None

        if form_data.get('inverter_brand_id'):
            inverter_brand = self.db_session.query(InverterBrand).filter_by(
                site_id=self.site.id
            ).first()

        if not inverter_brand:
            inverter_brand = InverterBrand(
                id=int(form_data.get('inverter_brand_id')),
                site_id=self.site.id,
            )

            self.db_session.add(inverter_brand)
            self.db_session.commit()
        else:
            inverter_brand.id = int(form_data.get('inverter_brand_id', inverter_brand.id))

        self.db_session.commit()


    def save_or_update_standalone_inverter(self, form_data):
        standalone_inverter = None
        prefix = 'inverter-'

        if form_data.get('standalone_inverter_id'):
            standalone_inverter = self.db_session.query(StandaloneInverter).get(int(form_data.get('standalone_inverter_id')))

        if not standalone_inverter:
            quantity = int(form_data[prefix + 'quantity'])

            for n in range(quantity):
                standalone_inverter = StandaloneInverter(
                    site_id=self.site.id,
                    attachment_point_id=form_data.get('attachment_point') if form_data.get('attachment_point') != 'switch_gear' else None,
                    ac_run_length=int(form_data.get('standalone_ac_run_length', 0))
                )
                self.db_session.add(standalone_inverter)
                self.db_session.commit()
                inverter = Inverter(
                    model=str(form_data[prefix + 'model']),
                    strings_per_inverter=int(form_data[prefix + 'strings_per_inverter']),
                    sunshade=bool(form_data.get(prefix + 'sunshade', False)),
                    dc_switch=bool(form_data.get(prefix + 'dc_switch', False)),
                    splice_box=bool(form_data.get(prefix + 'splice_box', False)),
                    power_station_id=None,
                    standalone_inverter_id=standalone_inverter.id,
                )
                self.db_session.add(inverter)
                self.db_session.commit()
        else:
            standalone_inverter.ac_run_length = int(form_data.get('standalone_ac_run_length', standalone_inverter.ac_run_length))
            standalone_inverter.attachment_point_id = form_data.get('attachment_point') if form_data.get('attachment_point', standalone_inverter.attachment_point_id) != 'switch_gear' else None

            standalone_inverter.inverter[0].model = str(form_data[prefix + 'model'])
            standalone_inverter.inverter[0].strings_per_inverter = int(form_data[prefix + 'strings_per_inverter'])
            standalone_inverter.inverter[0].sunshade = bool(form_data.get(prefix + 'sunshade', False))
            standalone_inverter.inverter[0].dc_switch = bool(form_data.get(prefix + 'dc_switch', False))
            standalone_inverter.inverter[0].splice_box = bool(form_data.get(prefix + 'splice_box', False))
            self.db_session.commit()

    def delete_standalone_inverter(self, inverter_id):
        standalone_inverter = self.db_session.query(StandaloneInverter).get(inverter_id)
        if standalone_inverter:
            self.db_session.delete(standalone_inverter)
            self.db_session.commit()

    def save_or_update_supervisor_monitor(self, form_data):
        monitor_id = form_data.get('monitor_id')
        if monitor_id:
            monitor = self.db_session.query(PowerMonitor).get(monitor_id)
            monitor.power_station_id = self.interpret_power_source(form_data.get('power_source'))
        else:
            monitor = PowerMonitor(
                site_id=self.site.id,
                power_station_id=self.interpret_power_source(form_data['power_source'])
            )
        self.db_session.add(monitor)
        self.db_session.commit()

    def delete_supervisor_monitor(self, monitor_id):
        monitor = self.db_session.query(PowerMonitor).get(monitor_id)
        if monitor:
            self.db_session.delete(monitor)
            self.db_session.commit()

    def save_uploaded_file(self, contents, cad_file_name=None, dxf_file_name=None):
        self.site.cad_file = contents
        self.site.cad_file_name = cad_file_name
        self.site.dxf_file_name = dxf_file_name
        self.store.delete('user_override_seismic_anchors')
        self.db_session.commit()

    def fill_saved_values_in_form(self, form):
        if self.site:
            form.project_name.data = self.site.project_name
            form.building_height.data = self.site.building_height
            form.building_width.data = self.site.building_width
            form.building_length.data = self.site.building_length
            form.building_parapet_height.data = self.site.parapet_height
            form.wind_speed.data = self.site.wind_speed
            form.exposure_category.data = self.site.exposure_category
            form.exposure_category_transition_distance.data = self.site.exposure_transition_distance
            form.ballast_block_weight.data = self.site.ballast_block_weight
            form.max_system_pressure.data = self.site.max_psf
            form.system_type.data = self.site.system_type
            form.module_type.data = self.site.module_type
            form.anchor_type.data = self.site.anchor_type
            form.design_spectral_response.data = self.site.spectral_response
            form.importance_factor.data = str(self.site.seismic_importance_factor)

    def save_user_provided_seismic_anchors(self, seismic_anchor_json):
        self.store.set("user_override_seismic_anchors", json.dumps(seismic_anchor_json))

    def save_buildings_polygons(self, buildings):
        # buildings is a
        # list of lists of points presented by list of two numbers
        # [ [ [123.1, 111.1] , [ 222.1, 223.2 ]  , [ 212.1, -225.2 ] ] ]
        polygons_primitive = list(map(lambda x: x.points, buildings))
        self.store.set("buildings_polygons", json.dumps(polygons_primitive))

    def save_is_drawing_inaccurate(self, is_drawing_inaccurate):
        self.store.set("is_drawing_inaccurate", json.dumps(is_drawing_inaccurate))

    def get_map_index(self, list_id, identifier_key, identifier):
        maps = self.store.get_list_of_maps(list_id)
        for idx, val in enumerate(maps):
            if val[identifier_key] == identifier:
                return idx

    def interpret_power_source(self, power_source):
        if power_source != 'switch_gear':
            power_station = self.db_session.query(PowerStation).get(power_source)
            if power_station:
                return power_station.id

        return None

