import '@babel/polyfill';
import Vue from 'vue';
import VueVimeoPlayer from 'vue-vimeo-player';
import apolloClientFactory from './graphql/client';
import VueApollo from 'vue-apollo';
import App from './App';
import {router, postLoginRedirectUrlKey} from './router';
import store from '@/store/index';
import VueScrollTo from 'vue-scrollto';
import {Validator, install as VeeValidate} from 'vee-validate/dist/vee-validate.minimal.esm.js';
import {required, min, decimal, confirmed} from 'vee-validate/dist/rules.esm.js';
import veeDe from 'vee-validate/dist/locale/de';
import {dateFilter, dateTimeFilter} from './filters/date-filter';
import autoGrow from '@/directives/auto-grow';
import clickOutside from '@/directives/click-outside';
import ME_QUERY from '@/graphql/gql/queries/meQuery.gql';
import VueModal from '@/plugins/modal';
import VueRemoveEdges from '@/plugins/edges';
import VueMatomo from 'vue-matomo';
import VueToast from 'vue-toast-notification';
import VueLogger from 'vuejs-logger';

Vue.config.productionTip = false;
const isProduction = process.env.NODE_ENV === 'production';

Vue.use(VueModal);
Vue.use(VueRemoveEdges);
Vue.use(VueApollo);
Vue.use(VueVimeoPlayer);
Vue.use(VueToast);
Vue.use(VueLogger, {
  isEnabled: true,
  logLevel: isProduction ? 'error' : 'debug',
  stringifyArguments: false,
  showConsoleColors: true
});

Vue.use(VueScrollTo, {
  duration: 500,
  easing: 'ease-out',
  offset: -50
});

if (process.env.MATOMO_HOST) {
  Vue.use(VueMatomo, {
    host: process.env.MATOMO_HOST,
    siteId: process.env.MATOMO_SITE_ID,
    router: router
  });
}

Vue.directive('click-outside', clickOutside);
Vue.directive('auto-grow', autoGrow);

const publicApolloClient = apolloClientFactory('/api/graphql-public/', null);
const privateApolloClient = apolloClientFactory('/api/graphql/', networkErrorCallback);

const apolloProvider = new VueApollo({
  clients: {
    publicClient: publicApolloClient
  },
  defaultClient: privateApolloClient
});

Validator.extend('required', required);
Validator.extend('min', min);
Validator.extend('decimal', decimal);
Validator.extend('confirmed', confirmed);

const dict = {
  custom: {
    oldPassword: {
      required: 'Dein aktuelles Passwort fehlt'
    },
    newPassword: {
      required: 'Dein neues Passwort fehlt',
      min: 'Das neue Passwort muss mindestens 8 Zeichen lang sein'
    }
  }
};

Validator.localize('de', veeDe);
Validator.localize('de', dict);

// https://github.com/baianat/vee-validate/issues/51
Validator.extend('strongPassword', {
  getMessage: field => 'Das Passwort muss Grossbuchstaben, Zahlen und Sonderzeichen beinhalten und mindestens 8 Zeichen lang sein',
  validate: value => {
    const strongRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!@#$%^&*?(),.":{}|<>+])(?=.{8,})/;
    return strongRegex.test(value);
  }
});

Validator.extend('email', {
  getMessage: field => 'Bitte geben Sie eine gülitge E-Mail an',
  validate: value => {
    const emailRegex = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+$/;
    return emailRegex.test(value);
  }
});

Vue.use(VeeValidate, {
  locale: 'de'
});

Vue.filter('date', dateFilter);
Vue.filter('datetime', dateTimeFilter);

/* guards */

function getCookieValue(cookieName) {
  // https://stackoverflow.com/questions/5639346/what-is-the-shortest-function-for-reading-a-cookie-by-name-in-javascript
  let cookieValue = document.cookie.match('(^|[^;]+)\\s*' + cookieName + '\\s*=\\s*([^;]+)');
  return cookieValue ? cookieValue.pop() : '';
}

function loginRequired(to) {
  // public pages have the meta.public property set to true
  return !to.hasOwnProperty('meta') || !to.meta.hasOwnProperty('public') || !to.meta.public;
}

function unauthorizedAccess(to) {
  return loginRequired(to) && getCookieValue('loginStatus') !== 'true';
}

function redirectUsersWithoutValidLicense(to) {
  return privateApolloClient.query({
    query: ME_QUERY,
  }).then(({data}) => data.me.expiryDate == null);
}

function redirectStudentsWithoutClass() {
  return privateApolloClient.query({
    query: ME_QUERY,
  }).then(({data}) => data.me.schoolClasses.edges.length === 0 && !data.me.isTeacher);
}

function redirectUsersToOnboarding(to) {
  return privateApolloClient.query({
    query: ME_QUERY,
  }).then(({data}) => !data.me.onboardingVisited);
}

function networkErrorCallback(statusCode) {
  if (statusCode === 402) {
    router.push({name: 'licenseActivation'}, 0);
  }
}

function joiningClass(to) {
  return to.name && (to.name === 'join-class' || to.name === 'licenseActivation');
}

router.beforeEach(async (to, from, next) => {
  if (to.path === '/logout') {
    publicApolloClient.resetStore();
    if (process.env.LOGOUT_REDIRECT_URL) {
      location.replace(`https://sso.hep-verlag.ch/logout?return_to=${process.env.LOGOUT_REDIRECT_URL}`);
      next(false);
    } else {
      next({name: 'hello'});
    }
  }

  if (unauthorizedAccess(to)) {
    const postLoginRedirectionUrl = to.path;
    const redirectUrl = `/hello/`;

    if (window.localStorage) {
      localStorage.setItem(postLoginRedirectUrlKey, postLoginRedirectionUrl);
    }

    Vue.$log.debug('redirecting to hello', to);
    next(redirectUrl);
    return;
  }

  if (to.name && to.name !== 'licenseActivation' && loginRequired(to) && await redirectUsersWithoutValidLicense()) {
    Vue.$log.debug('redirecting to licenseActivation', to, null);
    console.log('redirecting to licenseActivation', to, null);
    next({name: 'licenseActivation'});
    return;
  }

  if (!joiningClass(to) && loginRequired(to) && await redirectStudentsWithoutClass()) {
    Vue.$log.debug('redirecting to join-class', to);
    Vue.$log.debug('await redirectStudentsWithoutClass()', await redirectStudentsWithoutClass());
    next({name: 'join-class'});
    return;
  }

  if ((to.name && to.name.indexOf('onboarding') === -1) && !joiningClass(to) && loginRequired(to) && await redirectUsersToOnboarding()) {
    Vue.$log.debug('redirecting to onboarding-start', to);
    next({name: 'onboarding-start'});
    return;
  }

  next();
});

/* eslint-disable no-new */
new Vue({
  el: '#app',
  store,
  router,
  provide: apolloProvider.provide(),
  render: h => h(App)
});
