import { GlToast } from '@gitlab/ui';
import Visibility from 'visibilityjs';
import Vue from 'vue';
import { createAlert } from '~/alert';
import AccessorUtilities from '~/lib/utils/accessor';
import Poll from '~/lib/utils/poll';
import { s__ } from '~/locale';
import PersistentUserCallout from '~/persistent_user_callout';
import initSettingsPanels from '~/settings_panels';
import { initProjectSelects } from '~/vue_shared/components/entity_select/init_project_selects';
import RemoveClusterConfirmation from './components/remove_cluster_confirmation.vue';
import ClustersService from './services/clusters_service';
import ClustersStore from './stores/clusters_store';

const Environments = () => import('ee_component/clusters/components/environments.vue');

Vue.use(GlToast);

export default class Clusters {
  constructor() {
    const {
      statusPath,
      clusterEnvironmentsPath,
      hasRbac,
      providerType,
      clusterStatus,
      clusterStatusReason,
      helpPath,
      environmentsHelpPath,
      clustersHelpPath,
      deployBoardsHelpPath,
      clusterId,
    } = document.querySelector('.js-edit-cluster-form').dataset;

    this.clusterId = clusterId;
    this.clusterNewlyCreatedKey = `cluster_${this.clusterId}_newly_created`;
    this.clusterBannerDismissedKey = `cluster_${this.clusterId}_banner_dismissed`;

    this.store = new ClustersStore();
    this.store.setHelpPaths({
      helpPath,
      environmentsHelpPath,
      clustersHelpPath,
      deployBoardsHelpPath,
    });
    this.store.updateStatus(clusterStatus);
    this.store.updateStatusReason(clusterStatusReason);
    this.store.updateProviderType(providerType);
    this.store.updateRbac(hasRbac);
    this.service = new ClustersService({
      endpoint: statusPath,
      clusterEnvironmentsEndpoint: clusterEnvironmentsPath,
    });

    this.errorContainer = document.querySelector('.js-cluster-error');
    this.successContainer = document.querySelector('.js-cluster-success');
    this.creatingContainer = document.querySelector('.js-cluster-creating');
    this.unreachableContainer = document.querySelector('.js-cluster-api-unreachable');
    this.authenticationFailureContainer = document.querySelector(
      '.js-cluster-authentication-failure',
    );
    this.errorReasonContainer = this.errorContainer.querySelector('.js-error-reason');
    this.tokenField = document.querySelector('.js-cluster-token');

    initProjectSelects();
    Clusters.initDismissableCallout();
    initSettingsPanels();

    this.initEnvironments();

    if (clusterEnvironmentsPath && this.environments) {
      this.store.toggleFetchEnvironments(true);

      this.initPolling(
        'fetchClusterEnvironments',
        (data) => this.handleClusterEnvironmentsSuccess(data),
        () => this.handleEnvironmentsPollError(),
      );
    }

    this.updateContainer(null, this.store.state.status, this.store.state.statusReason);

    this.addListeners();
    if (statusPath && !this.environments) {
      this.initPolling(
        'fetchClusterStatus',
        (data) => this.handleClusterStatusSuccess(data),
        () => this.handlePollError(),
      );
    }

    this.initRemoveClusterActions();
  }

  initEnvironments() {
    const { store } = this;
    const el = document.querySelector('#js-cluster-environments');

    if (!el) {
      return;
    }

    this.environments = new Vue({
      el,
      data() {
        return {
          state: store.state,
        };
      },
      render(createElement) {
        return createElement(Environments, {
          props: {
            isFetching: this.state.fetchingEnvironments,
            environments: this.state.environments,
            environmentsHelpPath: this.state.environmentsHelpPath,
            clustersHelpPath: this.state.clustersHelpPath,
            deployBoardsHelpPath: this.state.deployBoardsHelpPath,
          },
        });
      },
    });
  }

  initRemoveClusterActions() {
    const el = document.querySelector('#js-cluster-remove-actions');
    if (el && el.dataset) {
      const { clusterName, clusterPath, hasManagementProject } = el.dataset;

      this.removeClusterAction = new Vue({
        el,
        render(createElement) {
          return createElement(RemoveClusterConfirmation, {
            props: {
              clusterName,
              clusterPath,
              hasManagementProject,
            },
          });
        },
      });
    }
  }

