'use strict'

Application.Controllers.controller "EventsController", ["$scope", "$state", 'Event', ($scope, $state, Event) ->



  ### PRIVATE STATIC CONSTANTS ###

  # Number of events added to the page when the user clicks on 'load next events'
  EVENTS_PER_PAGE = 12



  ### PUBLIC SCOPE ###

  ## The events displayed on the page
  $scope.events = []

  ## By default, the pagination mode is activated to limit the page size
  $scope.paginateActive = true

  ## The currently displayed page number
  $scope.page = 1

  ##
  # Adds EVENTS_PER_PAGE events to the bottom of the page, grouped by month
  ##
  $scope.loadMoreEvents = ->
    Event.query {page: $scope.page}, (data) ->
      $scope.events = $scope.events.concat data
      if data.length > 0
        $scope.paginateActive = false if ($scope.page-2)*EVENTS_PER_PAGE+data.length >= data[0].nb_total_events

        $scope.eventsGroupByMonth = _.groupBy($scope.events, (obj) ->
          _.map ['month', 'year'], (key, value) -> obj[key]
        )
        $scope.monthOrder = _.sortBy _.keys($scope.eventsGroupByMonth), (k)->
          monthYearArray = k.split(',')
          date = new Date()
          date.setMonth(monthYearArray[0])
          date.setYear(monthYearArray[1])
          return -date.getTime()
      else
        $scope.paginateActive = false
    $scope.page += 1



  ##
  # Callback to redirect the user to the specified event page
  # @param event {{id:number}}
  ##
  $scope.showEvent = (event) ->
    $state.go('app.public.events_show', {id: event.id})



  ### PRIVATE SCOPE ###

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



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







Application.Controllers.controller "ShowEventController", ["$scope", "$state", "$stateParams", "Event", '$uibModal', 'Member', 'Reservation', 'Price', 'CustomAsset', 'eventPromise', 'reducedAmountAlert', 'growl', '_t'
($scope, $state, $stateParams, Event, $uibModal, Member, Reservation, Price, CustomAsset, eventPromise, reducedAmountAlert, growl, _t) ->



  ### PUBLIC SCOPE ###
  $scope.reducedAmountAlert = reducedAmountAlert.setting.value

  ## reservations for the currently shown event
  $scope.reservations = []

  ## user to deal with
  $scope.ctrl =
      member: {}

  ## parameters for a new reservation
  $scope.reserve =
    nbPlaces: []
    nbReducedPlaces: []
    nbReservePlaces: 0
    nbReserveReducedPlaces: 0
    toReserve: false
    amountTotal : 0



  # get the details for the current event (event's id is recovered from the current URL)
  $scope.event = eventPromise



  ##
  # Callback to delete the provided event (admins only)
  # @param event {$resource} angular's Event $resource
  ##
  $scope.deleteEvent = (event) ->
    event.$delete ->
      $state.go('app.public.events_list')



  ##
  # Callback to call when the number of places change in the current booking
  ##
  $scope.changeNbPlaces = ->
    reste = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces
    $scope.reserve.nbReducedPlaces = [0..reste]
    $scope.computeEventAmount()



  ##
  # Callback to call when the number of discounted places change in the current booking
  ##
  $scope.changeNbReducedPlaces = ->
    reste = $scope.event.nb_free_places - $scope.reserve.nbReserveReducedPlaces
    $scope.reserve.nbPlaces = [0..reste]
    $scope.computeEventAmount()



  ##
  # Callback to reset the current reservation parameters
  # @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
  ##
  $scope.cancelReserve = (e)->
    e.preventDefault()
    resetEventReserve()



  ##
  # Callback to allow the user to set the details for his reservation
  ##
  $scope.reserveEvent = ->
    if $scope.event.nb_total_places > 0
      $scope.reserveSuccess = false
      if !$scope.isAuthenticated()
        $scope.login null, (user)->
          $scope.reserve.toReserve = !$scope.reserve.toReserve
          if user.role isnt 'admin'
            $scope.ctrl.member = user
          else
            Member.query (members) ->
              $scope.members = members
      else
        $scope.reserve.toReserve = !$scope.reserve.toReserve



  ##
  # Callback to deal with the reservations of the user selected in the dropdown list instead of the current user's
  # reservations. (admins only)
  ##
  $scope.updateMember = ->
    resetEventReserve()
    $scope.reserveSuccess = false
    if $scope.ctrl.member
      getReservations($scope.event.id, 'Event', $scope.ctrl.member.id)



  ##
  # Callback to trigger the payment process of the current reservation
  ##
  $scope.payEvent = ->

    # first, we check that a user was selected
    if Object.keys($scope.ctrl.member).length > 0
      reservation = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event)

      if $scope.currentUser.role isnt 'admin' and $scope.reserve.amountTotal > 0
        payByStripe(reservation)
      else
        if $scope.currentUser.role is 'admin' or $scope.reserve.amountTotal is 0
          payOnSite(reservation)
    else
      # otherwise we alert, this error musn't occur when the current user is not admin
      growl.error(_t('please_select_a_member_first'))



  ##
  # Callback to validate the booking of a free event
  ##
  $scope.validReserveEvent = ->
    reservation =
      user_id: $scope.ctrl.member.id
      reservable_id: $scope.event.id
      reservable_type: 'Event'
      slots_attributes: []
      nb_reserve_places: $scope.reserve.nbReservePlaces
      nb_reserve_reduced_places: $scope.reserve.nbReserveReducedPlaces
    reservation.slots_attributes.push
      start_at: $scope.event.start_date
      end_at: $scope.event.end_date
      availability_id: $scope.event.availability.id
    $scope.attempting = true
    Reservation.save reservation: reservation, (reservation) ->
      afterPayment(reservation)
      $scope.attempting = false
    , (response)->
      $scope.alerts = []
      $scope.alerts.push
        msg: response.data.card[0]
        type: 'danger'
      $scope.attempting = false



  ##
  # Callback to alter an already booked reservation date. A modal window will be opened to allow the user to choose
  # a new date for his reservation (if any available)
  # @param reservation {{id:number, reservable_id:number, nb_reserve_places:number, nb_reserve_reduced_places:number}}
  # @param e {Object} see https://docs.angularjs.org/guide/expression#-event-
  ##
  $scope.modifyReservation = (reservation, e)->
    e.preventDefault()
    e.stopPropagation()

    index = $scope.reservations.indexOf(reservation)
    $uibModal.open
      templateUrl: '<%= asset_path "events/modify_event_reservation_modal.html" %>'
      resolve:
        event: -> $scope.event
        reservation: -> reservation
      controller: ['$scope', '$uibModalInstance', 'event', 'reservation', 'Reservation', ($scope, $uibModalInstance, event, reservation, Reservation) ->
        # we copy the controller's resolved parameters into the scope
        $scope.event = event
        $scope.reservation = angular.copy reservation

        # set the reservable_id to the first available event
        for e in event.recurrence_events
          if e.nb_free_places > (reservation.nb_reserve_places + reservation.nb_reserve_reduced_places)
            $scope.reservation.reservable_id = e.id
            break

        # Callback to validate the new reservation's date
        $scope.ok = ->
          eventToPlace = null
          angular.forEach event.recurrence_events, (e)->
            if e.id is parseInt($scope.reservation.reservable_id, 10)
              eventToPlace = e
          $scope.reservation.slots[0].start_at = eventToPlace.start_date
          $scope.reservation.slots[0].end_at = eventToPlace.end_date
          $scope.reservation.slots[0].availability_id = eventToPlace.availability_id
          $scope.reservation.slots_attributes = $scope.reservation.slots
          $scope.attempting = true
          Reservation.update {id: reservation.id}, {reservation: $scope.reservation}, (reservation) ->
            $uibModalInstance.close(reservation)
            $scope.attempting = true
          , (response)->
            $scope.alerts = []
            angular.forEach response, (v, k)->
              angular.forEach v, (err)->
                $scope.alerts.push({msg: k+': '+err, type: 'danger'})
            $scope.attempting = false

        # Callback to cancel the modification
        $scope.cancel = ->
          $uibModalInstance.dismiss('cancel')
      ]
    .result['finally'](null).then (reservation)->
      $scope.reservations.splice(index, 1)
      $scope.event.nb_free_places = $scope.event.nb_free_places + reservation.nb_reserve_places + reservation.nb_reserve_reduced_places
      angular.forEach $scope.event.recurrence_events, (e)->
        if e.id is parseInt(reservation.reservable_id, 10)
          e.nb_free_places = e.nb_free_places - reservation.nb_reserve_places - reservation.nb_reserve_reduced_places



  ##
  # Checks if the provided reservation is able to be modified
  # @param reservation {{nb_reserve_places:number, nb_reserve_reduced_places:number}}
  ##
  $scope.reservationCanModify = (reservation)->
    isAble = false
    angular.forEach $scope.event.recurrence_events, (e)->
      isAble = true if e.nb_free_places > (reservation.nb_reserve_places + reservation.nb_reserve_reduced_places)
    isAble



  ##
  # Compute the total amount for the current reservation according to the previously set parameters
  # and assign the result in $scope.reserve.amountTotal
  ##
  $scope.computeEventAmount = ->
    # first we check that a user was selected
    if Object.keys($scope.ctrl.member).length > 0
      r = mkReservation($scope.ctrl.member, $scope.reserve, $scope.event)
      Price.compute {reservation: r}, (res) ->
        $scope.reserve.amountTotal = res.price
    else
      $scope.reserve.amountTotal = null



  ### PRIVATE SCOPE ###

  ##
  # Kind of constructor: these actions will be realized first when the controller is loaded
  ##
  initialize = ->
    # gather the current user or the list of users if the current user is an admin
    if $scope.currentUser
      if $scope.currentUser.role isnt 'admin'
        $scope.ctrl.member = $scope.currentUser
      else
        Member.query (members) ->
          $scope.members = members

    # check that the event's reduced rate is initialized
    if !$scope.event.reduced_amount
      $scope.event.reduced_amount = 0

    # initialize the "reserve" object with the event's data
    $scope.reserve.nbPlaces = [0..$scope.event.nb_free_places]
    $scope.reserve.nbReducedPlaces = [0..$scope.event.nb_free_places]

    # if non-admin, get the current user's reservations into $scope.reservations
    if $scope.currentUser
      getReservations($scope.event.id, 'Event', $scope.currentUser.id)



  ##
  # Retrieve the reservations for the couple event / user
  # @param reservable_id {number} the current event id
  # @param reservable_type {string} 'Event'
  # @param user_id {number} the user's id (current or managed)
  ##
  getReservations = (reservable_id, reservable_type, user_id)->
    Reservation.query(reservable_id: reservable_id, reservable_type: reservable_type, user_id: user_id).$promise.then (reservations)->
      $scope.reservations = reservations



  ##
  # Create an hash map implementing the Reservation specs
  # @param member {Object} User as retreived from the API: current user / selected user if current is admin
  # @param reserve {Object} Reservation parameters (places...)
  # @param event {Object} Current event (Atelier/Stage)
  # @return {{user_id:Number, reservable_id:Number, reservable_type:String, slots_attributes:Array<Object>, nb_reserve_places:Number, nb_reserve_reduced_places:Number}}
  ##
  mkReservation = (member, reserve, event) ->
    reservation =
      user_id: member.id
      reservable_id: event.id
      reservable_type: 'Event'
      slots_attributes: []
      nb_reserve_places: reserve.nbReservePlaces
      nb_reserve_reduced_places: reserve.nbReserveReducedPlaces

    reservation.slots_attributes.push
      start_at: event.start_date
      end_at: event.end_date
      availability_id: event.availability.id
      offered: event.offered || false

    reservation



  ##
  # Set the current reservation to the default values. This implies to reservation form to be hidden.
  ##
  resetEventReserve = ->
    if $scope.event
      $scope.reserve =
        nbPlaces: [0..$scope.event.nb_free_places]
        nbReducedPlaces: [0..$scope.event.nb_free_places]
        nbReservePlaces: 0
        nbReserveReducedPlaces: 0
        toReserve: false
        amountTotal : 0
      $scope.event.offered = false



  ##
  # Open a modal window which trigger the stripe payment process
  # @param reservation {Object} to book
  ##
  payByStripe = (reservation) ->
    $uibModal.open
      templateUrl: '<%= asset_path "stripe/payment_modal.html" %>'
      size: 'md'
      resolve:
        reservation: ->
          reservation
        price: ->
          Price.compute({reservation: reservation}).$promise
        cgv: ->
          CustomAsset.get({name: 'cgv-file'}).$promise
        objectToPay: ->
          eventToReserve: $scope.event
          reserve: $scope.reserve
          member: $scope.ctrl.member
      controller: ['$scope', '$uibModalInstance', '$state',  'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', ($scope, $uibModalInstance, $state,  reservation, price, cgv, Auth, Reservation, growl) ->
        # Price
        $scope.amount = price.price

        # CGV
        $scope.cgv = cgv.custom_asset

        # Reservation
        $scope.reservation = reservation

        # Callback for the stripe payment authorization
        $scope.payment = (status, response) ->
          if response.error
            growl.error(response.error.message)
          else
            $scope.attempting = true
            $scope.reservation.card_token = response.id
            Reservation.save reservation: $scope.reservation, (reservation) ->
              $uibModalInstance.close(reservation)
            , (response)->
              $scope.alerts = []
              $scope.alerts.push
                msg: response.data.card[0]
                type: 'danger'
              $scope.attempting = false
      ]
    .result['finally'](null).then (reservation)->
      afterPayment(reservation)



  ##
  # Open a modal window which trigger the local payment process
  # @param reservation {Object} to book
  ##
  payOnSite = (reservation) ->
    $uibModal.open
      templateUrl: '<%= asset_path "shared/valid_reservation_modal.html" %>'
      size: 'sm'
      resolve:
        reservation: ->
          reservation
        price: ->
          Price.compute({reservation: reservation}).$promise
      controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation) ->
        # Price
        $scope.amount = price.price

        # Reservation
        $scope.reservation = reservation

        # Button label
        if $scope.amount > 0
          $scope.validButtonName = _t('confirm_(payment_on_site)')
        else
          $scope.validButtonName = _t('confirm')

        # Callback to validate the payment
        $scope.ok = ->
          $scope.attempting = true
          Reservation.save reservation: $scope.reservation, (reservation) ->
            $uibModalInstance.close(reservation)
            $scope.attempting = true
          , (response)->
            $scope.alerts = []
            angular.forEach response, (v, k)->
              angular.forEach v, (err)->
                $scope.alerts.push
                    msg: k+': '+err
                    type: 'danger'
            $scope.attempting = false

        # Callback to cancel the payment
        $scope.cancel = ->
          $uibModalInstance.dismiss('cancel')
      ]
    .result['finally'](null).then (reservation)->
      afterPayment(reservation)



  ##
  # What to do after the payment was successful
  # @param resveration {Object} booked reservation
  ##
  afterPayment = (reservation)->
    $scope.event.nb_free_places = $scope.event.nb_free_places - reservation.nb_reserve_places - reservation.nb_reserve_reduced_places
    resetEventReserve()
    $scope.reserveSuccess = true
    $scope.reservations.push reservation
    if $scope.currentUser.role == 'admin'
      $scope.ctrl.member = null



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