/**
 * Copyright (c) 2020 Bubble, Inc.  All rights reserved.
 * For personal (non-commercial) use, see license: https://getbubblenow.com/bubble-license/
 */
import { systemService } from '~/_services';
import { util } from '~/_helpers';
import { router } from '~/_router';

import { account } from './account.module';

import preAuthStaticMessages from '~/_assets/pre_auth_messages.json';
import postAuthStaticMessages from '~/_assets/post_auth_messages.json';

const state = {
  configs: {
    networkUuid: null,
    allowRegistration: false,
    paymentsEnabled: false,
    sageLauncher: false,
    bubbleNode: null,
    entityClasses: [],
    locales: ['en_US'],
    cloudConfigs: {},
    sslPort: null,
    locked: null,
    launchLock: null,
    promoCodePolicy: null,
    requireSendMetrics: null,
    awaitingRestore: false,
    restoreInProgress: false,
    support: {},
    securityLevels: null,
    jarVersion: null,
    jarUpgradeAvailable: null,
    maxUsers: null,
    showBlockStatsSupported: null,
    loaded: false,
  },
  entityConfigs: {},
  searchResults: [],
  status: {
    activating: false,
    searching: false,
    creatingEntity: false,
    modelSetupInProgress: false,
  },
  activated: null,
  error: null,
  messages: {
    durationToMillis: function(count, units) {
      if (typeof count === 'undefined' || count === null || count === '')
        return null;
      return (
        parseInt(count) *
        parseInt(state.messages['time_duration_' + units + '_factor'])
      );
    },
    millisToDuration: function(ms) {
      const durations = state.timeDurationOptionsReversed;
      for (let i = 0; i < durations.length; i++) {
        const durationMillis = parseInt(
          state.messages['time_duration_' + durations[i] + '_factor']
        );
        if (
          ms >= durationMillis &&
          (ms % durationMillis === 0 || i === durations.length - 1)
        ) {
          return { count: parseInt(ms) / durationMillis, units: durations[i] };
        }
      }
      return { count: parseInt(ms), units: '' };
    },
    ...preAuthStaticMessages,
    ...postAuthStaticMessages,
  },
  messageGroupsLoaded: [],
  countries: [],
  locales: [],
  timezones: [],
  detectedTimezone: null,
  detectedLocale: null,
  accountDeletionOptions: [],
  timeDurationOptions: [],
  timeDurationOptionsReversed: [],
  contactTypes: [],
  appLinks: null,
  upgradeCheck: null,
  upgrading: null,
};

