'use strict'

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



  ### PUBLIC SCOPE ###

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

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

  ## List of categories for the events
  $scope.categories = categoriesPromise

  ## List of events themes
  $scope.themes = themesPromise

  ## List of age ranges
  $scope.ageRanges = ageRangesPromise

  ## Hide or show the 'load more' button
  $scope.noMoreResults = false

  ## Active filters for the events list
  $scope.filters =
    category_id: null
    theme_id: null
    age_range_id: null



  ##
  # Adds a resultset of events to the bottom of the page, grouped by month
  ##
  $scope.loadMoreEvents = ->
    Event.query Object.assign({page: $scope.page}, $scope.filters), (data) ->
      $scope.events = $scope.events.concat data
      groupEvents($scope.events)
      $scope.page += 1

      if (!data[0] || data[0].nb_total_events <= $scope.events.length)
        $scope.noMoreResults = true



  ##
  # 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})



  ##
  # Callback to refresh the events list according to the filters set
  ##
  $scope.filterEvents = ->
    # reinitialize results datasets
    $scope.page = 1
    $scope.eventsGroupByMonth = {}
    $scope.events = []
    $scope.monthOrder = []
    $scope.noMoreResults = false

    # run a search query
    Event.query Object.assign({page: $scope.page}, $scope.filters), (data) ->
      $scope.events = data
      groupEvents(data)
      $scope.page += 1

      if (!data[0] || data[0].nb_total_events <= $scope.events.length)
        $scope.noMoreResults = true



  ##
  # Test if the provided event occurs on a single day or on many days
  # @param event {{start_date:Date, end_date:Date}} Event object as retreived from the API
  # @return {boolean} false if the event occurs on many days
  ##
  $scope.onSingleDay = (event) ->
    moment(event.start_date).isSame(event.end_date, 'day')



  ### PRIVATE SCOPE ###

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



  ##
  # Group the provided events by month/year and concat them with existing results
  # Then compute the ordered list of months for the complete resultset.
  # Affect the resulting events groups in $scope.eventsGroupByMonth and the ordered month keys in $scope.monthOrder.
  # @param {Array} Events retrived from the API
  ##
  groupEvents = (events) ->
    if events.length > 0
      eventsGroupedByMonth = _.groupBy(events, (obj) ->
        _.map ['month', 'year'], (key, value) -> obj[key]
      )
      $scope.eventsGroupByMonth = Object.assign($scope.eventsGroupByMonth, eventsGroupedByMonth)

      monthsOrder = _.sortBy _.keys($scope.eventsGroupByMonth), (k)->
        monthYearArray = k.split(',')
        date = new Date()
        date.setMonth(monthYearArray[0])
        date.setYear(monthYearArray[1])
        return -date.getTime()

      $scope.monthOrder = monthsOrder



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







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



  ### PUBLIC SCOPE ###

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

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

  ## parameters for a new reservation
  $scope.reserve =
    nbPlaces:
        normal: []
    nbReservePlaces: 0
    tickets: {}
    toReserve: false
    amountTotal : 0
    totalNoCoupon: 0
    totalSeats: 0

  ## Discount coupon to apply to the basket, if any
  $scope.coupon =
    applied: null

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

  ## List of price categories for the events
  $scope.priceCategories = priceCategoriesPromise

  ## Global config: is the user authorized to change his bookings slots?
  $scope.enableBookingMove = (settingsPromise.booking_move_enable == "true")

  ## Global config: delay in hours before a booking while changing the booking slot is forbidden
  $scope.moveBookingDelay = parseInt(settingsPromise.booking_move_delay)

  ## Message displayed to the end user about rules that applies to events reservations
  $scope.eventExplicationsAlert = settingsPromise.event_explications_alert



  ##
  # 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 tickets to book changes in the current booking
  ##
  $scope.changeNbPlaces = ->
    # compute the total remaing places
    remain = $scope.event.nb_free_places - $scope.reserve.nbReservePlaces
    for ticket of $scope.reserve.tickets
      remain -= $scope.reserve.tickets[ticket]
    # we store the total number of seats booked, this is used to know if the 'pay' button must be shown
    $scope.reserve.totalSeats = $scope.event.nb_free_places - remain

    # update the availables seats for full price tickets
    fullPriceRemains = $scope.reserve.nbReservePlaces + remain
    $scope.reserve.nbPlaces.normal = [0..fullPriceRemains]

    # update the available seats for other prices tickets
    for key of $scope.reserve.nbPlaces
      if key != 'normal'
        priceRemain = $scope.reserve.tickets[key] + remain
        $scope.reserve.nbPlaces[key] = [0..priceRemain]

    # recompute the total price
    $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
        $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
      Member.get {id: $scope.ctrl.member.id}, (member) ->
        $scope.ctrl.member = 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)

      Wallet.getWalletByUser {user_id: $scope.ctrl.member.id}, (wallet) ->
        amountToPay = helpers.getAmountToPay($scope.reserve.amountTotal, wallet.amount)
        if $scope.currentUser.role isnt 'admin' and amountToPay > 0
          payByStripe(reservation)
        else
          if $scope.currentUser.role is 'admin' or amountToPay 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
      tickets_attributes: []
    # a single slot is used for events
    reservation.slots_attributes.push
      start_at: $scope.event.start_date
      end_at: $scope.event.end_date
      availability_id: $scope.event.availability.id
    # iterate over reservations per prices
    for price_id, seats of $scope.reserve.tickets
      reservation.tickets_attributes.push
        event_price_category_id: price_id
        booked: seats
    # set the attempting marker
    $scope.attempting = true
    # save the reservation to the API
    Reservation.save reservation: reservation, (reservation) ->
      # reservation successfull
      afterPayment(reservation)
      $scope.attempting = false
    , (response)->
      # reservation failed
      $scope.alerts = []
      $scope.alerts.push
        msg: response.data.card[0]
        type: 'danger'
      # unset the attempting marker
      $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}}
  # @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.total_booked_seats
            $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)->
      # remove the reservation from the user's reservations list for this event (occurrence)
      $scope.reservations.splice(index, 1)
      # add the number of places transfered (to the new date) to the total of free places for this event
      $scope.event.nb_free_places = $scope.event.nb_free_places + reservation.total_booked_seats
      # remove the number of places transfered from the total of free places of the receiving occurrance
      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.total_booked_seats



  ##
  # Checks if the provided reservation is able to be moved (date change)
  # @param reservation {{total_booked_seats:number}}
  ##
  $scope.reservationCanModify = (reservation)->
    slotStart = moment(reservation.slots[0].start_at)
    now = moment()

    isAble = false
    angular.forEach $scope.event.recurrence_events, (e)->
      isAble = true if e.nb_free_places >= reservation.total_booked_seats
    return (isAble and $scope.enableBookingMove and slotStart.diff(now, "hours") >= $scope.moveBookingDelay)



  ##
  # 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 mkRequestParams(r, $scope.coupon.applied), (res) ->
        $scope.reserve.amountTotal = res.price
        $scope.reserve.totalNoCoupon = res.price_without_coupon
    else
      $scope.reserve.amountTotal = null



  ##
  # Return the URL allowing to share the current project on the Facebook social network
  ##
  $scope.shareOnFacebook = ->
    'https://www.facebook.com/share.php?u='+$state.href('app.public.events_show', {id: $scope.event.id}, {absolute: true}).replace('#', '%23')



  ##
  # Return the URL allowing to share the current project on the Twitter social network
  ##
  $scope.shareOnTwitter = ->
    'https://twitter.com/intent/tweet?url='+encodeURIComponent($state.href('app.public.events_show', {id: $scope.event.id}, {absolute: true}))+'&text='+encodeURIComponent($scope.event.title)



  ##
  # Return the textual description of the conditions applyable to the given price's category
  # @param category_id {number} ID of the price's category
  ##
  $scope.getPriceCategoryConditions = (category_id) ->
    for cat in $scope.priceCategories
      if cat.id == category_id
        return cat.conditions



  ### PRIVATE SCOPE ###

  ##
  # Kind of constructor: these actions will be realized first when the controller is loaded
  ##
  initialize = ->
    # set the controlled user as the current user if the current user is not an admin
    if $scope.currentUser
      if $scope.currentUser.role isnt 'admin'
        $scope.ctrl.member = $scope.currentUser

    # initialize the "reserve" object with the event's data
    resetEventReserve()

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

    # watch when a coupon is applied to re-compute the total price
    $scope.$watch 'coupon.applied', (newValue, oldValue) ->
      unless newValue == null and oldValue == null
        $scope.computeEventAmount()


  ##
  # 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
  # @return {{user_id:number, reservable_id:number, reservable_type:string, slots_attributes:Array<Object>, nb_reserve_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
      tickets_attributes: []

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

    for evt_px_cat in event.prices
      booked = reserve.tickets[evt_px_cat.id]
      if booked > 0
        reservation.tickets_attributes.push
          event_price_category_id: evt_px_cat.id
          booked: booked

    reservation



  ##
  # Format the parameters expected by /api/prices/compute or /api/reservations and return the resulting object
  # @param reservation {Object} as returned by mkReservation()
  # @param coupon {Object} Coupon as returned from the API
  # @return {{reservation:Object, coupon_code:string}}
  ##
  mkRequestParams = (reservation, coupon) ->
    params =
      reservation: reservation
      coupon_code: (coupon.code if coupon)

    params


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

      for evt_px_cat in $scope.event.prices
        $scope.reserve.nbPlaces[evt_px_cat.id] = [0..$scope.event.nb_free_places]
        $scope.reserve.tickets[evt_px_cat.id] = 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(mkRequestParams(reservation, $scope.coupon.applied)).$promise
        wallet: ->
          Wallet.getWalletByUser({user_id: reservation.user_id}).$promise
        cgv: ->
          CustomAsset.get({name: 'cgv-file'}).$promise
        objectToPay: ->
          eventToReserve: $scope.event
          reserve: $scope.reserve
          member: $scope.ctrl.member
        coupon: ->
          $scope.coupon.applied
      controller: ['$scope', '$uibModalInstance', '$state',  'reservation', 'price', 'cgv', 'Auth', 'Reservation', 'growl', 'wallet', 'helpers', '$filter', 'coupon',
      ($scope, $uibModalInstance, $state,  reservation, price, cgv, Auth, Reservation, growl, wallet, helpers, $filter, coupon) ->
        # User's wallet amount
        $scope.walletAmount = wallet.amount

        # Price
        $scope.amount = helpers.getAmountToPay(price.price, wallet.amount)

        # CGV
        $scope.cgv = cgv.custom_asset

        # Reservation
        $scope.reservation = reservation

        # Used in wallet info template to interpolate some translations
        $scope.numberFilter = $filter('number')

        # 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 mkRequestParams($scope.reservation, coupon), (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(mkRequestParams(reservation, $scope.coupon.applied)).$promise
        wallet: ->
          Wallet.getWalletByUser({user_id: reservation.user_id}).$promise
        coupon: ->
          $scope.coupon.applied
      controller: ['$scope', '$uibModalInstance', '$state', 'reservation', 'price', 'Auth', 'Reservation', 'wallet', 'helpers', '$filter', 'coupon',
      ($scope, $uibModalInstance, $state, reservation, price, Auth, Reservation, wallet, helpers, $filter, coupon) ->
        # User's wallet amount
        $scope.walletAmount = wallet.amount

        # Price
        $scope.price = price.price

        # price to pay
        $scope.amount = helpers.getAmountToPay(price.price, wallet.amount)

        # Reservation
        $scope.reservation = reservation

        # Used in wallet info template to interpolate some translations
        $scope.numberFilter = $filter('number')

        # Button label
        if $scope.amount > 0
          $scope.validButtonName = _t('confirm_payment_of_html', {ROLE:$scope.currentUser.role, AMOUNT:$filter('currency')($scope.amount)}, "messageformat")
        else
          if price.price > 0 and $scope.walletAmount == 0
            $scope.validButtonName = _t('confirm_payment_of_html', {ROLE:$scope.currentUser.role, AMOUNT:$filter('currency')(price.price)}, "messageformat")
          else
            $scope.validButtonName = _t('confirm')

        # Callback to validate the payment
        $scope.ok = ->
          $scope.attempting = true
          Reservation.save mkRequestParams($scope.reservation, coupon), (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.total_booked_seats
    resetEventReserve()
    $scope.reserveSuccess = true
    $scope.coupon.applied = null
    $scope.reservations.push reservation
    if $scope.currentUser.role == 'admin'
      $scope.ctrl.member = null



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