import { Module, MutationTree, ActionTree, GetterTree } from 'vuex';
import { LoginData, LoginResponse } from 'src/plugins/user/models';
import { StateInterface } from 'src/store';
import { axios } from 'src/boot/axios';
import { AxiosError, AxiosResponse } from 'axios';
import { Router } from 'src/router';
import { LocalStorage } from 'quasar';

export interface SessionInterface {
  currentSession?: FG.Session;
  sessions: FG.Session[];
  loading: boolean;
  backendOffline: boolean;
}

/**
 * Load current session from LocalStorage
 * Used when we were already authenticated using this browser
 */
function loadCurrentSession() {
  const session = LocalStorage.getItem<FG.Session>('currentSession');
  if (session) session.expires = new Date(session.expires);
  return session;
}

const state: SessionInterface = {
  sessions: [],
  currentSession: loadCurrentSession() || undefined,
  loading: false,
  backendOffline: false
};

const mutations: MutationTree<SessionInterface> = {
  setCurrentSession(state, session: FG.Session) {
    LocalStorage.set('currentSession', session);
    state.currentSession = session;
  },
  clearCurrentSession(state) {
    LocalStorage.remove('currentSession');
    state.currentSession = undefined;
  },
  setSessions(state, sessions: FG.Session[]) {
    state.sessions = sessions;
  },
  setLoading(state, value: boolean) {
    state.loading = value;
  },
  setOffline(state, value: boolean) {
    state.backendOffline = value;
  }
};

const actions: ActionTree<SessionInterface, StateInterface> = {
  /** Used to authenticate the user
   * Setting current Session, User and Permissions.
   * @param param0 Context
   * @param data Credentitals
   */
  login({ commit }, data: LoginData) {
    return axios
      .post('/auth', data)
      .then((response: AxiosResponse<LoginResponse>) => {
        response.data.session.expires = new Date(response.data.session.expires);
        commit('setCurrentSession', response.data.session);
        commit('user/setCurrentUser', response.data.user, { root: true });
        commit('user/setCurrentPermissions', response.data.permissions, {
          root: true
        });
      });
  },
  /**
   * Logout from current session
   * Alias of deleteSession with current session as target
   */
  logout({ dispatch, rootState }) {
    if (rootState.session.currentSession) {
      dispatch('deleteSession', rootState.session.currentSession.token).catch(
        error => {
          console.log(error);
          void dispatch('clearCurrent', false);
        }
      );
    } else {
      void dispatch('clearCurrent', false);
    }
  },
  /**
   * Delete a given session
   */
  deleteSession({ commit, dispatch, rootState }, token: string) {
    commit('setLoading', true);
    axios
      .delete(`/auth/${token}`)
      .then(() => {
        if (token === rootState.session.currentSession?.token) {
          void dispatch('clearCurrent', false);
        } else {
          dispatch('getSessions').catch(error => {
            throw error;
          });
        }
      })
      .catch((error: AxiosError) => {
        if (!error.response || error.response.status != 401) throw error;
      })
      .finally(() => {
        commit('setLoading', false);
      });
  },
  /**
   * Clear current session and logged in user
   */
  clearCurrent({ commit }, redirect = true) {
    void Router.push({
      name: 'login',
      query: redirect ? { redirect: Router.currentRoute.fullPath } : {},
      params: { logout: 'true' }
    }).then(() => {
      commit('clearCurrentSession');
      commit('user/clearCurrentUser', null, { root: true });
    });
  },
  /**
   * Get all sessions from current User
   */
  getSessions({ commit, state }) {
    commit('setLoading', true);
    axios
      .get('/auth')
      .then((response: AxiosResponse<FG.Session[]>) => {
        response.data.forEach(session => {
          session.expires = new Date(session.expires);
        });
        commit('setSessions', response.data);
        const currentSession = response.data.find((session: FG.Session) => {
          return session.token === state.currentSession?.token;
        });
        if (currentSession) {
          commit('setCurrentSession', currentSession);
        }
      })
      .catch(error => {
        throw error;
      })
      .finally(() => {
        commit('setLoading', false);
      });
  }
};

const getters: GetterTree<SessionInterface, StateInterface> = {
  currentSession(state) {
    return state.currentSession;
  },
  sessions(state) {
    return state.sessions;
  },
  loading(state) {
    return state.loading;
  }
};

const sessions: Module<SessionInterface, StateInterface> = {
  namespaced: true,
  state,
  mutations,
  actions,
  getters
};

export default sessions;