const actions = {
  loadIsActivated({ commit }) {
    commit('loadIsActivatedRequest');
    systemService
      .loadIsActivated()
      .then(
        (activated) => commit('loadIsActivatedSuccess', activated),
        (error) => commit('loadIsActivatedFailure', error)
      );
  },
  activate({ commit }, { activation, messages, errors }) {
    commit('activateRequest');
    systemService.activate(activation, messages, errors).then(
      (admin) => {
        commit('activateSuccess', admin);
        router.replace('/');
      },
      (error) => commit('activateFailure', error)
    );
  },

  loadSystemConfigs({ commit }) {
    commit('loadSystemConfigsRequest');
    systemService
      .loadSystemConfigs()
      .then(
        (configs) => commit('loadSystemConfigsSuccess', configs),
        (error) => commit('loadSystemConfigsFailure', error)
      );
  },
  loadEntityConfigs({ commit }) {
    commit('loadEntityConfigsRequest');
    systemService
      .loadEntityConfigs()
      .then(
        (configs) => commit('loadEntityConfigsSuccess', configs),
        (error) => commit('loadEntityConfigsFailure', error)
      );
  },
  search({ commit }, type, query) {
    commit('searchRequest');
    systemService
      .search(type, query)
      .then(
        (results) => commit('searchSuccess', { type, query, results }),
        (error) => commit('searchFailure', error)
      );
  },
  createEntity({ commit }, { entityConfig, json, messages, errors }) {
    commit('createEntityRequest');
    systemService
      .createEntity(entityConfig, json, messages, errors)
      .then(
        (entity) => commit('createEntitySuccess', entity),
        (error) => commit('createEntityFailure', error)
      );
  },
  modelSetup({ commit }, { file, messages, errors }) {
    commit('modelSetupRequest');
    systemService
      .modelSetup(file, messages, errors)
      .then(
        (ok) => commit('modelSetupSuccess'),
        (errors) => commit('modelSetupFailure', errors)
      );
  },
  loadMessages({ commit }, group, locale) {
    commit('loadMessagesRequest');
    systemService
      .loadMessages(group, locale)
      .then(
        (messages) => commit('loadMessagesSuccess', { group, messages }),
        (error) => commit('loadMessagesFailure', error)
      );
  },
  loadTimezones({ commit }) {
    commit('loadTimezonesRequest');
    systemService
      .loadTimezones()
      .then(
        (timezones) => commit('loadTimezonesSuccess', timezones),
        (error) => commit('loadTimezonesFailure', error)
      );
  },
  detectTimezone({ commit }) {
    commit('detectTimezoneRequest');
    systemService
      .detectTimezone()
      .then(
        (timezone) => commit('detectTimezoneSuccess', timezone),
        (error) => commit('detectTimezoneFailure', error)
      );
  },
  detectLocale({ commit }) {
    commit('detectLocaleRequest');
    systemService
      .detectLocale()
      .then(
        (locales) => commit('detectLocaleSuccess', locales),
        (error) => commit('detectLocaleFailure', error)
      );
  },
  getAppLinks({ commit }, locale) {
    commit('getAppLinksRequest');
    systemService
      .getAppLinks(locale)
      .then(
        (links) => commit('getAppLinksSuccess', links),
        (error) => commit('getAppLinksFailure', error)
      );
  },
  checkForUpgrade({ commit }) {
    if (state.upgradeCheck === null) {
      commit('checkForUpgradeRequest');
      systemService
        .checkForUpgrade()
        .then(
          (configs) => commit('checkForUpgradeSuccess', configs),
          (error) => commit('checkForUpgradeFailure', error)
        );
    } else {
      console.log(
        'checkForUpgrade: already checked (' +
          state.upgradeCheck +
          '), not checking again'
      );
    }
  },
  upgrade({ commit }) {
    commit('upgradeRequest');
    systemService
      .upgrade()
      .then(
        (configs) => commit('upgradeSuccess', configs),
        (error) => commit('upgradeFailure', error)
      );
  },
  upgradeComplete({ commit }) {
    commit('upgradeCompleteRequest');
    commit('upgradeCompleteSuccess');
  },
};

