'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 (groups) ->
      $scope.groups = groups

    ## Retrieve the list the available trainings
    Training.query().$promise.then (data)->
      $scope.trainings = data.map (d) ->
        id: d.id
        name: d.name

    ## 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 = ($event) ->
      $event.preventDefault()
      $event.stopPropagation()
      $scope.datePicker.opened = true



    ##
    # Shows the end of subscription datepicker
    # @param $event {Object} jQuery event object
    ##
    $scope.openSubscriptionDatePicker = ($event) ->
      $event.preventDefault()
      $event.stopPropagation()
      $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 = (content) ->
      if !content.id?
        $scope.alerts = []
        angular.forEach content, (v, k)->
          angular.forEach v, (err)->
            $scope.alerts.push
              msg: k+': '+err,
              type: 'danger'
      else
        $state.go('app.admin.members')



    ##
    # Changes the admin's view to the members list page
    ##
    $scope.cancel = ->
      $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 = (v)->
      if v
        'fileinput-exists'
      else
        'fileinput-new'


##
# Controller used in the members/groups management page
##
Application.Controllers.controller "AdminMembersController", ["$scope","$sce", 'membersPromise', 'adminsPromise', 'growl', 'Admin', 'dialogs', '_t', 'Member', 'Export'
, ($scope, $sce, membersPromise, adminsPromise, growl, Admin, dialogs, _t, Member, Export) ->



  ### PRIVATE STATIC CONSTANTS ###

  # number of users loaded each time we click on 'load more...'
  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

  ## 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 = (orderBy)->
    if $scope.member.order == orderBy
      $scope.member.order = '-'+orderBy
    else
      $scope.member.order = orderBy

    resetSearchMember()
    memberSearch()



  ##
  # Change the admins ordering criterion to the one provided
  # @param orderBy {string} ordering criterion
  ##
  $scope.setOrderAdmin = (orderAdmin)->
    if $scope.orderAdmin == orderAdmin
      $scope.orderAdmin = '-'+orderAdmin
    else
      $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 = (admins, admin)->
    dialogs.confirm
      resolve:
        object: ->
          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'))
    , -> # cancel confirmed
      Admin.delete id: admin.id, ->
        admins.splice(findAdminIdxById(admins, admin.id), 1)
        growl.success(_t('administrator_successfully_deleted'))
      , (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 = ->
    $scope.member.page += 1
    memberSearch(true)



  ##
  # Callback when the search field content changes: reload the search results
  ##
  $scope.updateTextSearch = ->
    resetSearchMember()
    memberSearch()

  ##
  # Callback to alert the admin that the export request was acknowledged and is
  # processing right now.
  ##
  $scope.alertExport = (type) ->
    Export.status({category: 'users', type: type}).then (res) ->
      unless (res.data.exists)
        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
  ##
  initialize = ->
    if (!membersPromise[0] || membersPromise[0].maxMembers <= $scope.members.length)
      $scope.member.noMore = true

  ##
  # 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
  ##
  findAdminIdxById = (admins, id)->
    (admins.map (admin)->
      admin.id
    ).indexOf(id)



  ##
  # Reinitialize the context of members's search to display new results set
  ##
  resetSearchMember = ->
    $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
  ##
  memberSearch = (concat) ->
    Member.list { query: { search: $scope.member.searchText, order_by: $scope.member.order, page: $scope.member.page, size: USERS_PER_PAGE } }, (members) ->
      if concat
        $scope.members = $scope.members.concat(members)
      else
        $scope.members = members;

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

  ## !!! MUST BE CALLED AT THE END of the controller
  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'
, ($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 associables with user
  $scope.tags = tagsPromise

  ## The user to edit
  $scope.user = memberPromise

  ## Should the passord be modified?
  $scope.password =
    change: false

  ## the user subscription
  if $scope.user.subscribed_plan? and $scope.user.subscription?
    $scope.subscription = $scope.user.subscription
    $scope.subscription.expired_at = $scope.subscription.expired_at
  else
    Plan.query group_id: $scope.user.group_id, (plans)->
      $scope.plans = plans
      for plan in $scope.plans
        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 = (subscription, free)->
    modalInstance = $uibModal.open
      animation: true,
      templateUrl: '<%= asset_path "admin/subscriptions/expired_at_modal.html" %>'
      size: 'lg',
      controller: ['$scope', '$uibModalInstance', 'Subscription', ($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 = (ev)->
          ev.preventDefault();
          ev.stopPropagation();
          $scope.datePicker.opened = true


        $scope.ok = ->
          Subscription.update { id: subscription.id }, { subscription: { expired_at: $scope.new_expired_at, free: free } }, (_subscription)->
            growl.success(_t('you_successfully_changed_the_expiration_date_of_the_user_s_subscription'))
            $uibModalInstance.close(_subscription)
          , (error)->
            growl.error(_t('a_problem_occurred_while_saving_the_date'))
        $scope.cancel = ->
          $uibModalInstance.dismiss('cancel')
      ]
    # once the form was validated succesfully ...
    modalInstance.result.then (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 = (user, plans)->
    modalInstance = $uibModal.open
      animation: true,
      templateUrl: '<%= asset_path "admin/subscriptions/create_modal.html" %>'
      size: 'lg',
      controller: ['$scope', '$uibModalInstance', 'Subscription', 'Group', ($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 humain-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 = (plan, groups, short)->
          "#{$filter('humanReadablePlanName')(plan, groups, short)}"

        ##
        # Modal dialog validation callback
        ##
        $scope.ok = ->
          $scope.subscription.user_id = user.id
          Subscription.save { }, { subscription: $scope.subscription }, (_subscription)->

            growl.success(_t('subscription_successfully_purchased'))
            $uibModalInstance.close(_subscription)
            $state.reload()
          , (error)->
            growl.error(_t('a_problem_occurred_while_taking_the_subscription'))

        ##
        # Modal dialog cancellation callback
        ##
        $scope.cancel = ->
          $uibModalInstance.dismiss('cancel')
      ]
    # once the form was validated succesfully ...
    modalInstance.result.then (subscription) ->
      $scope.subscription = subscription


  $scope.createWalletCreditModal = (user, wallet)->
    modalInstance = $uibModal.open
      animation: true,
      templateUrl: '<%= asset_path "wallet/credit_modal.html" %>'
      controller: ['$scope', '$uibModalInstance', 'Wallet', ($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 = ($event) ->
          $event.preventDefault()
          $event.stopPropagation()
          $scope.datePicker.opened = !$scope.datePicker.opened

        ##
        # Modal dialog validation callback
        ##
        $scope.ok = ->
          Wallet.credit { id: wallet.id },
            amount: $scope.amount
            avoir: $scope.generate_avoir
            avoir_date: $scope.avoir_date
            avoir_description: $scope.description
          , (_wallet)->

            growl.success(_t('wallet_credit_successfully'))
            $uibModalInstance.close(_wallet)
          , (error)->
            growl.error(_t('a_problem_occurred_for_wallet_credit'))

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



  ### PRIVATE SCOPE ###



  ##
  # Kind of constructor: these actions will be realized first when the controller is loaded
  ##
  initialize = ->
    CSRF.setMetaTags()

    # init the birth date to JS object
    $scope.user.profile.birthday = moment($scope.user.profile.birthday).toDate()

    ## the user subscription
    if $scope.user.subscribed_plan? and $scope.user.subscription?
      $scope.subscription = $scope.user.subscription
      $scope.subscription.expired_at = $scope.subscription.expired_at
    else
      Plan.query group_id: $scope.user.group_id, (plans)->
        $scope.plans = plans
        for plan in $scope.plans
          plan.nameToDisplay = "#{plan.base_name} - #{plan.interval}"

    # Using the MembersController
    new MembersController($scope, $state, Group, Training)



  ## !!! MUST BE CALLED AT THE END of the controller
  initialize()
]



##
# Controller used in the member's creation page (admin view)
##
Application.Controllers.controller "NewMemberController", ["$scope", "$state", "$stateParams", "Member", 'Training', 'Group', 'CSRF'
, ($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 passord be set manually or generated?
  $scope.password =
    change: false

  ## Default member's profile parameters
  $scope.user =
    plan_interval: ''

  ## Callback when the admin check/unckeck the box telling that the new user is an organization.
  ## Disable or enable the organization fields in the form, accordingly
  $scope.toggleOrganization = ->
    if $scope.user.organization
      $scope.user.profile = {} unless $scope.user.profile
      $scope.user.profile.organization = {}
    else
      $scope.user.profile.organization = undefined



  ## Using the MembersController
  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', ($state, $scope, Admin, growl, _t)->

  ## default admin profile
  $scope.admin =
    profile_attributes:
      gender: true

  ## 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 = ($event)->
    $scope.datePicker.opened = true



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



  ### PRIVATE SCOPE ###

  ##
  # Return an enumerable meaninful string for the gender of the provider user
  # @param user {Object} Database user record
  # @return {string} 'male' or 'female'
  ##
  getGender = (user) ->
    if user.profile_attributes
      if user.profile_attributes.gender then 'male' else 'female'
    else 'other'


]