/* eslint-disable
    handle-callback-err,
    no-return-assign,
    no-self-assign,
    no-undef,
*/
// TODO: This file was created by bulk-decaffeinate.
// Fix any style issues and re-enable lint.
/*
 * decaffeinate suggestions:
 * DS101: Remove unnecessary use of Array.from
 * DS102: Remove unnecessary code created because of implicit returns
 * DS207: Consider shorter variations of null checks
 * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
 */
'use strict';

/* COMMON CODE */

/**
 * Provides a set of common properties and methods to the $scope parameter. They are used
 * in the various members' admin controllers.
 *
 * Provides :
 *  - $scope.groups = [{Group}]
 *  - $scope.trainings = [{Training}]
 *  - $scope.plans = []
 *  - $scope.datePicker = {}
 *  - $scope.submited(content)
 *  - $scope.cancel()
 *  - $scope.fileinputClass(v)
 *  - $scope.openDatePicker($event)
 *  - $scope.openSubscriptionDatePicker($event)
 *
 * Requires :
 *  - $state (Ui-Router) [ 'app.admin.members' ]
 */
class MembersController {
  constructor ($scope, $state, Group, Training) {
    // Retrieve the profiles groups (eg. students ...)
    Group.query(function (groups) { $scope.groups = groups.filter(function (g) { return (g.slug !== 'admins') && !g.disabled; }); });

    // Retrieve the list of available trainings
    Training.query().$promise.then(function (data) {
      $scope.trainings = data.map(function (d) {
        return ({
          id: d.id,
          name: d.name,
          disabled: d.disabled
        });
      });
    });

    // Default parameters for AngularUI-Bootstrap datepicker
    $scope.datePicker = {
      format: Fablab.uibDateFormat,
      opened: false, // default: datePicker is not shown
      subscription_date_opened: false,
      options: {
        startingDay: Fablab.weekStartingDay
      }
    };

    /**
     * Shows the birth day datepicker
     * @param $event {Object} jQuery event object
     */
    $scope.openDatePicker = function ($event) {
      $event.preventDefault();
      $event.stopPropagation();
      return $scope.datePicker.opened = true;
    };

    /**
     * Shows the end of subscription datepicker
     * @param $event {Object} jQuery event object
     */
    $scope.openSubscriptionDatePicker = function ($event) {
      $event.preventDefault();
      $event.stopPropagation();
      return $scope.datePicker.subscription_date_opened = true;
    };

    /**
     * For use with ngUpload (https://github.com/twilson63/ngUpload).
     * Intended to be the callback when an upload is done: any raised error will be stacked in the
     * $scope.alerts array. If everything goes fine, the user is redirected to the members listing page.
     * @param content {Object} JSON - The upload's result
     */
    $scope.submited = function (content) {
      if ((content.id == null)) {
        $scope.alerts = [];
        return angular.forEach(content, function (v, k) {
          angular.forEach(v, function (err) {
            $scope.alerts.push({
              msg: k + ': ' + err,
              type: 'danger'
            });
          });
        });
      } else {
        return $state.go('app.admin.members');
      }
    };

    /**
     * Changes the admin's view to the members list page
     */
    $scope.cancel = function () { $state.go('app.admin.members'); };

    /**
     * For use with 'ng-class', returns the CSS class name for the uploads previews.
     * The preview may show a placeholder or the content of the file depending on the upload state.
     * @param v {*} any attribute, will be tested for truthiness (see JS evaluation rules)
     */
    $scope.fileinputClass = function (v) {
      if (v) {
        return 'fileinput-exists';
      } else {
        return 'fileinput-new';
      }
    };
  }
}

/**
 * Controller used in the members/groups management page
 */