const getters = {
  dashboardApps: function() {
    const configs = state.configs;
    const messages = state.messages;
    const isAdmin =
      account.state.user !== null && account.state.user.admin === true;
    const dashApps = [];
    dashApps.push({
      href: '/me',
      title: messages.label_menu_account,
      icon: messages.label_menu_account_icon,
      index: 0,
    });
    if (configs.sageLauncher) {
      dashApps.push({
        href: '/bubble',
        title: messages.label_menu_networks,
        icon: messages.label_menu_networks_icon,
        index: 1,
      });
    } else {
      if (isAdmin) {
        dashApps.push({
          href: '/bubble/' + configs.networkUuid,
          title: messages.label_menu_network,
          icon: messages.label_menu_networks_icon,
          index: 1,
        });
      }
      dashApps.push({
        href: '/devices',
        title: messages.label_menu_devices,
        icon: messages.label_menu_devices_icon,
        index: 2,
      });
      dashApps.push({
        href: '/apps',
        title: messages.label_menu_apps,
        icon: messages.label_menu_apps_icon,
        index: 3,
      });
    }
    if (isAdmin && configs.sageLauncher) {
      const adminApps = [
        {
          href: '/',
          title: messages.label_menu_dashboard,
          icon: messages.label_menu_dashboard_icon,
          index: 0,
        },
      ];
      adminApps.push({
        href: '/admin/accounts',
        title: messages.label_menu_admin_users,
        icon: messages.label_menu_admin_users_icon,
        index: 1,
      });
      adminApps.push({
        href: '/admin/model',
        title: messages.label_menu_admin_model,
        icon: messages.label_menu_admin_model_icon,
        index: 2,
      });
      adminApps.push({
        href: '/admin/bubble',
        title: messages.label_menu_admin_networks,
        icon: messages.label_menu_admin_networks_icon,
        index: 3,
      });
      if (configs.paymentsEnabled) {
        adminApps.push({
          href: '/admin/bills',
          title: messages.label_menu_admin_bills,
          icon: messages.label_menu_admin_bills_icon,
          index: 4,
        });
      }
      dashApps.push({
        href: '/?app=admin',
        title: messages.label_menu_admin,
        icon: messages.label_menu_admin_icon,
        index: 4,
        apps: adminApps,
      });
    } else if (isAdmin && configs.maxUsers > 1) {
      dashApps.push({
        href: '/admin/accounts',
        title: messages.label_menu_admin_users,
        icon: messages.label_menu_admin_users_icon,
        index: 4,
      });
    }
    dashApps.push({
      href: '/logout',
      title: messages.label_menu_logout,
      icon: messages.label_menu_logout_icon,
      index: 1000,
    });
    return dashApps;
  },
  promoCodesEnabled: function() {
    return (
      state.promoCodePolicy === 'required' ||
      state.promoCodePolicy === 'optional'
    );
  },
  promoCodeRequired: function() {
    return state.promoCodePolicy === 'required';
  },
  configs: function() {
    return state.configs.loaded === true ? state.configs : {};
  },
};

