'use strict';
import Store from 'electron-store';
import Connection from '@/ipc-api/Connection';
import Schema from '@/ipc-api/Schema';
import Users from '@/ipc-api/Users';
import { uidGen } from 'common/libs/uidGen';
const persistentStore = new Store({ name: 'tabs' });
const tabIndex = [];

export default {
   namespaced: true,
   strict: true,
   state: {
      workspaces: [],
      selected_workspace: null
   },
   getters: {
      getSelected: state => {
         if (state.selected_workspace) return state.selected_workspace;
         if (state.workspaces.length) return state.workspaces[0].uid;
         return 'NEW';
      },
      getWorkspace: state => uid => {
         return state.workspaces.find(workspace => workspace.uid === uid);
      },
      getDatabaseVariable: state => (uid, name) => {
         return state.workspaces.find(workspace => workspace.uid === uid).variables.find(variable => variable.name === name);
      },
      getWorkspaceTab: (state, getters) => tUid => {
         if (!getters.getSelected) return;
         const workspace = state.workspaces.find(workspace => workspace.uid === getters.getSelected);
         if ('tabs' in workspace)
            return workspace.tabs.find(tab => tab.uid === tUid);
         return {};
      },
      getConnected: state => {
         return state.workspaces
            .filter(workspace => workspace.connectionStatus === 'connected')
            .map(workspace => workspace.uid);
      },
      getLoadedSchemas: state => uid => {
         return state.workspaces.find(workspace => workspace.uid === uid).loadedSchemas;
      },
      getSearchTerm: state => uid => {
         return state.workspaces.find(workspace => workspace.uid === uid).searchTerm;
      }
   },
   mutations: {
      SELECT_WORKSPACE (state, uid) {
         if (!uid)
            state.selected_workspace = state.workspaces.length ? state.workspaces[0].uid : 'NEW';
         else
            state.selected_workspace = uid;
      },
      SET_CONNECTED (state, payload) {
         const { uid, client, dataTypes, indexTypes, customizations, structure, version } = payload;

         const cachedTabs = payload.restoreTabs ? persistentStore.get(uid, []) : [];

         if (cachedTabs.length) {
            tabIndex[uid] = cachedTabs.reduce((acc, curr) => {
               if (curr.index > acc) acc = curr.index;
               return acc;
            }, null);
         }

         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               client,
               dataTypes,
               indexTypes,
               customizations,
               structure,
               connectionStatus: 'connected',
               tabs: cachedTabs,
               selectedTab: cachedTabs.length ? cachedTabs[0].uid : null,
               version
            }
            : workspace);
      },
      SET_CONNECTING (state, uid) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               structure: {},
               breadcrumbs: {},
               loadedSchemas: new Set(),
               connectionStatus: 'connecting'
            }
            : workspace);
      },
      SET_FAILED (state, uid) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               structure: {},
               breadcrumbs: {},
               loadedSchemas: new Set(),
               connectionStatus: 'failed'
            }
            : workspace);
      },
      SET_DISCONNECTED (state, uid) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               structure: {},
               breadcrumbs: {},
               loadedSchemas: new Set(),
               connectionStatus: 'disconnected'
            }
            : workspace);
      },
      REFRESH_STRUCTURE (state, { uid, structure }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               structure
            }
            : workspace);
      },
      REFRESH_SCHEMA (state, { uid, schema, schemaElements }) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === uid) {
               const schemaIndex = workspace.structure.findIndex(s => s.name === schema);

               if (schemaIndex !== -1)
                  workspace.structure[schemaIndex] = schemaElements;
               else
                  workspace.structure.push(schemaElements);
            }
            return workspace;
         });
      },
      REFRESH_COLLATIONS (state, { uid, collations }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               collations
            }
            : workspace);
      },
      REFRESH_VARIABLES (state, { uid, variables }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               variables
            }
            : workspace);
      },
      REFRESH_ENGINES (state, { uid, engines }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               engines
            }
            : workspace);
      },
      REFRESH_USERS (state, { uid, users }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               users
            }
            : workspace);
      },
      ADD_WORKSPACE (state, workspace) {
         state.workspaces.push(workspace);
      },
      CHANGE_BREADCRUMBS (state, { uid, breadcrumbs }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               breadcrumbs
            }
            : workspace);
      },
      SET_SEARCH_TERM (state, { uid, term }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid
            ? {
               ...workspace,
               searchTerm: term
            }
            : workspace);
      },
      NEW_TAB (state, { uid, tab, content, type, autorun, schema, elementName, elementType }) {
         if (type === 'query')
            tabIndex[uid] = tabIndex[uid] ? ++tabIndex[uid] : 1;

         const newTab = {
            uid: tab,
            index: type === 'query' ? tabIndex[uid] : null,
            selected: false,
            type,
            schema,
            elementName,
            elementType,
            fields: [],
            keyUsage: [],
            content: content || '',
            autorun: !!autorun
         };

         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === uid) {
               return {
                  ...workspace,
                  tabs: [...workspace.tabs, newTab]
               };
            }
            else
               return workspace;
         });

         persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
      },
      REMOVE_TAB (state, { uid, tab: tUid }) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === uid) {
               return {
                  ...workspace,
                  tabs: workspace.tabs.filter(tab => tab.uid !== tUid)
               };
            }
            else
               return workspace;
         });

         persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
      },
      REMOVE_TABS (state, { uid, schema, elementName, elementType }) { // Multiple tabs based on schema and element name
         if (elementType === 'procedure') elementType = 'routine'; // TODO: pass directly "routine"

         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === uid) {
               return {
                  ...workspace,
                  tabs: workspace.tabs.filter(tab =>
                     tab.schema !== schema ||
                     tab.elementName !== elementName ||
                     tab.elementType !== elementType
                  )
               };
            }
            else
               return workspace;
         });

         persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
      },
      REPLACE_TAB (state, { uid, tab: tUid, type, schema, content, elementName, elementType }) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === uid) {
               return {
                  ...workspace,
                  tabs: workspace.tabs.map(tab => {
                     if (tab.uid === tUid)
                        return { ...tab, type, schema, content, elementName, elementType };

                     return tab;
                  })
               };
            }
            else
               return workspace;
         });

         persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
      },
      RENAME_TABS (state, { uid, schema, elementName, elementType, elementNewName }) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === uid) {
               return {
                  ...workspace,
                  tabs: workspace.tabs.map(tab => {
                     if (tab.elementName === elementName && tab.schema === schema) {
                        return {
                           ...tab,
                           elementName: elementNewName
                        };
                     }

                     return tab;
                  })
               };
            }
            else
               return workspace;
         });

         persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
      },
      SELECT_TAB (state, { uid, tab }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, selectedTab: tab } : workspace);
      },
      UPDATE_TABS (state, { uid, tabs }) {
         state.workspaces = state.workspaces.map(workspace => workspace.uid === uid ? { ...workspace, tabs } : workspace);
         persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
      },
      SET_TAB_FIELDS (state, { cUid, tUid, fields }) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === cUid) {
               return {
                  ...workspace,
                  tabs: workspace.tabs.map(tab => {
                     if (tab.uid === tUid)
                        return { ...tab, fields };
                     else
                        return tab;
                  })
               };
            }
            else
               return workspace;
         });

         persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
      },
      SET_TAB_KEY_USAGE (state, { cUid, tUid, keyUsage }) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === cUid) {
               return {
                  ...workspace,
                  tabs: workspace.tabs.map(tab => {
                     if (tab.uid === tUid)
                        return { ...tab, keyUsage };
                     else
                        return tab;
                  })
               };
            }
            else
               return workspace;
         });

         persistentStore.set(uid, state.workspaces.find(workspace => workspace.uid === uid).tabs);
      },
      SET_UNSAVED_CHANGES (state, { uid, tUid, isChanged }) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === uid) {
               return {
                  ...workspace,
                  tabs: workspace.tabs.map(tab => {
                     if (tab.uid === tUid)
                        return { ...tab, isChanged };

                     return tab;
                  })
               };
            }
            else
               return workspace;
         });
      },
      ADD_LOADED_SCHEMA (state, payload) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === payload.uid)
               workspace.loadedSchemas.add(payload.schema);
            return workspace;
         });
      },
      ADD_LOADING_ELEMENT (state, payload) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === payload.uid)
               workspace.loadingElements.push(payload.element);
            return workspace;
         });
      },
      REMOVE_LOADING_ELEMENT (state, payload) {
         state.workspaces = state.workspaces.map(workspace => {
            if (workspace.uid === payload.uid) {
               const loadingElements = workspace.loadingElements.filter(el =>
                  el.schema !== payload.element.schema &&
                  el.name !== payload.element.name &&
                  el.type !== payload.element.type
               );

               workspace = { ...workspace, loadingElements };
            }
            return workspace;
         });
      }
   },
   actions: {
      selectWorkspace ({ commit }, uid) {
         commit('SELECT_WORKSPACE', uid);
      },
      async connectWorkspace ({ dispatch, commit, getters, rootGetters }, connection) {
         commit('SET_CONNECTING', connection.uid);

         try {
            const { status, response } = await Connection.connect(connection);
            if (status === 'error') {
               dispatch('notifications/addNotification', { status, message: response }, { root: true });
               commit('SET_FAILED', connection.uid);
            }
            else {
               let dataTypes = [];
               let indexTypes = [];
               let customizations = {};

               switch (connection.client) {
                  case 'mysql':
                  case 'maria':
                     dataTypes = require('common/data-types/mysql');
                     indexTypes = require('common/index-types/mysql');
                     customizations = require('common/customizations/mysql');
                     break;
                  case 'pg':
                     dataTypes = require('common/data-types/postgresql');
                     indexTypes = require('common/index-types/postgresql');
                     customizations = require('common/customizations/postgresql');
                     break;
                  case 'sqlite':
                     dataTypes = require('common/data-types/sqlite');
                     indexTypes = require('common/index-types/sqlite');
                     customizations = require('common/customizations/sqlite');
                     break;
               }

               const { status, response: version } = await Schema.getVersion(connection.uid);

               if (status === 'error')
                  dispatch('notifications/addNotification', { status, message: version }, { root: true });

               // Check if Maria or MySQL
               const isMySQL = version.name.includes('MySQL');

               if (isMySQL && connection.client !== 'mysql') {
                  const connProxy = Object.assign({}, connection);
                  connProxy.client = 'mysql';
                  dispatch('connections/editConnection', connProxy, { root: true });
               }
               else if (!isMySQL && connection.client === 'mysql') {
                  const connProxy = Object.assign({}, connection);
                  connProxy.client = 'maria';
                  dispatch('connections/editConnection', connProxy, { root: true });
               }

               commit('SET_CONNECTED', {
                  uid: connection.uid,
                  client: connection.client,
                  dataTypes,
                  indexTypes,
                  customizations,
                  structure: response,
                  version,
                  restoreTabs: rootGetters['settings/getRestoreTabs']
               });
               dispatch('refreshCollations', connection.uid);
               dispatch('refreshVariables', connection.uid);
               dispatch('refreshEngines', connection.uid);
               dispatch('refreshUsers', connection.uid);
            }
         }
         catch (err) {
            dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
         }
      },
      async refreshStructure ({ dispatch, commit, getters }, uid) {
         try {
            const { status, response } = await Schema.getStructure({ uid, schemas: getters.getLoadedSchemas(uid) });

            if (status === 'error')
               dispatch('notifications/addNotification', { status, message: response }, { root: true });
            else
               commit('REFRESH_STRUCTURE', { uid, structure: response });
         }
         catch (err) {
            dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
         }
      },
      async refreshSchema ({ dispatch, commit }, { uid, schema }) {
         try {
            const { status, response } = await Schema.getStructure({ uid, schemas: new Set([schema]) });
            if (status === 'error')
               dispatch('notifications/addNotification', { status, message: response }, { root: true });
            else
               commit('REFRESH_SCHEMA', { uid, schema, schemaElements: response.find(_schema => _schema.name === schema) });
         }
         catch (err) {
            dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
         }
      },
      async refreshCollations ({ dispatch, commit }, uid) {
         try {
            const { status, response } = await Schema.getCollations(uid);
            if (status === 'error')
               dispatch('notifications/addNotification', { status, message: response }, { root: true });
            else
               commit('REFRESH_COLLATIONS', { uid, collations: response });
         }
         catch (err) {
            dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
         }
      },
      async refreshVariables ({ dispatch, commit }, uid) {
         try {
            const { status, response } = await Schema.getVariables(uid);
            if (status === 'error')
               dispatch('notifications/addNotification', { status, message: response }, { root: true });
            else
               commit('REFRESH_VARIABLES', { uid, variables: response });
         }
         catch (err) {
            dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
         }
      },
      async refreshEngines ({ dispatch, commit }, uid) {
         try {
            const { status, response } = await Schema.getEngines(uid);
            if (status === 'error')
               dispatch('notifications/addNotification', { status, message: response }, { root: true });
            else
               commit('REFRESH_ENGINES', { uid, engines: response });
         }
         catch (err) {
            dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
         }
      },
      async refreshUsers ({ dispatch, commit }, uid) {
         try {
            const { status, response } = await Users.getUsers(uid);
            if (status === 'error')
               dispatch('notifications/addNotification', { status, message: response }, { root: true });
            else
               commit('REFRESH_USERS', { uid, users: response });
         }
         catch (err) {
            dispatch('notifications/addNotification', { status: 'error', message: err.stack }, { root: true });
         }
      },
      removeConnected ({ commit }, uid) {
         Connection.disconnect(uid);
         commit('SET_DISCONNECTED', uid);
         commit('SELECT_TAB', { uid, tab: 0 });
      },
      addWorkspace ({ commit }, uid) {
         const workspace = {
            uid,
            connectionStatus: 'disconnected',
            selectedTab: 0,
            searchTerm: '',
            tabs: [],
            structure: {},
            variables: [],
            collations: [],
            users: [],
            breadcrumbs: {},
            loadingElements: [],
            loadedSchemas: new Set()
         };

         commit('ADD_WORKSPACE', workspace);
      },
      changeBreadcrumbs ({ commit, getters }, payload) {
         const breadcrumbsObj = {
            schema: null,
            table: null,
            trigger: null,
            triggerFunction: null,
            procedure: null,
            function: null,
            scheduler: null,
            view: null,
            query: null
         };

         commit('CHANGE_BREADCRUMBS', { uid: getters.getSelected, breadcrumbs: { ...breadcrumbsObj, ...payload } });
      },
      addLoadedSchema ({ commit, getters }, schema) {
         commit('ADD_LOADED_SCHEMA', { uid: getters.getSelected, schema });
      },
      addLoadingElement ({ commit, getters }, element) {
         commit('ADD_LOADING_ELEMENT', { uid: getters.getSelected, element });
      },
      removeLoadingElement ({ commit, getters }, element) {
         commit('REMOVE_LOADING_ELEMENT', { uid: getters.getSelected, element });
      },
      setSearchTerm ({ commit, getters }, term) {
         commit('SET_SEARCH_TERM', { uid: getters.getSelected, term });
      },
      newTab ({ state, commit }, { uid, content, type, autorun, schema, elementName, elementType }) {
         let tabUid;
         const workspaceTabs = state.workspaces.find(workspace => workspace.uid === uid);

         switch (type) {
            case 'new-table':
            case 'new-trigger':
            case 'new-trigger-function':
            case 'new-function':
            case 'new-routine':
            case 'new-scheduler':
               tabUid = uidGen('T');
               commit('NEW_TAB', {
                  uid,
                  tab: tabUid,
                  content,
                  type,
                  autorun,
                  schema,
                  elementName,
                  elementType
               });
               break;
            case 'temp-data':
            case 'temp-trigger-props':
            case 'temp-trigger-function-props':
            case 'temp-function-props':
            case 'temp-routine-props':
            case 'temp-scheduler-props': {
               const existentTab = workspaceTabs
                  ? workspaceTabs.tabs.find(tab =>
                     tab.schema === schema &&
                     tab.elementName === elementName &&
                     tab.elementType === elementType &&
                     [type, type.replace('temp-', '')].includes(tab.type))
                  : false;

               if (existentTab) { // if tab exists
                  tabUid = existentTab.uid;
               }
               else {
                  const tempTabs = workspaceTabs ? workspaceTabs.tabs.filter(tab => tab.type.includes('temp-')) : false;

                  if (tempTabs && tempTabs.length) { // if temp tab already opened
                     for (const tab of tempTabs) {
                        if (tab.isChanged) {
                           commit('REPLACE_TAB', { // make permanent a temp table with unsaved changes
                              uid,
                              tab: tab.uid,
                              type: tab.type.replace('temp-', ''),
                              schema: tab.schema,
                              elementName: tab.elementName,
                              elementType: tab.elementType
                           });

                           tabUid = uidGen('T');
                           commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
                        }
                        else {
                           commit('REPLACE_TAB', { uid, tab: tab.uid, type, schema, elementName, elementType });
                           tabUid = tab.uid;
                        }
                     }
                  }
                  else {
                     tabUid = uidGen('T');
                     commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
                  }
               }
            }
               break;
            case 'data':
            case 'table-props':
            case 'trigger-props':
            case 'trigger-function-props':
            case 'function-props':
            case 'routine-props':
            case 'scheduler-props': {
               const existentTab = workspaceTabs
                  ? workspaceTabs.tabs.find(tab =>
                     tab.schema === schema &&
                     tab.elementName === elementName &&
                     tab.elementType === elementType &&
                     [`temp-${type}`, type].includes(tab.type))
                  : false;

               if (existentTab) {
                  commit('REPLACE_TAB', { uid, tab: existentTab.uid, type, schema, elementName, elementType });
                  tabUid = existentTab.uid;
               }
               else {
                  tabUid = uidGen('T');
                  commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
               }
            }
               break;
            default:
               tabUid = uidGen('T');
               commit('NEW_TAB', { uid, tab: tabUid, content, type, autorun, schema, elementName, elementType });
               break;
         }

         commit('SELECT_TAB', { uid, tab: tabUid });
      },
      checkSelectedTabExists ({ state, commit }, uid) {
         const workspace = state.workspaces.find(workspace => workspace.uid === uid);
         const isSelectedExistent = workspace
            ? workspace.tabs.some(tab => tab.uid === workspace.selectedTab)
            : false;

         if (!isSelectedExistent && workspace.tabs.length)
            commit('SELECT_TAB', { uid, tab: workspace.tabs[workspace.tabs.length - 1].uid });
      },
      updateTabContent ({ commit }, { uid, tab, type, schema, content }) {
         commit('REPLACE_TAB', { uid, tab, type, schema, content });
      },
      renameTabs ({ commit }, payload) {
         commit('RENAME_TABS', payload);
      },
      removeTab ({ commit, dispatch }, payload) {
         commit('REMOVE_TAB', payload);
         dispatch('checkSelectedTabExists', payload.uid);
      },
      removeTabs ({ commit, dispatch }, payload) {
         commit('REMOVE_TABS', payload);
         dispatch('checkSelectedTabExists', payload.uid);
      },
      selectTab ({ commit }, payload) {
         commit('SELECT_TAB', payload);
      },
      updateTabs ({ commit }, payload) {
         commit('UPDATE_TABS', payload);
      },
      setTabFields ({ commit }, payload) {
         commit('SET_TAB_FIELDS', payload);
      },
      setTabKeyUsage ({ commit }, payload) {
         commit('SET_TAB_KEY_USAGE', payload);
      },
      setUnsavedChanges ({ commit }, payload) {
         commit('SET_UNSAVED_CHANGES', payload);
      }
   }
};