Application.Controllers.controller('AdminMembersController', ['$scope', '$sce', 'membersPromise', 'adminsPromise', 'growl', 'Admin', 'dialogs', '_t', 'Member', 'Export',
  function ($scope, $sce, membersPromise, adminsPromise, growl, Admin, dialogs, _t, Member, Export) {
  /* PRIVATE STATIC CONSTANTS */

    // number of users loaded each time we click on 'load more...'
    const USERS_PER_PAGE = 20;

    /* PUBLIC SCOPE */

    // members list
    $scope.members = membersPromise;

    $scope.member = {
    // Members plain-text filtering. Default: not filtered
      searchText: '',
      // Members ordering/sorting. Default: not sorted
      order: 'id',
      // currently displayed page of members
      page: 1,
      // true when all members where loaded
      noMore: false
    };

    // admins list
    $scope.admins = adminsPromise.admins.filter(function(m) { return m.id != Fablab.superadminId; });

    // Admins ordering/sorting. Default: not sorted
    $scope.orderAdmin = null;

    /**
     * Change the members ordering criterion to the one provided
     * @param orderBy {string} ordering criterion
     */
    $scope.setOrderMember = function (orderBy) {
      if ($scope.member.order === orderBy) {
        $scope.member.order = `-${orderBy}`;
      } else {
        $scope.member.order = orderBy;
      }

      resetSearchMember();
      return memberSearch();
    };

    /**
     * Change the admins ordering criterion to the one provided
     * @param orderAdmin {string} ordering criterion
     */
    $scope.setOrderAdmin = function (orderAdmin) {
      if ($scope.orderAdmin === orderAdmin) {
        return $scope.orderAdmin = `-${orderAdmin}`;
      } else {
        return $scope.orderAdmin = orderAdmin;
      }
    };

    /**
     * Ask for confirmation then delete the specified administrator
     * @param admins {Array} full list of administrators
     * @param admin {Object} administrator to delete
     */
    $scope.destroyAdmin = function (admins, admin) {
      dialogs.confirm(
        {
          resolve: {
            object () {
              return {
                title: _t('confirmation_required'),
                msg: $sce.trustAsHtml(_t('do_you_really_want_to_delete_this_administrator_this_cannot_be_undone') + '<br/><br/>' + _t('this_may_take_a_while_please_wait'))
              };
            }
          }
        },
        function () { // cancel confirmed
          Admin.delete(
            { id: admin.id },
            function () {
              admins.splice(findAdminIdxById(admins, admin.id), 1);
              return growl.success(_t('administrator_successfully_deleted'));
            },
            function (error) { growl.error(_t('unable_to_delete_the_administrator')); }
          );
        }
      );
    };

    /**
     * Callback for the 'load more' button.
     * Will load the next results of the current search, if any
     */
    $scope.showNextMembers = function () {
      $scope.member.page += 1;
      return memberSearch(true);
    };

    /**
     * Callback when the search field content changes: reload the search results
     */
    $scope.updateTextSearch = function () {
      if (searchTimeout) clearTimeout(searchTimeout);
      searchTimeout = setTimeout(function() {
        resetSearchMember();
        memberSearch();
      }, 300);
    };

    /**
     * Callback to alert the admin that the export request was acknowledged and is
     * processing right now.
     */
    $scope.alertExport = function (type) {
      Export.status({ category: 'users', type }).then(function (res) {
        if (!res.data.exists) {
          return growl.success(_t('export_is_running_you_ll_be_notified_when_its_ready'));
        }
      });
    };

    /* PRIVATE SCOPE */

    /**
     * Kind of constructor: these actions will be realized first when the controller is loaded
     */
    const initialize = function () {
      if (!membersPromise[0] || (membersPromise[0].maxMembers <= $scope.members.length)) {
        return $scope.member.noMore = true;
      }
    };

    /**
     * Will temporize the search query to prevent overloading the API
     */
    var searchTimeout = null;

    /**
     * Iterate through the provided array and return the index of the requested admin
     * @param admins {Array} full list of users with role 'admin'
     * @param id {Number} user id of the admin to retrieve in the list
     * @returns {Number} index of the requested admin, in the provided array
     */
    var findAdminIdxById = function (admins, id) {
      return (admins.map(function (admin) { return admin.id; })).indexOf(id);
    };

    /**
     * Reinitialize the context of members's search to display new results set
     */
    var resetSearchMember = function () {
      $scope.member.noMore = false;
      $scope.member.page = 1;
    };

    /**
     * Run a search query with the current parameters set ($scope.member[searchText,order,page])
     * and affect or append the result in $scope.members, depending on the concat parameter
     * @param [concat] {boolean} if true, the result will be append to $scope.members instead of being affected
     */
    var memberSearch = function (concat) {
      Member.list({
        query: {
          search: $scope.member.searchText,
          order_by: $scope.member.order,
          page: $scope.member.page,
          size: USERS_PER_PAGE
        }
      }, function (members) {
        if (concat) {
          $scope.members = $scope.members.concat(members);
        } else {
          $scope.members = members;
        }

        if (!members[0] || (members[0].maxMembers <= $scope.members.length)) {
          return $scope.member.noMore = true;
        }
      });
    };

    // !!! MUST BE CALLED AT THE END of the controller
    return initialize();
  }
]);

