import Vue from 'vue';
import Vuex from 'vuex';
import VueRouter from 'vue-router';
import Axios from 'axios';
import JwtDecode from 'jwt-decode';

import App from './app.vue';
import Views from './views/views.js';

Vue.use(VueRouter);
Vue.use(Vuex);

const router = new VueRouter();

const store = new Vuex.Store({
  state: {
    api: {},
    navigation: [],
    persist: {
      login: {},
      authToken: '',
      jwt: {},
      credentials: {}
    }
  },
  mutations: {
    setAPI(state, payload) {
      state.api = payload;
    },
    setNavigation(state, payload) {
      for (var i = 0; i < payload.length; i++) {
        state.navigation.push(payload[i]); // = payload;
      }
    },
    setCredentials(state, payload) {
      state.persist.credentials = {
        username: payload.username,
        password: payload.password
      };
      objectToPersist(state.persist, 'persistantStore');
    },
    setLogin(state, payload) {
      state.persist.login = payload.User;
      state.persist.authToken = payload.AuthToken;
      state.persist.jwt = JwtDecode(payload.AuthToken);
      objectToPersist(state.persist, 'persistantStore');
    },
    clearLogin(state) {
      state.persist.login = {};
      state.persist.authToken = '';
      state.persist.jwt = {};
      state.persist.credentials = {};
      objectToPersist(state.persist, 'persistantStore');
    }
  },
  actions: {
    apiRequest(context, payload) {
      let doRequest = () => {
        return new Promise((resolve, reject) => {
          Axios({
            method: payload.method ? payload.method : 'get',
            baseURL: context.state.api.baseURL,
            url: payload.endpoint,
            params: payload.params,
            data: payload.data,
            headers: {
              'X-Auth-Token': context.state.persist.authToken
            }
          })
            .then(response => {
              console.log(response);
              resolve(response.data);
            })
            .catch(error => {
              console.dir(error);
              reject(error);
            })
        });
      };

      if (payload.endpoint != 'login') {
        // no jwt check for login call

        // empty username = not logged in
        if (!context.state.persist.credentials.username) {
          return new Promise((resolve, reject) => {
            // show login page
            router.push('/login');
            reject(['not logged in']);
          });
        }

        let now = Math.round(Date.now() / 1000);
        if (context.state.persist.jwt.exp < (now + 300)) {
          // too old jwt, logout
          return new Promise((resolve, reject) => {
            context.commit('clearLogin');
            router.push('/login');
            reject(['jwt too old, logout']);
          });
        }

        if ((context.state.persist.jwt.exp - 60) < now) {
          // jwt near expire, get new one
          console.log("getting new jwt");

          return context.dispatch('login', context.state.persist.credentials)
            .then(() => {
              console.log("LOOOGIIIINNN");
              return doRequest();
            })
            .catch(error => {
              throw error;
            })
        }
      }

      return doRequest();
    },
    login(context, payload) {
      return new Promise((resolve, reject) => {
        context.dispatch('apiRequest', {
          method: 'post',
          endpoint: 'login',
          data: payload
        })
          .then(data => {
            context.commit('setCredentials', payload);
            context.commit('setLogin', data);
            resolve(data.User);
          })
          .catch(error => {
            if (error.response && error.response.data && error.response.data.error) {
              reject(error.response.data.error);
            } else {
              reject([]);
            }
          });
      });
    }
  }
});

function objectToPersist(obj, key) {
  try {
    const serialized = JSON.stringify(obj);
    localStorage.setItem(key ? key : 'persistant', serialized);
  } catch (error) {
    console.error(error);
  }
};

function persistToObject(key) {
  try {
    const serialized = localStorage.getItem(key ? key : 'persistant');
    if (serialized === null) {
      return undefined;
    }
    return JSON.parse(serialized);
  } catch (error) {
    console.log(error);
    return undefined;
  }
}

// get store persist part from localStorage
let persist = persistToObject('persistantStore');
if (persist) {
  store.state.persist = persist;
}

// load init
Axios.get('conf/init.json')
  .then(results => {
    // set navigation
    if (!Array.isArray(results.data.navigation)) {
      alert('invalid data in init.json');
      return;
    }
    let navigation = [];
    results.data.navigation.forEach(({name, to}) => navigation.push({name, to}));

    store.commit("setNavigation", navigation);

    // set api config in store
    store.commit("setAPI", results.data.api);

    // add routes
    let routes = [];
    let rIdx = 0;
    results.data.navigation.forEach(({name, to, content, data}) => routes.push({
      name: name,
      path: to,
      meta: {
        title: name
      },
      component: Vue.component(name + rIdx++, {
        template: '<div id="' + name + rIdx + '">' + content + '</div>',
        components: Views,
        data() {
          if (typeof data != 'object') {
            return {};
          }
          return data;
        }
      })
    }));
    router.addRoutes(routes);
    router.beforeEach((to, from, next) => {
      console.log(to);
      document.title = (to.meta && to.meta.title) ? results.data.title + ': ' + to.meta.title : results.data.title;
      next();
    });

    // load app, when init finishs
    new Vue({
      el: '#vue-app',
      render: h => h(App),
      router,
      store
    });
  })
  .catch(error => {
    alert('error loading: ' + error.message);
  });