import { Base64 } from 'js-base64';

import store from '../redux';
import { history } from '../components/utils/router';
import { ALL_NAMESPACES_KEY, LAST_NAMESPACE_NAME_LOCAL_STORAGE_KEY } from '../const';
import { getNSPrefix } from '../components/utils/link';
import { allModels } from '../module/k8s/k8s-models';

// URL routes that can be namespaced
export const namespacedResources = new Set();

allModels().forEach((v, k) => {
  if (!v.namespaced) {
    return;
  }
  if (v.crd) {
    namespacedResources.add(k);
    return;
  }

  namespacedResources.add(v.plural);
});

export const getActiveNamespace = () => store.getState().UI.get('activeNamespace');

export const formatNamespacedRouteForResource = (resource, activeNamespace=getActiveNamespace()) => {
  return activeNamespace === ALL_NAMESPACES_KEY
    ? `/k8s/all-namespaces/${resource}`
    : `/k8s/ns/${activeNamespace}/${resource}`;
};

export const formatNamespaceRoute = (activeNamespace, originalPath, location) => {
  const prefix = getNSPrefix(originalPath);
  if (!prefix) {
    return originalPath;
  }

  originalPath = originalPath.substr(prefix.length + window.SERVER_FLAGS.basePath.length);

  let parts = originalPath.split('/').filter(p => p);
  let previousNS = '';
  if (parts[0] === 'all-namespaces') {
    parts.shift();
    previousNS = ALL_NAMESPACES_KEY;
  } else if (parts[0] === 'ns') {
    parts.shift();
    previousNS = parts.shift();
  }

  if ((previousNS !== activeNamespace && (parts[1] !== 'new' || activeNamespace !== ALL_NAMESPACES_KEY)) || activeNamespace === ALL_NAMESPACES_KEY && parts[1] === 'new') {
    // a given resource will not exist when we switch namespaces, so pop off the tail end
    parts = parts.slice(0, 1);
  }

  const namespacePrefix = activeNamespace === ALL_NAMESPACES_KEY ? 'all-namespaces' : `ns/${activeNamespace}`;

  let path = `${prefix}/${namespacePrefix}`;
  if (parts.length) {
    path += `/${parts.join('/')}`;
  }

  if (location) {
    path += `${location.search}${location.hash}`;
  }

  return path;
};

export const types = {
  dismissOverviewDetails: 'dismissOverviewDetails',
  selectOverviewDetailsTab: 'selectOverviewDetailsTab',
  selectOverviewItem: 'selectOverviewItem',
  selectOverviewView: 'selectOverviewView',
  setActiveNamespace: 'setActiveNamespace',
  setCreateProjectMessage: 'setCreateProjectMessage',
  setClusterID: 'setClusterID',
  setCurrentLocation: 'setCurrentLocation',
  setMonitoringData: 'setMonitoringData',
  toggleMonitoringGraphs: 'toggleMonitoringGraphs',
  setUser: 'setUser',
  sortList: 'sortList',
  startImpersonate: 'startImpersonate',
  stopImpersonate: 'stopImpersonate',
  updateOverviewMetrics: 'updateOverviewMetrics',
  updateOverviewResources: 'updateOverviewResources',
  updateOverviewSelectedGroup: 'updateOverviewSelectedGroup',
  updateOverviewGroupOptions: 'updateOverviewGroupOptions',
  updateOverviewFilterValue: 'updateOverviewFilterValue',
};