/**
 * Controller used in the member edition page
 */
Application.Controllers.controller('EditMemberController', ['$scope', '$state', '$stateParams', 'Member', 'Training', 'dialogs', 'growl', 'Group', 'Subscription', 'CSRF', 'memberPromise', 'tagsPromise', '$uibModal', 'Plan', '$filter', '_t', 'walletPromise', 'transactionsPromise', 'activeProviderPromise', 'Wallet',
  function ($scope, $state, $stateParams, Member, Training, dialogs, growl, Group, Subscription, CSRF, memberPromise, tagsPromise, $uibModal, Plan, $filter, _t, walletPromise, transactionsPromise, activeProviderPromise, Wallet) {
  /* PUBLIC SCOPE */

    // API URL where the form will be posted
    $scope.actionUrl = `/api/members/${$stateParams.id}`;

    // Form action on the above URL
    $scope.method = 'patch';

    // List of tags joinable with user
    $scope.tags = tagsPromise;

    // The user to edit
    $scope.user = memberPromise;

    // Should the password be modified?
    $scope.password = { change: false };

    // the user subscription
    if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) {
      $scope.subscription = $scope.user.subscription;
      $scope.subscription.expired_at = $scope.subscription.expired_at;
    } else {
      Plan.query({ group_id: $scope.user.group_id }, function (plans) {
        $scope.plans = plans;
        return Array.from($scope.plans).map(function (plan) {
          return (plan.nameToDisplay = $filter('humanReadablePlanName')(plan));
        });
      });
    }

    // Available trainings list
    $scope.trainings = [];

    // Profiles types (student/standard/...)
    $scope.groups = [];

    // the user wallet
    $scope.wallet = walletPromise;

    // user wallet transactions
    $scope.transactions = transactionsPromise;

    // used in wallet partial template to identify parent view
    $scope.view = 'member_edit';

    // current active authentication provider
    $scope.activeProvider = activeProviderPromise;

    /**
     * Open a modal dialog, allowing the admin to extend the current user's subscription (freely or not)
     * @param subscription {Object} User's subscription object
     * @param free {boolean} True if the extent is offered, false otherwise
     */
    $scope.updateSubscriptionModal = function (subscription, free) {
      const modalInstance = $uibModal.open({
        animation: true,
        templateUrl: '<%= asset_path "admin/subscriptions/expired_at_modal.html" %>',
        size: 'lg',
        controller: ['$scope', '$uibModalInstance', 'Subscription', function ($scope, $uibModalInstance, Subscription) {
          $scope.new_expired_at = angular.copy(subscription.expired_at);
          $scope.free = free;
          $scope.datePicker = {
            opened: false,
            format: Fablab.uibDateFormat,
            options: {
              startingDay: Fablab.weekStartingDay
            },
            minDate: new Date()
          };

          $scope.openDatePicker = function (ev) {
            ev.preventDefault();
            ev.stopPropagation();
            return $scope.datePicker.opened = true;
          };

          $scope.ok = function () {
            Subscription.update(
              { id: subscription.id },
              { subscription: { expired_at: $scope.new_expired_at, free } },
              function (_subscription) {
                growl.success(_t('you_successfully_changed_the_expiration_date_of_the_user_s_subscription'));
                return $uibModalInstance.close(_subscription);
              },
              function (error) { growl.error(_t('a_problem_occurred_while_saving_the_date')); }
            );
          };

          $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
        }]
      });
      // once the form was validated successfully ...
      return modalInstance.result.then(function (subscription) { $scope.subscription.expired_at = subscription.expired_at; });
    };

    /**
     * Open a modal dialog allowing the admin to set a subscription for the given user.
     * @param user {Object} User object, user currently reviewed, as recovered from GET /api/members/:id
     * @param plans {Array} List of plans, availables for the currently reviewed user, as recovered from GET /api/plans
     */
    $scope.createSubscriptionModal = function (user, plans) {
      const modalInstance = $uibModal.open({
        animation: true,
        templateUrl: '<%= asset_path "admin/subscriptions/create_modal.html" %>',
        size: 'lg',
        controller: ['$scope', '$uibModalInstance', 'Subscription', 'Group', function ($scope, $uibModalInstance, Subscription, Group) {
        // selected user
          $scope.user = user;

          // available plans for the selected user
          $scope.plans = plans;

          /**
           * Generate a string identifying the given plan by literal human-readable name
           * @param plan {Object} Plan object, as recovered from GET /api/plan/:id
           * @param groups {Array} List of Groups objects, as recovered from GET /api/groups
           * @param short {boolean} If true, the generated name will contains the group slug, otherwise the group full name
           * will be included.
           * @returns {String}
           */
          $scope.humanReadablePlanName = function (plan, groups, short) { return `${$filter('humanReadablePlanName')(plan, groups, short)}`; };

          /**
           * Modal dialog validation callback
           */
          $scope.ok = function () {
            $scope.subscription.user_id = user.id;
            return Subscription.save({ }, { subscription: $scope.subscription }, function (_subscription) {
              growl.success(_t('subscription_successfully_purchased'));
              $uibModalInstance.close(_subscription);
              return $state.reload();
            }
            , function (error) {
              growl.error(_t('a_problem_occurred_while_taking_the_subscription'));
              console.error(error);
            });
          };

          /**
           * Modal dialog cancellation callback
           */
          $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
        }]
      });
      // once the form was validated succesfully ...
      return modalInstance.result.then(function (subscription) { $scope.subscription = subscription; });
    };

    $scope.createWalletCreditModal = function (user, wallet) {
      const modalInstance = $uibModal.open({
        animation: true,
        templateUrl: '<%= asset_path "wallet/credit_modal.html" %>',
        controller: ['$scope', '$uibModalInstance', 'Wallet', function ($scope, $uibModalInstance, Wallet) {
        // default: do not generate a refund invoice
          $scope.generate_avoir = false;

          // date of the generated refund invoice
          $scope.avoir_date = null;

          // optional description shown on the refund invoice
          $scope.description = '';

          // default configuration for the avoir date selector widget
          $scope.datePicker = {
            format: Fablab.uibDateFormat,
            opened: false,
            options: {
              startingDay: Fablab.weekStartingDay
            }
          };

          /**
           * Callback to open/close the date picker
           */
          $scope.toggleDatePicker = function ($event) {
            $event.preventDefault();
            $event.stopPropagation();
            return $scope.datePicker.opened = !$scope.datePicker.opened;
          };

          /**
           * Modal dialog validation callback
           */
          $scope.ok = function () {
            Wallet.credit(
              { id: wallet.id },
              {
                amount: $scope.amount,
                avoir: $scope.generate_avoir,
                avoir_date: $scope.avoir_date,
                avoir_description: $scope.description
              },
              function (_wallet) {
                growl.success(_t('wallet_credit_successfully'));
                return $uibModalInstance.close(_wallet);
              },
              function (error) {
                growl.error(_t('a_problem_occurred_for_wallet_credit'));
                console.error(error);
              }
            );
          };

          /**
           * Modal dialog cancellation callback
           */
          $scope.cancel = function () { $uibModalInstance.dismiss('cancel'); };
        }
        ] });
      // once the form was validated succesfully ...
      return modalInstance.result.then(function (wallet) {
        $scope.wallet = wallet;
        return Wallet.transactions({ id: wallet.id }, function (transactions) { $scope.transactions = transactions; });
      });
    };

    /**
     * To use as callback in Array.prototype.filter to get only enabled plans
     */
    $scope.filterDisabledPlans = function (plan) { return !plan.disabled; };

    /* PRIVATE SCOPE */

    /**
     * Kind of constructor: these actions will be realized first when the controller is loaded
     */
    const initialize = function () {
      CSRF.setMetaTags();

      // init the birth date to JS object
      $scope.user.statistic_profile.birthday = moment($scope.user.statistic_profile.birthday).toDate();

      // the user subscription
      if (($scope.user.subscribed_plan != null) && ($scope.user.subscription != null)) {
        $scope.subscription = $scope.user.subscription;
        $scope.subscription.expired_at = $scope.subscription.expired_at;
      } else {
        Plan.query({ group_id: $scope.user.group_id }, function (plans) {
          $scope.plans = plans;
          return Array.from($scope.plans).map(function (plan) {
            return (plan.nameToDisplay = `${plan.base_name} - ${plan.interval}`);
          });
        });
      }

      // Using the MembersController
      return new MembersController($scope, $state, Group, Training);
    };

    // !!! MUST BE CALLED AT THE END of the controller
    return initialize();
  }
]);

