'use strict'

###*
  @ngdoc factory
  @name payrollhero.api.ScheduleEventsService

  @factory
  @description Schedule Event Service
###

angular.module('payrollhero.api').factory 'ScheduleEventsService', (ScheduleEvent, PhRestangularV4, $q,
                                                                    TimeHelper, $rootScope, RestangularObjectCache) ->
  PhRestangularV4.extendModel 'schedule_events', (model) ->
    _.defaults(new ScheduleEvent(),model)

  service = PhRestangularV4.service('schedule_events')

  RestangularObjectCache.track 'schedule_events', service: PhRestangularV4
  RestangularObjectCache.index 'schedule_events', 'employee_id'
  RestangularObjectCache.index 'schedule_events', 'worksite_id'
  RestangularObjectCache.defineRelationships 'schedule_events', (relationships) ->
    relationships.belongsTo 'worksite', key: 'shift_worksite_id'
    relationships.belongsTo 'employee', key: 'employee_id'
    relationships.belongsTo 'multiplier', key: 'day_event_day_type_id'

  transformBreaks = (breaks, days, tz) ->
    _(breaks).map (theBreak) ->
      {
        start_time: TimeHelper.moveAndPutInTz(days, theBreak.start, tz).toISOString()
        end_time: TimeHelper.moveAndPutInTz(days, theBreak.end, tz).toISOString()
        kind: theBreak.kind
      }

  createException = (day, exceptionInfo) ->
    tz = exceptionInfo.tz
    dayDiff = TimeHelper.differenceInWholeDays(day, exceptionInfo.start)
    event = {
      kind: 'single_shift'
      employee_id: exceptionInfo.employeeId || exceptionInfo.employee_id
      shift_worksite_id: exceptionInfo.worksiteId || exceptionInfo.worksite_id
      is_rest_day: false
      shift_start_time: TimeHelper.moveAndPutInTz(dayDiff, exceptionInfo.start, tz).toISOString()
      shift_end_time: TimeHelper.moveAndPutInTz(dayDiff, exceptionInfo.end, tz).toISOString()
      shift_breaks: transformBreaks(exceptionInfo.breaks, dayDiff, tz)
      recurrence_info: {type: 'none'}
    }
    service.post({schedule_event: event})

  createWholeDayEvent = (exceptionInfo) ->
    event = {
      kind: exceptionInfo.kind,
      event_date: exceptionInfo.date,
    }

  overrideSecondaryDayTypesOnDate = (day, scheduleEvents) ->
    secondaryDayTypeEvents = _(scheduleEvents).select (scheduleEvent) -> scheduleEvent.isRestDay()
    $q.all(overridePromiseFor scheduleEvent, day for scheduleEvent in secondaryDayTypeEvents)

  overrideSchedulesOnDate = (day, scheduleEvents, options = { overridesSecondaryDayType: false }) ->
    overridableEvents = if !options.overridesSecondaryDayType
                          _(scheduleEvents).reject (scheduleEvent) -> scheduleEvent.isRestDay()
                        else
                          scheduleEvents

    $q.all(overridePromiseFor scheduleEvent, day for scheduleEvent in overridableEvents)

  overridePromiseFor = (scheduleEvent, day) ->
    if scheduleEvent.isRecurringSchedule()
      scheduleEvent.addException(day)
    else
      scheduleEvent.removeIgnoringNotFound().then(broadcastRemovalOf(scheduleEvent))


  removeOverridesOnDate = (day, scheduleEvents) ->
    $q.all(
      for scheduleEvent in scheduleEvents
        if scheduleEvent.isRecurringSchedule()
          scheduleEvent.removeExceptionIgnoringNotFound(day)
        else
          scheduleEvent.removeIgnoringNotFound().then(broadcastRemovalOf(scheduleEvent))
    )

  createUnpaidDayOff = (day, employee) ->
    event = {
      kind: 'unpaid_day_off'
      employee_id: employee.id
      day_event_date: day.toISODateString()
      is_rest_day: false,
      recurrence_info: {type: 'none'}
    }
    service.post({schedule_event: event}).then(broadcastAddOf)

  createPaidDayOff = (day, employee) ->
    event = {
      kind: 'paid_day_off'
      employee_id: employee.id
      day_event_date: day.toISODateString()
      is_rest_day: false,
      recurrence_info: {type: 'none'}
    }
    service.post({schedule_event: event}).then(broadcastAddOf)

  setSecondaryDayType = (day, employee, multiplier) ->
    event = {
      kind: 'rest_day',
      employee_id: employee.id
      day_event_date: day.toISODateString()
      is_rest_day: true,
      recurrence_info: {type: 'none'},
      day_event_day_type_id: multiplier.id
    }
    service.post({schedule_event: event}).then(broadcastAddOf)

  createWorkThisHoliday = (day, employee) ->
    event = {
      kind: 'work_this_holiday'
      employee_id: employee.id
      day_event_date: day.toISODateString()
      is_rest_day: false,
      recurrence_info: {type: 'none'}
    }
    service.post({schedule_event: event}).then(broadcastAddOf)

  broadcastRemovalOf = (scheduleEvent) ->
    ->
      RestangularObjectCache.removeObject('schedule_events', scheduleEvent)
      $rootScope.$broadcast('payrollhero.api.scheduleEvent.removed', scheduleEvent.id)
      arguments

  broadcastAddOf = (scheduleEvent) ->
    $rootScope.$broadcast('payrollhero.api.scheduleEvent.added', scheduleEvent)
    scheduleEvent

  class ScheduleEventsService
    ###*
      @ngdoc function
      @name payrollhero.api.ScheduleEventsService#createIndividualScheduleOnDay
      @function

      @description
        Given a list of schedule events it will clear that date from
        those schedules and add an individual schedule on that day.
      @param {moment} day Day to make this schedule on.
      @param {Array<ScheduleEvent>} schedule events which already occur on that day.
      @param {Object} Details for this new schedule..
        start: moment to start the schedule on (this function will adjust it to be on that day)
        end: moment to end that shift on (will be adjusted to be on 'day')
        breaks: array of objects with start, end, and 'kind'
        tz: timezone to use for conversion of the date and times.
        worksiteId: worksite to be used when creating the schedule.
      @return {Promise} promise of the completion of all XHRs involved in this task.
    ###
    createIndividualScheduleOnDay: (day, scheduleEvents, exceptionInfo) ->
      overrideSchedulesOnDate(day, scheduleEvents).then ->
        createException(day, exceptionInfo).then(broadcastAddOf)

    ###*
      @ngdoc function
      @name payrollhero.api.ScheduleEventsService#createUnpaidDayOff
      @function

      @description
        Given a {EmployeeScheduleDayList} object, removes all schedules for that
        day and adds an unpaid day off exception
      @param {EmployeeScheduleDayList} daySchedules the day schedule to operate on
      @return {Promise} promise of the completion of all XHRs involved in this task.
    ###
    createUnpaidDayOff: (daySchedules) ->
      overrideSchedulesOnDate(daySchedules.day, daySchedules.schedules, overridesSecondaryDayType: true).then ->
        createUnpaidDayOff(daySchedules.day, daySchedules.employee)

    ###*
      @ngdoc function
      @name payrollhero.api.ScheduleEventsService#createUnpaidDayOff
      @function

      @description
        Given a {EmployeeScheduleDayList} object, removes all schedules for that
        day and adds an unpaid day off exception
      @param {EmployeeScheduleDayList} daySchedules the day schedule to operate on
      @return {Promise} promise of the completion of all XHRs involved in this task.
    ###
    setSecondaryDayType: (daySchedules, multiplier) ->
      overrideSecondaryDayTypesOnDate(daySchedules.day, daySchedules.schedules).then ->
        setSecondaryDayType(daySchedules.day, daySchedules.employee, multiplier)

    ###*
      @ngdoc function
      @name payrollhero.api.ScheduleEventsService#createPaidDayOff
      @function

      @description
        Given a {EmployeeScheduleDayList} object, removes all schedules for that
        day and adds an paid day off exception
      @param {EmployeeScheduleDayList} daySchedules the day schedule to operate on
      @return {Promise} promise of the completion of all XHRs involved in this task.
    ###
    createPaidDayOff: (daySchedules) ->
      overrideSchedulesOnDate(daySchedules.day, daySchedules.schedules, overridesSecondaryDayType: true).then ->
        createPaidDayOff(daySchedules.day, daySchedules.employee)


    ###*
      @ngdoc function
      @name payrollhero.api.ScheduleEventsService#createWorkThisHoliday
      @function

      @description
        Given a {EmployeeScheduleDayList} object, checks to see if the employee
        already works on that holiday and if not adds a 'work_this_holiday' exception.
      @param {EmployeeScheduleDayList} daySchedules the day schedule to operate on
      @return {Promise} promise of the completion of all XHRs involved in this task.
    ###
    createWorkThisHoliday: (daySchedules) ->
      if daySchedules.employee.worksHolidayOn(daySchedules.day)
        return $q.when(false)
      else
        createWorkThisHoliday(daySchedules.day, daySchedules.employee)

    ###*
      @ngdoc function
      @name payrollhero.api.ScheduleEventsService#clearSchedule
      @function

      @description
        Given a {EmployeeScheduleDayList} object, removes all schedules for that day
      @param {EmployeeScheduleDayList} daySchedules the day schedule to operate on
      @return {Promise} promise of the completion of all XHRs involved in this task.
    ###
    clearSchedule: (daySchedules) ->
      overrideSchedulesOnDate(daySchedules.day, daySchedules.schedules, overridesSecondaryDayType: true)

    ###*
      @ngdoc function
      @name payrollhero.api.ScheduleEventsService#resetSchedule
      @function

      @description
        Given a {EmployeeScheduleDayList} object, removes overrides on that day.
      @param {EmployeeScheduleDayList} daySchedules the day schedule to operate on
      @return {Promise} promise of the completion of all XHRs involved in this task.
    ###
    resetSchedule: (daySchedules) ->
      removeOverridesOnDate(daySchedules.day, daySchedules.schedules)

  service = _(service).extend(ScheduleEventsService.prototype)
  service
