'use strict';

let BaseBuildController = BaseController.extend({
  init: async function ($scope, projectsService, $routeParams, $modal) {
    this._super($scope);

    this.projectsService = projectsService;
    this.$modal = $modal;

    $scope.project = {};
    $scope.hubKey = undefined;
    $scope.projectID = $routeParams.id || window.localStorage.getItem('projectID');
    $scope.anyProjectDeployed = (await projectsService.anyProjectDeployed()).data.deployed;
    $scope.data = {
      selectedProject: { id: $scope.projectID },
      actionSelector: false,
      action: null
    };

    if (!$scope.projectID) {
      projectsService.list(1).then(response => {
        if (response.data.data && Array.isArray(response.data.data) && response.data.data.length > 0) {
          $scope.projectID = response.data.data[0].id;
        } else {
          window.location.href = '/build/project/create';
        }
      });
    }

    $scope.push = this.push.bind(this);
    $scope.localDeploy = this.localDeploy.bind(this);
    $scope.pushAndDeploy = this.pushAndDeploy.bind(this);
    $scope.deploy = this.deploy.bind(this);
    $scope.setDefaultAction = this.setDefaultAction.bind(this);

    $scope.isProjectAhead = projectsService.isProjectAhead($scope.projectID);
    $scope.hostsNumber = (await CFE_licenses.getHostsCount()).hostCount;

    projectsService.list(50).then(response => {
      $scope.data.projects = response.data.data;
    });

    this.prepareModuleData = ({ projectID, name, version }) => {
      // if masterfiles version is master then use git repository url to add the module
      return version === MASTER_VERSION
        ? { projectID, url: MASTERFILES_REPOSITORY }
        : { projectID, name, version };
    };

    this.addModule = ({ projectID, name, version, url }) => {
      common.globalSpinner.show();
      const promise = url ? this.projectsService.addModuleByUrl(projectID, url) : this.projectsService.addModule(projectID, name, version);
      promise.then(
        () => {
          notify.success('Module successfully added.');
          this.refreshProjectStatus();
          this.loadProjectInfo(this.$scope.projectID);
          common.globalSpinner.hide();
        },
        error => {
          displayError(error, this.$scope);
          common.globalSpinner.hide();
        }
      );
    };
  },
  defineListeners: function () {
    let self = this;

    this.$scope.$watch('data.actionSelectorInited', function (actionSelectorInited) {
      if (actionSelectorInited === true) {
        self.initActionSelectorCloseOnOutsideClick();
      }
    });

    this.$scope.$watch('project.classic_policy_set', function (classic_policy_set) {
      if (classic_policy_set === undefined) return;

      if (classic_policy_set === true) {
        self.$scope.addedModules = [];
      } else {
        self.projectsService.getModules(self.$scope.project.id).then(response => {
          self.$scope.addedModules = response.data;
        });
      }
    });

    this.$scope.$watch('projectID', function (id) {
      if (id) {
        self.loadProjectInfo(id);
        window.localStorage.setItem('projectID', id);
      }
    });

    this.$scope.$watchCollection('[project, isProjectAhead, data.isProjectSynchronized, hubKey]', function (data) {
      const [project, isProjectAhead, isProjectSynchronized, hubKey] = data;
      if (project == undefined || isProjectAhead == undefined || isProjectSynchronized == undefined) {
        return;
      }

      /**
       * Push should be disabled when:
       *  * project is local.
       *  * project is a classic policy set.
       *  * project has no commits to push.
       *  * project has already deployed (not locally), because the effect of pushing is the same as push and deploy,
       *    the policy will get deployed.
       */
      self.$scope.pushDisabled =
        project.is_local ||
        project.classic_policy_set ||
        !isProjectAhead ||
        (isProjectSynchronized.isSynchronised && isProjectSynchronized.deployedLocally === false);

      /**
       * Push and deploy should be disabled when:
       *  * project is local
       *  * project is a classic policy set.
       *  * project is deployed (not locally) and no changes in project and project was not deployed before
       */
      self.$scope.pushAndDeployDisabled =
        !hubKey ||
        project.is_local ||
        project.classic_policy_set ||
        (isProjectSynchronized.isSynchronised && isProjectSynchronized.deployedLocally === false && project.pushed_at !== null && !isProjectAhead);

      /**
       * Local deploy should be disabled when a project is deployed locally
       */
      self.$scope.localDeployDisabled =
        !hubKey ||
        project.classic_policy_set ||
        (project.is_deployed_locally && isProjectSynchronized.isSynchronised && isProjectSynchronized.deployedLocally === true);

      self.$scope.classicPolicySetDeployDisabled = project.classic_policy_set === true && isProjectSynchronized.isSynchronised;
    });
    document.body.addEventListener('spinner', event => (this.$scope.isSpinnerOn = event.detail.isOn));
    document.addEventListener('project_ahead_changed', () => (self.$scope.isProjectAhead = self.projectsService.isProjectAhead(self.$scope.projectID)));

    self.$scope.$watch('refreshStatus', function (status) {
      switch (status) {
        case 'behind':
          self.projectsService.rebase(self.$scope.projectID).then(() => self.loadProjectInfo(self.$scope.projectID));
          break;
        case 'diverged':
          this.modalInstance = self.$modal.open({
            templateUrl: 'diverged.html',
            backdrop: 'static',
            keyboard: true,
            windowClass: 'diverged-dialog',
            controller: function ($scope, $modalInstance, $parent) {
              $scope.close = function () {
                $modalInstance.close('cancel');
              };
              $scope.forceRebaseAndPush = function () {
                common.globalSpinner.show();
                self.projectsService.forceRebase(self.$scope.projectID).then(
                  () => {
                    self.projectsService.push(self.$scope.projectID).then(
                      () => {
                        notify.success('Pushed local changes in a new commit.');
                        $modalInstance.close('cancel');
                        common.globalSpinner.hide();
                        self.refreshProjectStatus();
                        self.loadProjectInfo(self.$scope.projectID);
                      },
                      error => {
                        displayError(error, this.$scope);
                        common.globalSpinner.hide();
                      }
                    );
                  },
                  error => {
                    displayError(error, this.$scope);
                    common.globalSpinner.hide();
                  }
                );
              };

              $scope.forcePull = function () {
                common.globalSpinner.show();
                self.projectsService.forcePull(self.$scope.projectID).then(
                  () => {
                    notify.success('Local changes discarded.');
                    $modalInstance.close('cancel');
                    common.globalSpinner.hide();
                    self.refreshProjectStatus();
                    self.loadProjectInfo(self.$scope.projectID);
                  },
                  error => {
                    displayError(error, this.$scope);
                    common.globalSpinner.hide();
                  }
                );
              };
            },
            resolve: {
              $parent: function () {
                return this;
              }
            }
          });
          break;
        case 'ahead':
          self.projectsService.setAhead(self.$scope.projectID);
          break;
        case 'ok':
          self.projectsService.removeAhead(self.$scope.projectID);
          break;
      }
    });
  },
  loadProjectInfo: async function (id) {
    let self = this;
    self.$scope.anyProjectDeployed = (await self.projectsService.anyProjectDeployed()).data.deployed;
    try {
      self.$scope.hubKey = await self.projectsService.getHubKey();
    } catch (_) {
      self.$scope.hubKey = null;
    }
    self.projectsService
      .get(id)
      .then(response => {
        self.$scope.project = response.data;
        self.projectsService.isProjectSynchronizedWithVcs(id).then(function (isProjectSynchronized) {
          self.$scope.data.isProjectSynchronized = isProjectSynchronized;

          if (self.$scope.project.classic_policy_set === false) {
            // if no default action in the DB then apply following logic to define an action:
            // if a project is local then `localDeploy`
            // if a project is not local and:
            //   project already deployed then `pushAndDeploy`
            //   project no projects deployed before then `pushAndDeploy`
            //   project there is already deployed project then `push`
            self.$scope.data.action =
              response.data.action ||
              (self.$scope.project.is_local
                ? self.localDeploy.name
                : self.$scope.anyProjectDeployed && !self.$scope.data.isProjectSynchronized.isSynchronised
                  ? self.push.name
                  : self.pushAndDeploy.name);
          } else {
            self.$scope.data.action = self.deploy.name;
          }
        });

        self.projectsService.getModules(self.$scope.project.id).then(response => {
            self.$scope.addedModules = response.data;
        });
      })
      .catch(e => {
        if (e.status == 404) {
          window.location.href = '/404';
        }
      });
  },

  push: async function (id) {
    common.globalSpinner.show();
    return new Promise((resolve, reject) => {
      this.projectsService
        .push(id)
        .then(() => {
          this.refreshProjectStatus();
          this.loadProjectInfo(this.$scope.projectID);
          common.globalSpinner.hide();
          notify.success('Project successfully pushed.');
          resolve();
        })
        .catch(error => {
          common.globalSpinner.hide();
          displayError(error, this.$scope);
          reject();
        });
    });
  },

  pushAndDeploy: async function (id) {
    common.globalSpinner.show();
    try {
      await this.push(id);
      await this.projectsService.deploy(id);
      notify.success('Project successfully deployed.');
      this.refreshProjectStatus();
      this.loadProjectInfo(this.$scope.projectID);
      common.globalSpinner.hide();
    } catch (error) {
      common.globalSpinner.hide();
      if (error.data && error.data.msg != undefined) {
        displayError(error, this.$scope);
      } else {
        notify.error(error.data);
      }
    }
  },

  deploy: async function (id) {
    common.globalSpinner.show();
    try {
      await this.projectsService.deploy(id);
      notify.success('Project successfully deployed.');
      this.refreshProjectStatus();
      this.loadProjectInfo(this.$scope.projectID);
      common.globalSpinner.hide();
    } catch (error) {
      common.globalSpinner.hide();
      if (error.data && error.data.msg != undefined) {
        displayError(error, this.$scope);
      } else {
        notify.error(error.data);
      }
    }
  },

  localDeploy: async function (id) {
    common.globalSpinner.show();
    try {
      await this.projectsService.localDeploy(id);
      await this.projectsService.deploy(id, true);
      notify.success('Project successfully deployed locally.');
      this.refreshProjectStatus();
      this.loadProjectInfo(this.$scope.projectID);
      common.globalSpinner.hide();
    } catch (error) {
      common.globalSpinner.hide();
      if (error.data && error.data.msg != undefined) {
        displayError(error, this.$scope);
      } else {
        notify.error(error.data);
      }
    }
  },

  setDefaultAction: function (action) {
    this.projectsService.setDefaultAction(this.$scope.projectID, action);
    this.$scope.data.action = action;
    this.$scope.data.actionSelector = false;
  },

  refreshProjectStatus: function () {
    this.projectsService.refresh(this.$scope.projectID).then(
      r => (this.$scope.refreshStatus = r.data.status),
      error => {
        common.globalSpinner.hide();
        displayError(error, this.$scope);
      }
    );
  },
  removeModule: function () {
    common.globalSpinner.show();
    let name = this.$scope.module.name;
    let encodedName = false;
    if (this.$scope.module.external) {
      name = this.$scope.module.encodedName;
      encodedName = true;
    }
    this.projectsService.removeModule(this.$scope.projectID, name, encodedName).then(
      () => {
        notify.success('Module successfully removed.');
        common.globalSpinner.hide();
        if (this.$scope.module.external) {
          window.location.href = `/build/project/${this.$scope.projectID}`;
        } else {
          this.refreshProjectStatus();
          this.loadProjectInfo(this.$scope.projectID);
        }
      },
      error => {
        displayError(error, this.$scope);
        common.globalSpinner.hide();
      }
    );
  },

  initActionSelectorCloseOnOutsideClick: function () {
    const self = this;
    const actionSelectorElement = document.querySelector('.actionSelector');
    const actionSelectorButton = document.querySelector('button.deploy-btn.options');
    document.addEventListener('click', function (event) {
      const outsideClick = !actionSelectorElement.contains(event.target) && !actionSelectorButton.contains(event.target);
      if (outsideClick) {
        self.$scope.data.actionSelector = false;
        self.$scope.$apply();
      }
    });
  }
});

const displayError = function (error, $scope) {
  if (error.data.msg) {
    notify.error(error.data.msg);
    if (error.data.details && Array.isArray(error.data.details)) {
      $scope.errors = error.data.details;
    }
  } else {
    notify.error(error.data);
  }
};

buildApp.directive('selectProject', function () {
  const linker = function (scope, element, attr) {
    element.chosen({ disable_search: true });
    element.prepend("<option class='create-project' value='' ><i class='bi bi-plus'></i> Add new project</option>");
    element.on('change', function (evt, params) {
      const url = params.selected == '' ? '/build/project/create' : `/build/project/${params.selected}`;
      window.location.href = url;
    });
    scope.$watchCollection(attr.watch, () => setTimeout(() => element.trigger('chosen:updated'), 50));
  };
  return {
    restrict: 'A',
    link: linker
  };
});