/**
 * Controller used in the member's creation page (admin view)
 */
Application.Controllers.controller('NewMemberController', ['$scope', '$state', '$stateParams', 'Member', 'Training', 'Group', 'CSRF',
  function ($scope, $state, $stateParams, Member, Training, Group, CSRF) {
    CSRF.setMetaTags();

    /* PUBLIC SCOPE */

    // API URL where the form will be posted
    $scope.actionUrl = '/api/members';

    // Form action on the above URL
    $scope.method = 'post';

    // Should the password be set manually or generated?
    $scope.password = { change: false };

    // Default member's profile parameters
    $scope.user = {
      plan_interval: '',
      invoicing_profile: {},
      statistic_profile: {}
    };

    // Callback when the admin check/uncheck the box telling that the new user is an organization.
    // Disable or enable the organization fields in the form, accordingly
    $scope.toggleOrganization = function () {
      if ($scope.user.organization) {
        if (!$scope.user.invoicing_profile) { $scope.user.invoicing_profile = {}; }
        $scope.user.invoicing_profile.organization = {};
      } else {
        $scope.user.invoicing_profile.organization = undefined;
      }
    };

    // Using the MembersController
    return new MembersController($scope, $state, Group, Training);
  }
]);

/**
 * Controller used in the admin's creation page (admin view)
 */
