import Vue from 'vue';
import Vuex from 'vuex';
import { createLogger } from 'vuex';
import createPersistedState from 'vuex-persistedstate';
import getRepository from '@/api/RepositoryFactory';
import RoomRepository from '@/api/roomRepository';
import RecordRepository from '@/api/recorderRepository';
import VirtualCommandRepository from '@/api/virtualCommandRepository';
import StateRepository from '@/api/stateRepository';

// imports of AJAX functions will go here
import {
  fetchSurveys,
  fetchSurvey,
  getProviders,
  saveSurveyResponse,
  postNewSurvey,
  authenticate,
  register,
  oidc_login, fetchUsers, getFreshToken, fetchProfile, fetchUserGroups, revokeRefreshKey,
} from '@/api';
import {isValidJwt, EventBus} from '@/utils';

Vue.use(Vuex);

const state = {
  // single source of data
  surveys: [],
  rooms: [],
  recorders: [],
  recorderModels: [],
  recorderCommands: [],
  recorderStates: [],
  virtualCommands: [],
  loginProviders: [],
  currentSurvey: {},
  profile: {},
  users: [],
  userGroups: [],
  access_token: '',
  refresh_token: '',
};


// const RoomRepository = getRepository('room');


const actions = {
  // asynchronous operations
  loadRooms(context: any) {
    return RoomRepository.getRooms()
      .then((response: any) => {
        context.commit('setRooms', {rooms: response.data});
        EventBus.$emit('roomsLoaded', response.data);
      })
      .catch((error: any) => {
        EventBus.$emit('failedLoadingRooms', error);
      });
  },
  loadRecorders(context: any) {
    return RecordRepository.getRecorders()
      .then((response: any) => {
        context.commit('setRecorders', {recorders: response.data});
        EventBus.$emit('recordersLoaded', response.data);
      })
      .catch((error: any) => {
        EventBus.$emit('failedLoadingRecorders', error);
      });
  },
  loadRecorderStates(context: any) {
    return StateRepository.getRecordersStates()
        .then((response: any) => {
          context.commit('setRecorderStates', {recorderStates: response.data});
          EventBus.$emit('recorderStatesLoaded', response.data);
        })
        .catch((error: any) => {
          EventBus.$emit('failedLoadingRecorderStates', error);
        });
  },
  loadRecorderState(context: any, recorder_id: number) {
    return StateRepository.getRecorderState(recorder_id)
        .then((response: any) => {
          context.commit('setRecorderState', {recorderState: response.data});
          EventBus.$emit('recorderStateLoaded', response.data);
        })
        .catch((error: any) => {
          EventBus.$emit('failedLoadingRecorderStates', error);
        });
  },
  loadRecorderModels(context: any) {
    return RecordRepository.getRecorderModels()
      .then((response: any) => {
        context.commit('setRecorderModels', {recorderModels: response.data});
        EventBus.$emit('recorderModelsLoaded', response.data);
      })
      .catch((error: any) => {
        EventBus.$emit('failedLoadingRecorderModels', error);
      });
  },
  loadRecorderCommands(context: any) {
    return RecordRepository.getRecorderCommands()
        .then((response: any) => {
          context.commit('setRecorderCommands', {recorderCommands: response.data});
          EventBus.$emit('recorderCommandsLoaded', response.data);
        })
        .catch((error: any) => {
          EventBus.$emit('failedLoadingRecorderCommands', error);
        });
  },
  loadVirtualCommands(context: any) {
    return VirtualCommandRepository.getVirtualCommands()
      .then((response: any) => {
        context.commit('setVirtualCommands', {virtualCommands: response.data});
        EventBus.$emit('virtualCommandsLoaded', response.data);
      })
      .catch((error: any) => {
        EventBus.$emit('failedLoadingVirtualCommands', error);
      });
  },
  loadCommands(context: any) {
    return context.dispatch('loadRecorderCommands').then(() => {
      return context.dispatch('loadVirtualCommands');
    });
  },
  loadUsers(context: any) {
    return fetchUsers(context.state.access_token)
      .then((response) => {
        context.commit('setUsers', {users: response.data});
        EventBus.$emit('usersLoaded', response.data);
      })
      .catch((error) => {
        EventBus.$emit('failedLoadingUsers', error);
      });
  },
  loadUserGroups(context: any) {
    return fetchUserGroups(context.state.access_token)
      .then((response) => {
        context.commit('setUserGroups', {groups: response.data});
        EventBus.$emit('groupsLoaded', response.data);
      })
      .catch((error) => {
        EventBus.$emit('failedLoadingUserGroups', error);
      });
  },
  loadProfileAuthCheck(context: any) {
    if (!getters.isAuthenticated) {
      EventBus.$emit('accessTokenInvalid');
      if (!getters.isRefreshTokenValid) {
        EventBus.$emit('refreshTokenInvalid');
        EventBus.$emit('accessAndRefreshTokenInvalid');
      } else {
        return context.dispatch('refreshToken').then(() => {
          context.commit('loadProfile');
        });
      }
    } else {
      context.commit('loadProfile');
    }
  },
  loadProfile(context: any) {
    return fetchProfile(context.state.access_token)
      .then((response) => {
        context.commit('setProfile', {profile: response.data});
        EventBus.$emit('profileLoaded', response.data);
      })
      .catch((error) => {
        EventBus.$emit('failedLoadingProfile', error);
      });
  },
  loadLoginProviders(context: any) {
    return getProviders()
      .then((response) => {
        context.commit('setLoginProviderData', {providers: response.data});
        EventBus.$emit('loginProvidersLoaded', response.data);
      });
  },
  login(context: any, userData: any) {
    context.commit('setUserData', {userData});
    return authenticate(userData)
      .then((response) => context.commit('setJwtToken', {tokens: response.data}))
      .catch((error) => {
        EventBus.$emit('failedAuthentication', error);
      });
  },
  logout(context: any, revokeRefreshToken: any) {
    context.commit('setTokens', {tokens: {access_token: '', refresh_token: ''}});
    if (revokeRefreshToken) {
      return revokeRefreshKey(context.state.access_token)
          .catch((error) => {
            EventBus.$emit('failedRevokingRefreshToken', error);
          });
    }
    // this.clearAll();
  },
  oidc_login(context: any, redirectionUrl: any) {
    // context.commit('setUserData', { userData });
    return oidc_login(redirectionUrl)
      .then((response) => context.commit('setJwtToken', {tokens: response.data}))
      .catch((error) => {
        EventBus.$emit('failedAuthentication', error);
      });
  },
  refreshToken(context: any) {
    EventBus.$emit('refreshingToken');
    return getFreshToken(context.state.refresh_token)
      .then((response) => {
        context.commit('setTokens', {tokens: response.data});
      })
      .catch((error) => {
        EventBus.$emit('failedRefreshingToken', error);
      });
  },
  resetToken(context: any) {
    EventBus.$emit('resettingTokens');
    context.commit('setTokens', {tokens: {access_token: '', refresh_token: ''}});
    EventBus.$emit('tokensReset');
  },
  storeTokens(context: any, tokens: any) {
    context.commit('setTokens', {tokens});
    EventBus.$emit('storedTokens');
  },
  register(context: any, userData: any) {
    context.commit('setUserData', {userData});
    return register(userData)
      .then(context.dispatch('login', userData))
      .catch((error) => {
        EventBus.$emit('failedRegistering: ', error);
      });
  },
  submitNewSurvey(context: any, survey: any) {
    return postNewSurvey(survey, context.state.access_token);
  },
};