  handleClusterEnvironmentsSuccess(data) {
    this.store.toggleFetchEnvironments(false);
    this.store.updateEnvironments(data.data);
  }

  static initDismissableCallout() {
    const callout = document.querySelector('.js-cluster-security-warning');
    PersistentUserCallout.factory(callout);
  }

  addBannerCloseHandler(el, status) {
    el.querySelector('.js-close').addEventListener('click', () => {
      el.classList.add('hidden');
      this.setBannerDismissedState(status, true);
    });
  }

  addListeners() {
    // Add event listener to all the banner close buttons
    this.addBannerCloseHandler(this.unreachableContainer, 'unreachable');
    this.addBannerCloseHandler(this.authenticationFailureContainer, 'authentication_failure');
  }

  initPolling(method, successCallback, errorCallback) {
    this.poll = new Poll({
      resource: this.service,
      method,
      successCallback,
      errorCallback,
    });

    if (!Visibility.hidden()) {
      this.poll.makeRequest();
    }

    Visibility.change(() => {
      if (!Visibility.hidden() && !this.destroyed) {
        this.poll.restart();
      } else {
        this.poll.stop();
      }
    });
  }

  handlePollError() {
    this.constructor.handleError();
  }

  handleEnvironmentsPollError() {
    this.store.toggleFetchEnvironments(false);

    this.handlePollError();
  }

  static handleError() {
    createAlert({
      message: s__('ClusterIntegration|Something went wrong on our end.'),
    });
  }

  handleClusterStatusSuccess(data) {
    const prevStatus = this.store.state.status;

    this.store.updateStateFromServer(data.data);

    this.updateContainer(prevStatus, this.store.state.status, this.store.state.statusReason);
  }

  hideAll() {
    this.errorContainer.classList.add('hidden');
    this.successContainer.classList.add('hidden');
    this.creatingContainer.classList.add('hidden');
    this.unreachableContainer.classList.add('hidden');
    this.authenticationFailureContainer.classList.add('hidden');
  }

  setBannerDismissedState(status, isDismissed) {
    if (AccessorUtilities.canUseLocalStorage()) {
      window.localStorage.setItem(this.clusterBannerDismissedKey, `${status}_${isDismissed}`);
    }
  }

  isBannerDismissed(status) {
    let bannerState;
    if (AccessorUtilities.canUseLocalStorage()) {
      bannerState = window.localStorage.getItem(this.clusterBannerDismissedKey);
    }

    return bannerState === `${status}_true`;
  }

  setClusterNewlyCreated(state) {
    if (AccessorUtilities.canUseLocalStorage()) {
      window.localStorage.setItem(this.clusterNewlyCreatedKey, Boolean(state));
    }
  }

  isClusterNewlyCreated() {
    // once this is true, it will always be true for a given page load
    if (!this.isNewlyCreated) {
      let newlyCreated;
      if (AccessorUtilities.canUseLocalStorage()) {
        newlyCreated = window.localStorage.getItem(this.clusterNewlyCreatedKey);
      }

      this.isNewlyCreated = newlyCreated === 'true';
    }
    return this.isNewlyCreated;
  }

  updateContainer(prevStatus, status, error) {
    if (status !== 'created' && this.isBannerDismissed(status)) {
      return;
    }
    this.setBannerDismissedState(status, false);

    if (prevStatus !== status) {
      this.hideAll();

      switch (status) {
        case 'created':
          if (this.isClusterNewlyCreated()) {
            this.setClusterNewlyCreated(false);
            this.successContainer.classList.remove('hidden');
          } else if (prevStatus) {
            this.setClusterNewlyCreated(true);
            window.location.reload();
          }
          break;
        case 'errored':
          this.errorContainer.classList.remove('hidden');
          this.errorReasonContainer.textContent = error;
          break;
        case 'unreachable':
          this.unreachableContainer.classList.remove('hidden');
          break;
        case 'authentication_failure':
          this.authenticationFailureContainer.classList.remove('hidden');
          break;
        case 'scheduled':
        case 'creating':
          this.creatingContainer.classList.remove('hidden');
          break;
        default:
      }
    }
  }

  destroy() {
    this.destroyed = true;

    if (this.poll) {
      this.poll.stop();
    }

    if (this.environments) {
      this.environments.$destroy();
    }
  }
}
