import _ from 'lodash-es';
import { getEnvironments } from '@/react/portainer/environments/environment.service';
import { confirmWebEditorDiscard } from '@@/modals/confirm';
import { EnvironmentType } from '@/react/portainer/environments/types';
import { createWebhookId } from '@/portainer/helpers/webhookHelper';

export class EditEdgeStackViewController {
  /* @ngInject */
  constructor($async, $state, $window, EdgeGroupService, EdgeStackService, Notifications) {
    this.$async = $async;
    this.$state = $state;
    this.$window = $window;
    this.EdgeGroupService = EdgeGroupService;
    this.EdgeStackService = EdgeStackService;
    this.Notifications = Notifications;

    this.stack = null;
    this.edgeGroups = null;

    this.state = {
      actionInProgress: false,
      activeTab: 0,
      isStackDeployed: false,
    };

    this.formValues = {
      content: '',
    };

    this.deployStack = this.deployStack.bind(this);
    this.deployStackAsync = this.deployStackAsync.bind(this);
    this.getPaginatedEndpoints = this.getPaginatedEndpoints.bind(this);
    this.getPaginatedEndpointsAsync = this.getPaginatedEndpointsAsync.bind(this);
    this.onEditorChange = this.onEditorChange.bind(this);
    this.isEditorDirty = this.isEditorDirty.bind(this);
  }

  async $onInit() {
    return this.$async(async () => {
      const { stackId, tab } = this.$state.params;
      this.state.activeTab = tab;
      try {
        const [edgeGroups, model, file] = await Promise.all([this.EdgeGroupService.groups(), this.EdgeStackService.stack(stackId), this.EdgeStackService.stackFile(stackId)]);

        this.edgeGroups = edgeGroups;
        this.stack = model;
        this.stackEndpointIds = this.filterStackEndpoints(model.EdgeGroups, edgeGroups);
        this.originalFileContent = file;
        this.formValues = {
          content: file,
        };

        const stackEdgeGroups = model.EdgeGroups.map((id) => this.edgeGroups.find((e) => e.Id === id));
        const endpointTypes = stackEdgeGroups.flatMap((group) => group.EndpointTypes);
        const initiallyContainsKubeEnv = endpointTypes.includes(EnvironmentType.EdgeAgentOnKubernetes);
        const isComposeStack = this.stack.DeploymentType === 0;

        this.allowKubeToSelectCompose = initiallyContainsKubeEnv && isComposeStack;
      } catch (err) {
        this.Notifications.error('Failure', err, 'Unable to retrieve stack data');
      }

      this.oldFileContent = this.formValues.StackFileContent;

      this.$window.onbeforeunload = () => {
        if (this.isEditorDirty()) {
          return '';
        }
      };
    });
  }

  $onDestroy() {
    this.$window.onbeforeunload = undefined;
  }

  async uiCanExit() {
    if (this.isEditorDirty()) {
      return confirmWebEditorDiscard();
    }
  }

  onEditorChange(content) {
    this.formValues.content = content;
  }

  isEditorDirty() {
    return !this.state.isStackDeployed && this.formValues.content.replace(/(\r\n|\n|\r)/gm, '') !== this.originalFileContent.replace(/(\r\n|\n|\r)/gm, '');
  }

  filterStackEndpoints(groupIds, groups) {
    return _.flatten(
      _.map(groupIds, (Id) => {
        const group = _.find(groups, { Id });
        return group.Endpoints;
      })
    );
  }

  deployStack(values) {
    return this.deployStackAsync(values);
  }

  async deployStackAsync(values) {
    this.state.actionInProgress = true;
    try {
      const updateVersion = !!(this.originalFileContent != values.content || values.useManifestNamespaces !== this.stack.UseManifestNamespaces);

      await this.EdgeStackService.updateStack(this.stack.Id, {
        stackFileContent: values.content,
        edgeGroups: values.edgeGroups,
        deploymentType: values.deploymentType,
        updateVersion,
        webhook: values.webhookEnabled ? this.stack.Webhook || createWebhookId() : '',
      });
      this.Notifications.success('Success', 'Stack successfully deployed');
      this.state.isStackDeployed = true;
      this.$state.go('edge.stacks');
    } catch (err) {
      this.Notifications.error('Deployment error', err, 'Unable to deploy stack');
    } finally {
      this.state.actionInProgress = false;
    }
  }

  getPaginatedEndpoints(...args) {
    return this.$async(this.getPaginatedEndpointsAsync, ...args);
  }

  async getPaginatedEndpointsAsync(lastId, limit, search) {
    try {
      if (this.stackEndpointIds.length === 0) {
        return { endpoints: [], totalCount: 0 };
      }

      const query = { search, endpointIds: this.stackEndpointIds };
      const { value, totalCount } = await getEnvironments({ start: lastId, limit, query });

      return { endpoints: value, totalCount };
    } catch (err) {
      this.Notifications.error('Failure', err, 'Unable to retrieve environment information');
    }
  }
}