const mutations = {
  // isolated data mutations
  setSurveys(sState: any, payload: any) {
    sState.surveys = payload.surveys;
  },
  setSurvey(sState: any, payload: any) {
    const nQuestions = payload.survey.questions.length;
    for (let i = 0; i < nQuestions; i++) {
      payload.survey.questions[i].choice = null;
    }
    sState.currentSurvey = payload.survey;
  },
  setRooms(sState: any, payload: any) {
    sState.rooms = payload.rooms;
  },
  setRecorders(sState: any, payload: any) {
    sState.recorders = payload.recorders;
  },
  setRecorderModels(sState: any, payload: any) {
    sState.recorderModels = payload.recorderModels;
  },
  setRecorderCommands(sState: any, payload: any) {
    sState.recorderCommands = payload.recorderCommands;
  },
  setRecorderStates(sState: any, payload: any) {
    sState.recorderStates = payload.recorderStates;
  },
  setRecorderState(sState: any, payload: any) {
    const n_rec_states = sState.recorderStates.length;
    const rec_id = payload.recorderState.id;
    let found = false;
    for (let i = 0; i < n_rec_states; i++) {
      if (sState.recorderStates[i].id === rec_id) {
        sState.recorderStates[i] = payload.recorderState;
        found = true;
        break;
      }
    }
    if(!found){
      sState.recorderStates.push(payload.recorderState);
    }
  },
  setVirtualCommands(sState: any, payload: any) {
    sState.virtualCommands = payload.virtualCommands;
  },
  setUsers(sState: any, payload: any) {
    sState.users = payload.users;
  },
  setUserGroups(sState: any, payload: any) {
    sState.userGroups = payload.groups;
  },
  setChoice(sState: any, payload: any) {
    const {questionId, choice} = payload;
    const nQuestions = sState.currentSurvey.questions.length;
    for (let i = 0; i < nQuestions; i++) {
      if (sState.currentSurvey.questions[i].id === questionId) {
        sState.currentSurvey.questions[i].choice = choice;
        break;
      }
    }
  },
  setLoginProviderData(sState: any, payload: any) {
    sState.loginProviders = payload.providers;
  },
  // probably old ...
  setUserData(sState: any, payload: any) {
    sState.userData = payload.userData;
  },
  setProfile(sState: any, payload: any) {
    sState.profile = payload.profile;
  },
  setJwtToken(sState: any, payload: any) {
    localStorage.tokens = payload.tokens;
    sState.access_token = payload.tokens.access_token;
    sState.refresh_token = payload.tokens.refresh_token;
  },
  setTokens(sState: any, payload: any) {
    if ('access_token' in payload.tokens) {
      sState.access_token = payload.tokens.access_token;
    }
    if ('refresh_token' in payload.tokens) {
      sState.refresh_token = payload.tokens.refresh_token;
    }
  },
};

const getters = {
  // reusable data accessors
  isAuthenticated(sState: any) {
    const valid = isValidJwt(sState.access_token);
    return valid;
  },
  isRefreshTokenValid(sState: any) {
    const valid = isValidJwt(sState.refresh_token);
    return valid;
  },
  getLoginProviders(sState: any) {
    return sState.loginProviders;
  },
  getUserName(sState: any) {
    if (sState.profile == null || Object.keys(sState.profile).length === 0) {
      return '';
    }
    if (sState.profile.nickname) {
      return sState.profile.nickname;
    }
    return sState.profile.first_name + ' ' + sState.profile.last_name;
  },
  // this is probably wrong!!
  hasAccessRight(sState: any) {
    (requestedPermission: string) => {
      return sState.profile.effective_permissions.find((permission: any) => {
        permission.name === requestedPermission;
      });
    };
  },
};

const store = new Vuex.Store({
  state,
  actions,
  mutations,
  getters,
  plugins: [createPersistedState(), createLogger()],
});

export default store;
