'use strict'

##
# Controller used in the admin invoices listing page
##
Application.Controllers.controller "InvoicesController", ["$scope", "$state", 'Invoice', '$uibModal', "growl", "$filter", 'Setting', 'settings', '_t'
, ($scope, $state, Invoice, $uibModal, growl, $filter, Setting, settings, _t) ->



  ### PUBLIC SCOPE ###

  ## List of all users invoices
  $scope.invoices = Invoice.query()

  ## Default invoices ordering/sorting
  $scope.orderInvoice = '-reference'

  ## Invoices parameters
  $scope.invoice =
    logo: null
    reference:
      model: ''
      help: null
      templateUrl: 'editReference.html'
    code:
      model: ''
      active: true
      templateUrl: 'editCode.html'
    number:
      model: ''
      help: null
      templateUrl: 'editNumber.html'
    VAT:
      rate: 19.6
      active: false
      templateUrl: 'editVAT.html'
    text:
      content: ''
    legals:
      content: ''

  ## Placeholding date for the invoice creation
  $scope.today = moment()

  ## Placeholding date for the reservation begin
  $scope.inOneWeek = moment().add(1, 'week').startOf('hour')

  ## Placeholding date for the reservation end
  $scope.inOneWeekAndOneHour = moment().add(1, 'week').add(1, 'hour').startOf('hour')



  ##
  # Change the invoices ordering criterion to the one provided
  # @param orderBy {string} ordering criterion
  ##
  $scope.setOrderInvoice = (orderBy)->
    if $scope.orderInvoice == orderBy
      $scope.orderInvoice = '-'+orderBy
    else
      $scope.orderInvoice = orderBy



  ##
  # Open a modal window asking the admin the details to refund the user about the provided invoice
  # @param invoice {Object} invoice inherited from angular's $resource
  ##
  $scope.generateAvoirForInvoice = (invoice)->
    # open modal
    modalInstance = $uibModal.open
      templateUrl: '<%= asset_path "admin/invoices/avoirModal.html" %>'
      controller: 'AvoirModalController'
      resolve:
        invoice: -> invoice

    # once done, update the invoice model and inform the admin
    modalInstance.result.then (res) ->
      $scope.invoices.unshift res.avoir
      Invoice.get {id: invoice.id}, (data) ->
        invoice.has_avoir = data.has_avoir
        growl.success(_t('refund_invoice_successfully_created'))



  ##
  # Generate an invoice reference sample from the parametrized model
  # @returns {string} invoice reference sample
  ##
  $scope.mkReference = ->
    sample = $scope.invoice.reference.model
    if sample
      # invoice number per day (dd..dd)
      sample = sample.replace(/d+(?![^\[]*])/g, (match, offset, string) ->
        padWithZeros(2, match.length)
      )
      # invoice number per month (mm..mm)
      sample = sample.replace(/m+(?![^\[]*])/g, (match, offset, string) ->
        padWithZeros(12, match.length)
      )
      # invoice number per year (yy..yy)
      sample = sample.replace(/y+(?![^\[]*])/g, (match, offset, string) ->
        padWithZeros(8, match.length)
      )
      # date informations
      sample = sample.replace(/[YMD]+(?![^\[]*])/g, (match, offset, string) ->
        $scope.today.format(match)
      )
      # information about online selling (X[text])
      sample = sample.replace(/X\[([^\]]+)\]/g, (match, p1, offset, string) ->
        p1
      )
      # information about refunds (R[text]) - does not apply here
      sample = sample.replace(/R\[([^\]]+)\]/g, "")
    sample


  ##
  # Generate an order nmuber sample from the parametrized model
  # @returns {string} invoice reference sample
  ##
  $scope.mkNumber = ->
    sample = $scope.invoice.number.model
    if sample
      # global order number (nn..nn)
      sample = sample.replace(/n+(?![^\[]*])/g, (match, offset, string) ->
        padWithZeros(327, match.length)
      )
      # order number per year (yy..yy)
      sample = sample.replace(/y+(?![^\[]*])/g, (match, offset, string) ->
        padWithZeros(8, match.length)
      )
      # order number per month (mm..mm)
      sample = sample.replace(/m+(?![^\[]*])/g, (match, offset, string) ->
        padWithZeros(12, match.length)
      )
      # order number per day (dd..dd)
      sample = sample.replace(/d+(?![^\[]*])/g, (match, offset, string) ->
        padWithZeros(2, match.length)
      )
      # date informations
      sample = sample.replace(/[YMD]+(?![^\[]*])/g, (match, offset, string) ->
        $scope.today.format(match)
      )
    sample



  ##
  # Open a modal dialog allowing the user to edit the invoice reference generation template
  ##
  $scope.openEditReference = ->
    modalInstance = $uibModal.open
      animation: true,
      templateUrl: $scope.invoice.reference.templateUrl,
      size: 'lg',
      resolve:
        model: ->
          $scope.invoice.reference.model
      controller: ($scope, $uibModalInstance, model) ->
        $scope.model = model
        $scope.ok = ->
          $uibModalInstance.close($scope.model)
        $scope.cancel = ->
          $uibModalInstance.dismiss('cancel')

    modalInstance.result.then (model) ->
      Setting.update { name: 'invoice_reference' }, { value: model }, (data)->
        $scope.invoice.reference.model = model
        growl.success(_t('invoice_reference_successfully_saved'))
      , (error)->
        growl.error(_t('an_error_occurred_while_saving_invoice_reference'))
        console.error(error)




  ##
  # Open a modal dialog allowing the user to edit the invoice code
  ##
  $scope.openEditCode = ->
    modalInstance = $uibModal.open
      animation: true,
      templateUrl: $scope.invoice.code.templateUrl,
      size: 'lg',
      resolve:
        model: ->
          $scope.invoice.code.model
        active: ->
          $scope.invoice.code.active
      controller: ($scope, $uibModalInstance, model, active) ->
        $scope.codeModel = model
        $scope.isSelected = active


        $scope.ok = ->
          $uibModalInstance.close({model: $scope.codeModel, active: $scope.isSelected})
        $scope.cancel = ->
          $uibModalInstance.dismiss('cancel')

    modalInstance.result.then (result) ->
      Setting.update { name: 'invoice_code-value' }, { value: result.model }, (data)->
        $scope.invoice.code.model = result.model
        if result.active
          growl.success(_t('invoicing_code_succesfully_saved'))
      , (error)->
        growl.error(_t('an_error_occurred_while_saving_the_invoicing_code'))
        console.error(error)

      Setting.update { name: 'invoice_code-active' }, { value: if result.active then "true" else "false" }, (data)->
        $scope.invoice.code.active = result.active
        if result.active
          growl.success(_t('code_successfully_activated'))
        else
          growl.success(_t('code_successfully_disabled'))
      , (error)->
        growl.error(_t('an_error_occurred_while_activating_the_invoicing_code'))
        console.error(error)




  ##
  # Open a modal dialog allowing the user to edit the invoice number
  ##
  $scope.openEditInvoiceNb = ->
    modalInstance = $uibModal.open
      animation: true,
      templateUrl: $scope.invoice.number.templateUrl,
      size: 'lg',
      resolve:
        model: ->
          $scope.invoice.number.model
      controller: ($scope, $uibModalInstance, model) ->
        $scope.model = model
        $scope.ok = ->
          $uibModalInstance.close($scope.model)
        $scope.cancel = ->
          $uibModalInstance.dismiss('cancel')

    modalInstance.result.then (model) ->
      Setting.update { name: 'invoice_order-nb' }, { value: model }, (data)->
        $scope.invoice.number.model = model
        growl.success(_t('order_number_successfully_saved'))
      , (error)->
        growl.error(_t('an_error_occurred_while_saving_the_order_number'))
        console.error(error)




  ##
  # Open a modal dialog allowing the user to edit the VAT parameters for the invoices
  # The VAT can be disabled and its rate can be configured
  ##
  $scope.openEditVAT = ->
    modalInstance = $uibModal.open
      animation: true,
      templateUrl: $scope.invoice.VAT.templateUrl,
      size: 'lg',
      resolve:
        rate: ->
          $scope.invoice.VAT.rate
        active: ->
          $scope.invoice.VAT.active
      controller: ($scope, $uibModalInstance, rate, active) ->
        $scope.rate = rate
        $scope.isSelected = active


        $scope.ok = ->
          $uibModalInstance.close({rate: $scope.rate, active: $scope.isSelected})
        $scope.cancel = ->
          $uibModalInstance.dismiss('cancel')

    modalInstance.result.then (result) ->
      Setting.update { name: 'invoice_VAT-rate' }, { value: result.rate+"" }, (data)->
        $scope.invoice.VAT.rate = result.rate
        if result.active
          growl.success(_t('VAT_rate_successfully_saved'))
      , (error)->
        growl.error(_t('an_error_occurred_while_saving_the_VAT_rate'))
        console.error(error)

      Setting.update { name: 'invoice_VAT-active' }, { value: if result.active then "true" else "false" }, (data)->
        $scope.invoice.VAT.active = result.active
        if result.active
          growl.success(_t('VAT_successfully_activated'))
        else
          growl.success(_t('VAT_successfully_disabled'))
      , (error)->
        growl.error(_t('an_error_occurred_while_activating_the_VAT'))
        console.error(error)



  ##
  # Callback to save the value of the text zone when editing is done
  ##
  $scope.textEditEnd = (event) ->
    parsed = parseHtml($scope.invoice.text.content)
    Setting.update { name: 'invoice_text' }, { value: parsed }, (data)->
      $scope.invoice.text.content = parsed
      growl.success(_t('text_successfully_saved'))
    , (error)->
      growl.error(_t('an_error_occurred_while_saving_the_text'))
      console.error(error)



  ##
  # Callback to save the value of the legal informations zone when editing is done
  ##
  $scope.legalsEditEnd = (event) ->
    parsed = parseHtml($scope.invoice.legals.content)
    Setting.update { name: 'invoice_legals' }, { value: parsed }, (data)->
      $scope.invoice.legals.content = parsed
      growl.success(_t('address_and_legal_information_successfully_saved'))
    , (error)->
      growl.error(_t('an_error_occurred_while_saving_the_address_and_the_legal_information'))
      console.error(error)



  ### PRIVATE SCOPE ###

  ##
  # Kind of constructor: these actions will be realized first when the controller is loaded
  ##
  initialize = ->
    # retrieve settings from the DB through the API
    $scope.invoice.legals.content = settings['invoice_legals']
    $scope.invoice.text.content = settings['invoice_text']
    $scope.invoice.VAT.rate = parseFloat(settings['invoice_VAT-rate'])
    $scope.invoice.VAT.active = (settings['invoice_VAT-active'] == "true")
    $scope.invoice.number.model = settings['invoice_order-nb']
    $scope.invoice.code.model = settings['invoice_code-value']
    $scope.invoice.code.active = (settings['invoice_code-active'] == "true")
    $scope.invoice.reference.model = settings['invoice_reference']
    $scope.invoice.logo =
      filetype: 'image/png'
      filename: 'logo.png'
      base64: settings['invoice_logo']

    # Watch the logo, when a change occurs, save it
    $scope.$watch 'invoice.logo', ->
      if $scope.invoice.logo and $scope.invoice.logo.filesize
        Setting.update { name: 'invoice_logo' }, { value: $scope.invoice.logo.base64 }, (data)->
          growl.success(_t('logo_successfully_saved'))
        , (error)->
          growl.error(_t('an_error_occurred_while_saving_the_logo'))
          console.error(error)



  ##
  # Output the given integer with leading zeros. If the given value is longer than the given
  # length, it will be truncated.
  # @param value {number} the integer to pad
  # @param length {number} the length of the resulting string.
  ##
  padWithZeros = (value, length) ->
    (1e15+value+"").slice(-length)



  ##
  # Remove every unsupported html tag from the given html text (like <p>, <span>, ...).
  # The supported tags are <b>, <u>, <i> and <br>.
  # @param html {string} single line html text
  # @return {string} multi line simplified html text
  ##
  parseHtml = (html) ->
    html = html.replace(/<\/?(\w+)((\s+\w+(\s*=\s*(?:".*?"|'.*?'|[^'">\s]+))?)+\s*|\s*)\/?>/g, (match, p1, offset, string) ->
      if p1 in ['b', 'u', 'i', 'br']
        match
      else
        ''
    )



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



##
# Controller used in the invoice refunding modal window
##
Application.Controllers.controller 'AvoirModalController', ["$scope", "$uibModalInstance", "invoice", "Invoice", "growl", '_t'
, ($scope, $uibModalInstance, invoice, Invoice, growl, _t) ->



  ### PUBLIC SCOPE ###

  ## invoice linked to the current refund
  $scope.invoice = invoice

  ## Associative array containing invoice_item ids associated with boolean values
  $scope.partial = {}

  ## Default refund parameters
  $scope.avoir =
    invoice_id: invoice.id
    subscription_to_expire: false
    invoice_items_ids: []

  ## Possible refunding methods
  $scope.avoirModes = [
    {name: _t('none'), value: 'none'}
    {name: _t('by_cash'), value: 'cash'}
    {name: _t('by_cheque'), value: 'cheque'}
    {name: _t('by_transfer'), value: 'transfer'}
  ]

  ## If a subscription was took with the current invoice, should it be canceled or not
  $scope.subscriptionExpireOptions = {}
  $scope.subscriptionExpireOptions[_t('yes')] = true
  $scope.subscriptionExpireOptions[_t('no')] = false

  ## AngularUI-Bootstrap datepicker parameters to define when to refund
  $scope.datePicker =
    format: Fablab.uibDateFormat
    opened: false # default: datePicker is not shown
    options:
      startingDay: Fablab.weekStartingDay



  ##
  # Callback to open the datepicker
  ##
  $scope.openDatePicker = ($event) ->
    $event.preventDefault()
    $event.stopPropagation()
    $scope.datePicker.opened = true



  ##
  # Validate the refunding and generate a refund invoice
  ##
  $scope.ok = ->
    # check that at least 1 element of the invoice is refunded
    $scope.avoir.invoice_items_ids = []
    for itemId, refundItem of $scope.partial
      $scope.avoir.invoice_items_ids.push(parseInt(itemId)) if refundItem

    if $scope.avoir.invoice_items_ids.length is 0
      growl.error(_t('you_must_select_at_least_one_element_to_create_a_refund'))
    else
      Invoice.save {avoir: $scope.avoir}, (avoir) ->
        # success
        $uibModalInstance.close({avoir:avoir, invoice:$scope.invoice})
      , (err) ->
        # failed
        growl.error(_t('unable_to_create_the_refund'))



  ##
  # Cancel the refund, dismiss the modal window
  ##
  $scope.cancel = ->
    $uibModalInstance.dismiss('cancel')



  ### PRIVATE SCOPE ###

  ##
  # Kind of constructor: these actions will be realized first when the controller is loaded
  ##
  initialize = ->
    ## if the invoice was payed with stripe, allow to refund through stripe
    Invoice.get {id: invoice.id}, (data) ->
      $scope.invoice = data
      # default : all elements of the invoice are refund
      for item in data.items
        $scope.partial[item.id] = (typeof item.avoir_item_id isnt 'number')

    if invoice.stripe
      $scope.avoirModes.push {name: _t('online_payment'), value: 'stripe'}


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