import Vue from 'vue'
import Vuex from 'vuex'
import VuexPersistence from 'vuex-persist'
import IUser from '@/models/IUser'
import userService from '@/services/UserService'
import couchService from '@/services/CouchService'
import IResponse from '@/models/IResponse'
import IUserPassword from '@/models/IUserPassword'
import IIdPassword from '@/models/IEmailPassword'
import notif from '@/utils/notif'
import { hasGoodNetwork } from '@/utils/network'
import { toHex } from './utils'

Vue.use(Vuex)

const SET_LOCALE: string = 'SET_LOCALE'
const SET_TITLE: string = 'SET_TITLE'
const SET_USER: string = 'SET_USER'
const ADD_PUBLIC_ACCOUNT: string = 'ADD_PUBLIC_ACCOUNT'
const REMOVE_PUBLIC_ACCOUNT: string = 'REMOVE_PUBLIC_ACCOUNT'

interface IState {
  locale: string
  user: IUser | null
  hexUser: string | null
  title: string | null
  publicAccountIds: string[]
}

export default new Vuex.Store<IState>({
  state: {
    locale: '',
    user: null,
    hexUser: null,
    title: null,
    publicAccountIds: []
  },
  getters: {
    locale: ({ locale }) => locale,
    user: ({ user }) => user,
    title: ({ title }) => title,
    username: (state: IState) =>
      state.user
        ? state.user.firstname && state.user.lastname
          ? `${state.user.firstname} ${state.user.lastname}`.trim()
          : state.user.email
        : '',
    isAppAdmin: (state: IState) =>
      state.user &&
      state.user.roles &&
      state.user.roles.find((role: string) => role === 'vaquant-admin'),
    publicAccountIds: ({ publicAccountIds }) => publicAccountIds
  },
  mutations: {
    [SET_LOCALE](store, locale: string) {
      store.locale = locale
    },
    [SET_TITLE](store, title: string | null) {
      store.title = title
    },
    [SET_USER](
      store,
      { user, replicate }: { user: IUser; replicate?: boolean }
    ) {
      store.user = user
      store.hexUser = user ? toHex(user.userId) : null
      couchService.setUser(user, store.hexUser, replicate || false)
    },
    [ADD_PUBLIC_ACCOUNT](store, { accountId }: { accountId: string }) {
      if (!accountId) {
        return
      }
      store.publicAccountIds = [
        ...new Set([...store.publicAccountIds, accountId])
      ]
    },
    [REMOVE_PUBLIC_ACCOUNT](store, { accountId }: { accountId: string }) {
      if (!accountId) {
        return
      }
      store.publicAccountIds = store.publicAccountIds.filter(
        (id: string) => id !== accountId && id !== null
      )
    }
  },
  actions: {
    setLocale({ commit }, locale: string) {
      commit(SET_LOCALE, locale)
    },
    setTitle({ commit }, title: string) {
      commit(SET_TITLE, title)
    },
    async retrieveUser({ commit, state }) {
      let user = state.user
      if (navigator.onLine && hasGoodNetwork()) {
        user = await userService.getUser(user)
      }
      commit(SET_USER, { user, replicate: false })
    },
    async signup({ commit }, { user, password, replicate }: IUserPassword) {
      const response: IResponse = await userService.signup(user, password)
      if (response.ok) {
        commit(SET_USER, { user, replicate })
      } else {
        notif.error(response.message || '')
      }
    },
    async login({ commit, state }, { userId, password }: IIdPassword) {
      const response: IResponse = await userService.login(userId, password)
      if (response.ok) {
        const user: IUser | null = await userService.getUser(state.user)
        commit(SET_USER, { user })
      } else {
        notif.error(response.message || '')
      }
    },
    async logout({ commit }) {
      const response = await userService.logout()
      if (response.ok) {
        commit(SET_USER, { user: null })
      } else {
        notif.error(response.message || '')
      }
    },
    async remove({ commit, state }) {
      if (state.user) {
        const response = await userService.deleteUser(state.user.userId)
        if (response.ok) {
          commit(SET_USER, { user: null })
        } else {
          notif.error(response.message || '')
        }
      }
    },
    addAccountId({ commit }, { accountId }: { accountId: string }) {
      commit(ADD_PUBLIC_ACCOUNT, { accountId })
    },
    removeAccountId({ commit }, { accountId }: { accountId: string }) {
      commit(REMOVE_PUBLIC_ACCOUNT, { accountId })
    }
  },
  plugins: [
    new VuexPersistence<any>({
      storage: window.localStorage
    }).plugin
  ]
})
