(function() {
  /**
   * The Setup page provides a nice GUI walkthrough experience for setting up Quay Enterprise.
   */
  angular.module('quayPages').config(['pages', function(pages) {
    pages.create('setup', 'setup.html', SetupCtrl,
      {
        'newLayout': true,
        'title': 'Quay Enterprise Setup'
      })
  }]);

  function SetupCtrl($scope, $timeout, ApiService, Features, UserService, ContainerService, CoreDialog) {
    if (!Features.SUPER_USERS) {
      return;
    }

    $scope.HOSTNAME_REGEX = '^[a-zA-Z-0-9_\.\-]+(:[0-9]+)?$';

    $scope.validateHostname = function(hostname) {
      if (hostname.indexOf('127.0.0.1') == 0 || hostname.indexOf('localhost') == 0) {
        return 'Please specify a non-localhost hostname. "localhost" will refer to the container, not your machine.'
      }

      return null;
    };

    // Note: The values of the enumeration are important for isStepFamily. For example,
    // *all* states under the "configuring db" family must start with "config-db".
    $scope.States = {
      // Loading the state of the product.
      'LOADING': 'loading',

      // The configuration directory is missing.
      'MISSING_CONFIG_DIR': 'missing-config-dir',

      // The config.yaml exists but it is invalid.
      'INVALID_CONFIG': 'config-invalid',

      // License is being uploaded.
      'UPLOAD_LICENSE': 'upload-license',

      // DB is being configured.
      'CONFIG_DB': 'config-db',

      // DB information is being validated.
      'VALIDATING_DB': 'config-db-validating',

      // DB information is being saved to the config.
      'SAVING_DB': 'config-db-saving',

      // A validation error occurred with the database.
      'DB_ERROR': 'config-db-error',

      // Database is being setup.
      'DB_SETUP': 'setup-db',

      // Database setup has succeeded.
      'DB_SETUP_SUCCESS': 'setup-db-success',

      // An error occurred when setting up the database.
      'DB_SETUP_ERROR': 'setup-db-error',

      // The container is being restarted for the database changes.
      'DB_RESTARTING': 'setup-db-restarting',

      // A superuser is being configured.
      'CREATE_SUPERUSER': 'create-superuser',

      // The superuser is being created.
      'CREATING_SUPERUSER': 'create-superuser-creating',

      // An error occurred when setting up the superuser.
      'SUPERUSER_ERROR': 'create-superuser-error',

      // The superuser was created successfully.
      'SUPERUSER_CREATED': 'create-superuser-created',

      // General configuration is being setup.
      'CONFIG': 'config',

      // The configuration is fully valid.
      'VALID_CONFIG': 'valid-config',

      // The container is being restarted for the configuration changes.
      'CONFIG_RESTARTING': 'config-restarting',

      // The product is ready for use.
      'READY': 'ready'
    }

    $scope.csrf_token = window.__token;
    $scope.currentStep = $scope.States.LOADING;
    $scope.errors = {};
    $scope.stepProgress = [];
    $scope.hasSSL = false;
    $scope.hostname = null;
    $scope.currentConfig = null;

    $scope.currentState = {
      'hasDatabaseSSLCert': false,
      'licenseValid': false
    };

    $scope.$watch('currentStep', function(currentStep) {
      $scope.stepProgress = $scope.getProgress(currentStep);

      switch (currentStep) {
        case $scope.States.CONFIG:
          $('#setupModal').modal('hide');
          break;

        case $scope.States.MISSING_CONFIG_DIR:
          $scope.showMissingConfigDialog();
          break;

        case $scope.States.INVALID_CONFIG:
          $scope.showInvalidConfigDialog();
          break;

        case $scope.States.DB_SETUP:
          $scope.performDatabaseSetup();
          // Fall-through.

        case $scope.States.CREATE_SUPERUSER:
        case $scope.States.DB_RESTARTING:
        case $scope.States.CONFIG_DB:
        case $scope.States.UPLOAD_LICENSE:
        case $scope.States.VALID_CONFIG:
        case $scope.States.READY:
          $('#setupModal').modal({
              keyboard: false,
              backdrop: 'static'
          });
          break;
      }
    });

    $scope.restartContainer = function(state) {
      $scope.currentStep = state;
      ContainerService.restartContainer(function() {
        $scope.checkStatus()
      });
    };

    $scope.showSuperuserPanel = function() {
      $('#setupModal').modal('hide');
      var prefix = $scope.hasSSL ? 'https' : 'http';
      var hostname = $scope.hostname;
      if (!hostname) {
        hostname = document.location.hostname;
        if (document.location.port) {
          hostname = hostname + ':' + document.location.port;
        }
      }

      window.location = prefix + '://' + hostname + '/superuser';
    };

    $scope.configurationSaved = function(config) {
      $scope.hasSSL = config['PREFERRED_URL_SCHEME'] == 'https';
      $scope.hostname = config['SERVER_HOSTNAME'];
      $scope.currentConfig = config;

      $scope.currentStep = $scope.States.VALID_CONFIG;
    };

    $scope.getProgress = function(step) {
      var isStep = $scope.isStep;
      var isStepFamily = $scope.isStepFamily;
      var States = $scope.States;

      return [
        isStepFamily(step, States.UPLOAD_LICENSE),
        isStepFamily(step, States.CONFIG_DB),
        isStepFamily(step, States.DB_SETUP),
        isStep(step, States.DB_RESTARTING),
        isStepFamily(step, States.CREATE_SUPERUSER),
        isStep(step, States.CONFIG),
        isStep(step, States.VALID_CONFIG),
        isStep(step, States.CONFIG_RESTARTING),
        isStep(step, States.READY)
      ];
    };

    $scope.isStepFamily = function(step, family) {
      if (!step) { return false; }
      return step.indexOf(family) == 0;
    };

    $scope.isStep = function(step) {
      for (var i = 1; i < arguments.length; ++i) {
        if (arguments[i] == step) {
          return true;
        }
      }
      return false;
    };

    $scope.beginSetup = function() {
      $scope.currentStep = $scope.States.CONFIG_DB;
    };

    $scope.showInvalidConfigDialog = function() {
      var message = "The <code>config.yaml</code> file found in <code>conf/stack</code> could not be parsed."
      var title = "Invalid configuration file";
      CoreDialog.fatal(title, message);
    };


    $scope.showMissingConfigDialog = function() {
      var message = "A volume should be mounted into the container at <code>/conf/stack</code>: " +
                    "<br><br><pre>docker run -v /path/to/config:/conf/stack</pre>" +
                    "<br>Once fixed, restart the container. For more information, " +
                    "<a href='https://coreos.com/docs/enterprise-registry/initial-setup/'>" +
                    "Read the Setup Guide</a>"

      var title = "Missing configuration volume";
      CoreDialog.fatal(title, message);
    };

    $scope.parseDbUri = function(value) {
      if (!value) { return null; }

      // Format: mysql+pymysql://<username>:<url escaped password>@<hostname>/<database_name>
      var uri = URI(value);
      return {
        'kind': uri.protocol(),
        'username': uri.username(),
        'password': uri.password(),
        'server': uri.host(),
        'database': uri.path() ? uri.path().substr(1) : ''
      };
    };

    $scope.serializeDbUri = function(fields) {
      if (!fields['server']) { return ''; }

      try {
        if (!fields['server']) { return ''; }
        if (!fields['database']) { return ''; }

        var uri = URI();
        uri = uri && uri.host(fields['server']);
        uri = uri && uri.protocol(fields['kind']);
        uri = uri && uri.username(fields['username']);
        uri = uri && uri.password(fields['password']);
        uri = uri && uri.path('/' + (fields['database'] || ''));
        uri = uri && uri.toString();
      } catch (ex) {
        return '';
      }

      return uri;
    };

    $scope.createSuperUser = function() {
      $scope.currentStep = $scope.States.CREATING_SUPERUSER;
      ApiService.scCreateInitialSuperuser($scope.superUser, null).then(function(resp) {
        UserService.load();
        $scope.checkStatus();
      }, function(resp) {
        $scope.currentStep = $scope.States.SUPERUSER_ERROR;
        $scope.errors.SuperuserCreationError = ApiService.getErrorMessage(resp, 'Could not create superuser');
      });
    };

    $scope.performDatabaseSetup = function() {
      $scope.currentStep = $scope.States.DB_SETUP;
      ApiService.scSetupDatabase(null, null).then(function(resp) {
        if (resp['error']) {
          $scope.currentStep = $scope.States.DB_SETUP_ERROR;
          $scope.errors.DatabaseSetupError = resp['error'];
        } else {
          $scope.currentStep = $scope.States.DB_SETUP_SUCCESS;
        }
      }, ApiService.errorDisplay('Could not setup database. Please report this to support.'))
    };

    $scope.validateDatabase = function() {
      $scope.currentStep = $scope.States.VALIDATING_DB;
      $scope.databaseInvalid = null;

      var data = {
        'config': {
          'DB_URI': $scope.databaseUri
        },
        'hostname': window.location.host
      };

      if ($scope.currentState.hasDatabaseSSLCert) {
        data['config']['DB_CONNECTION_ARGS'] = {
          'ssl': {
            'ca': 'conf/stack/database.pem'
          }
        };
      }

      var params = {
        'service': 'database'
      };

      ApiService.scValidateConfig(data, params).then(function(resp) {
        var status = resp.status;

        if (status) {
          $scope.currentStep = $scope.States.SAVING_DB;
          ApiService.scUpdateConfig(data, null).then(function(resp) {
            $scope.checkStatus();
          }, ApiService.errorDisplay('Cannot update config. Please report this to support'));
        } else {
          $scope.currentStep = $scope.States.DB_ERROR;
          $scope.errors.DatabaseValidationError = resp.reason;
        }
      }, ApiService.errorDisplay('Cannot validate database. Please report this to support'));
    };

    $scope.checkStatus = function() {
      ContainerService.checkStatus(function(resp) {
        $scope.currentStep = resp['status'];
      }, $scope.currentConfig);
    };

    // Load the initial status.
    $scope.checkStatus();
  };
})();