Application.Controllers.controller('NewAdminController', ['$state', '$scope', 'Admin', 'growl', '_t', function ($state, $scope, Admin, growl, _t) {
  // default admin profile
  let getGender;
  $scope.admin = {
    statistic_profile_attributes: {
      gender: true
    },
    profile_attributes: {},
    invoicing_profile_attributes: {}
  };

  // Default parameters for AngularUI-Bootstrap datepicker
  $scope.datePicker = {
    format: Fablab.uibDateFormat,
    opened: false,
    options: {
      startingDay: Fablab.weekStartingDay
    }
  };

  /**
   * Shows the birth day datepicker
   * @param $event {Object} jQuery event object
   */
  $scope.openDatePicker = function ($event) { $scope.datePicker.opened = true; };

  /**
   * Send the new admin, currently stored in $scope.admin, to the server for database saving
   */
  $scope.saveAdmin = function () {
    Admin.save(
      {},
      { admin: $scope.admin },
      function () {
        growl.success(_t('administrator_successfully_created_he_will_receive_his_connection_directives_by_email', { GENDER: getGender($scope.admin) }, 'messageformat'));
        return $state.go('app.admin.members');
      }
      , function (error) {
        console.log(error);
      }
    );
  };

  /* PRIVATE SCOPE */

  /**
   * Return an enumerable meaningful string for the gender of the provider user
   * @param user {Object} Database user record
   * @return {string} 'male' or 'female'
   */
  return getGender = function (user) {
    if (user.statistic_profile_attributes) {
      if (user.statistic_profile_attributes.gender) { return 'male'; } else { return 'female'; }
    } else { return 'other'; }
  };
}

]);