const mutations = {
  loadIsActivatedRequest(state) {},
  loadIsActivatedSuccess(state, activated) {
    state.activated = activated;
  },
  loadIsActivatedFailure(state, error) {
    state.error = error;
  },

  activateRequest(state) {
    state.status.activating = true;
  },
  activateSuccess(state, admin) {
    state.status.activating = false;
    state.activated = true;
    state.user = admin;
    state.status = { loggedIn: admin !== null };
    localStorage.setItem(util.USER_KEY, JSON.stringify(admin));
  },
  activateFailure(state, error) {
    state.status.activating = false;
    state.error = error;
  },

  loadSystemConfigsRequest(state) {},
  loadSystemConfigsSuccess(state, configs) {
    state.configs = { ...configs, loaded: true };
  },
  loadSystemConfigsFailure(state, error) {
    state.error = error;
  },
  loadEntityConfigsRequest(state) {},
  loadEntityConfigsSuccess(state, configs) {
    // console.log('loadEntityConfigsSuccess: received configs='+JSON.stringify(configs));
    const newConfigs = {};
    for (let i = 0; i < configs.length; i++) {
      for (let j = 0; j < configs[i].names.length; j++) {
        newConfigs[configs[i].names[j]] = configs[i].entityConfig;
      }
    }
    state.entityConfigs = newConfigs;
  },
  loadEntityConfigsFailure(state, error) {
    state.error = error;
  },
  searchRequest(state) {
    state.status.searching = true;
  },
  searchSuccess(state, { type, query, results }) {
    state.status.searching = false;
    state.searchResults = results;
  },
  searchFailure(state, error) {
    state.status.searching = false;
    state.error = error;
  },
  createEntityRequest(state) {
    state.status.creatingEntity = true;
  },
  createEntitySuccess(state, { entity }) {
    state.status.creatingEntity = false;
  },
  createEntityFailure(state, error) {
    state.status.creatingEntity = false;
    state.error = error;
  },
  modelSetupRequest(state) {
    state.status.modelSetupInProgress = true;
  },
  modelSetupSuccess(state) {
    state.status.modelSetupInProgress = false;
  },
  modelSetupFailure(state, errors) {
    state.status.modelSetupInProgress = false;
    state.error = errors;
  },
  loadMessagesRequest(state) {},
  loadMessagesSuccess(state, { group, messages }) {
    if (state.messageGroupsLoaded.indexOf(group) === -1)
      state.messageGroupsLoaded.push(group);
    state.messages = util.addMessages(state.messages, messages);
    if (messages.country_codes) {
      const countries = [];
      const codes = messages.country_codes.split(',');
      for (let i = 0; i < codes.length; i++) {
        countries.push({
          code: codes[i],
          countryName: messages['country_' + codes[i]],
        });
      }
      state.countries = countries;
    }
    if (messages.locale_codes) {
      const locales = [];
      const codes = messages.locale_codes.split(',');
      for (let i = 0; i < codes.length; i++) {
        locales.push({
          localeCode: codes[i],
          localeName: messages['locale_' + codes[i]],
        });
      }
      state.locales = locales;
    }
    if (messages.field_label_policy_account_deletion_options) {
      state.accountDeletionOptions = messages.field_label_policy_account_deletion_options.split(
        ','
      );
    }
    if (messages.time_duration_options) {
      state.timeDurationOptions = messages.time_duration_options.split(',');
      state.timeDurationOptionsReversed = state.timeDurationOptions
        .slice()
        .reverse();
    }
    if (messages.field_label_policy_contact_type_options) {
      state.contactTypes = messages.field_label_policy_contact_type_options.split(
        ','
      );
    }
  },
  loadMessagesFailure(state, error) {
    state.error = error;
  },
  loadTimezonesRequest(state) {},
  loadTimezonesSuccess(state, timezones) {
    state.timezones = timezones;
  },
  loadTimezonesFailure(state, error) {
    state.error = error;
  },
  detectTimezoneRequest(state) {},
  detectTimezoneSuccess(state, detectedTimezone) {
    state.detectedTimezone = detectedTimezone;
  },
  detectTimezoneFailure(state, error) {
    state.error = error;
  },
  detectLocaleRequest(state) {},
  detectLocaleSuccess(state, detectedLocales) {
    if (detectedLocales.length) {
      for (let i = 0; i < detectedLocales.length; i++) {
        if (state.locales.indexOf(detectedLocales[i])) {
          state.detectedLocale = detectedLocales[i];
          return;
        }
      }
    }
    console.warn(
      'detectLocaleSuccess: server detected locales ' +
        JSON.stringify(detectedLocales) +
        ' but none were found in locales list: ' +
        JSON.stringify(state.locales)
    );

    // keep current value, or assign default if no current value
    if (
      state.detectedLocale === null &&
      state.locales &&
      state.locales.length > 0
    )
      state.detectedLocale = state.locales[0];
  },
  detectLocaleFailure(state, error) {
    state.error = error;

    // keep current value, or assign default if no current value
    if (
      state.detectedLocale === null &&
      state.locales &&
      state.locales.length > 0
    )
      state.detectedLocale = state.locales[0];
  },

  getAppLinksRequest(state) {},
  getAppLinksSuccess(state, links) {
    state.appLinks = links;
  },
  getAppLinksFailure(state, error) {
    state.error = error;
  },

  checkForUpgradeRequest(state) {},
  checkForUpgradeSuccess(state, ok) {
    console.log('checkForUpgradeSuccess: ok');
    state.upgradeCheck = true;
  },
  checkForUpgradeFailure(state, error) {
    state.error = error;
  },

  upgradeRequest(state) {},
  upgradeSuccess(state, configs) {
    state.configs = configs;
    state.upgrading = true;
  },
  upgradeFailure(state, error) {
    state.error = error;
  },

  upgradeCompleteRequest(state) {},
  upgradeCompleteSuccess(state) {
    state.upgrading = false;
  },
  upgradeCompleteFailure(state, error) {
    state.upgrading = false;
    state.error = error;
  },
};

export const system = {
  namespaced: true,
  getters,
  state,
  actions,
  mutations,
};