/** @type {{[key: string]: function}} */
export const UIActions = {
  [types.setCurrentLocation]: location => ({location, type: types.setCurrentLocation}),

  [types.setClusterID]: clusterID => ({clusterID, type: types.setClusterID}),

  [types.setActiveNamespace]: (namespace) => {
    if (namespace) {
      namespace = namespace.trim();
    }

    // make it noop when new active namespace is the same
    // otherwise users will get page refresh and cry about
    // broken direct links and bookmarks
    if (namespace !== getActiveNamespace()) {
      const oldPath = window.location.pathname;
      if (getNSPrefix(oldPath)) {
        history.pushPath(formatNamespaceRoute(namespace, oldPath, window.location));
      }
      // remember the most recently-viewed project, which is automatically
      // selected when returning to the console
      localStorage.setItem(LAST_NAMESPACE_NAME_LOCAL_STORAGE_KEY, namespace);
    }

    return {
      type: types.setActiveNamespace,
      value: namespace,
    };
  },

  [types.startImpersonate]: (kind, name) => async(dispatch, getState) => {
    let textEncoder;
    try {
      textEncoder = new TextEncoder('utf-8');
    } catch (e) {
      // eslint-disable-next-line no-console
      console.info('Browser lacks TextEncoder. Falling back to polyfill.', e);
    }

    if (!textEncoder) {
      textEncoder = await import('text-encoding').then(module => new module.TextEncoder('utf-8'));
    }

    const imp = getState().UI.get('impersonate', {});
    if ((imp.name && imp.name !== name) || (imp.kin && imp.kind !== kind)) {
      // eslint-disable-next-line no-console
      console.warn(`Impersonate race detected: ${name} vs ${imp.name} / ${kind} ${imp.kind}`);
      return;
    }

    /* Subprotocols are comma-separated, so commas aren't allowed. Also "="
     * and "/" aren't allowed, so base64 but replace illegal chars.
     */
    let encodedName = textEncoder.encode(name);
    encodedName = Base64.encode(String.fromCharCode.apply(String, encodedName));
    encodedName = encodedName.replace(/=/g, '_').replace(/\//g, '-');

    let subprotocols;
    if (kind === 'User' ) {
      subprotocols = [`Impersonate-User.${encodedName}`];
    }
    if (kind === 'Group') {
      subprotocols = [`Impersonate-Group.${encodedName}`];
    }

    dispatch({kind, name, subprotocols, type: types.startImpersonate});
    history.push(window.SERVER_FLAGS.basePath);
  },

  [types.stopImpersonate]: () => dispatch => {
    dispatch({type: types.stopImpersonate});
    history.push(window.SERVER_FLAGS.basePath);
  },

  [types.sortList]: (listId, field, func, orderBy, column) => {
    const url = new URL(window.location);
    const sp = new URLSearchParams(window.location.search);
    sp.set('orderBy', orderBy);
    sp.set('sortBy', column);
    history.replace(`${url.pathname}?${sp.toString()}${url.hash}`);
    return {listId, field, func, orderBy, type: types.sortList};
  },

  [types.setCreateProjectMessage]: message => ({type: types.setCreateProjectMessage, message}),

  [types.setUser]: user => ({type: types.setUser, user}),

  [types.selectOverviewView]: view => ({type: types.selectOverviewView, view}),

  [types.selectOverviewItem]: uid => ({type: types.selectOverviewItem, uid}),

  [types.selectOverviewDetailsTab]: tab => ({type: types.selectOverviewDetailsTab, tab}),

  [types.updateOverviewMetrics]: metrics => ({type: types.updateOverviewMetrics, metrics}),

  [types.updateOverviewResources]: resources => ({type: types.updateOverviewResources, resources}),

  [types.dismissOverviewDetails]: () => ({type: types.dismissOverviewDetails}),

  [types.updateOverviewSelectedGroup]: (group) => ({type: types.updateOverviewSelectedGroup, group}),

  [types.updateOverviewGroupOptions]: (groups) => ({type: types.updateOverviewGroupOptions, groups}),

  [types.updateOverviewFilterValue]: (value) => ({type: types.updateOverviewFilterValue, value}),

  monitoringLoading: key => ({type: types.setMonitoringData, key, data: {loaded: false, loadError: null, data: null}}),

  monitoringLoaded: (key, data) => ({type: types.setMonitoringData, key, data: {loaded: true, loadError: null, data}}),

  monitoringErrored: (key, loadError) => ({type: types.setMonitoringData, key, data: {loaded: true, loadError, data: null}}),

  [types.toggleMonitoringGraphs]: () => ({type: types.toggleMonitoringGraphs}),
};
