/**
* Copyright © 2019-2024 code initially contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
**/

const _ = require('lodash')
const Promise = require('bluebird')

module.exports = function GroupsCtrl(
  $scope
, $filter
, GroupsService
, UserService
, UsersService
, DevicesService
, SettingsService
, ItemsPerPageOptionsService
, GenericModalService
, CommonService
) {
  const originDevices = []
  const originDevicesBySerial = {}
  const standardizableDevices = []
  const standardizableDevicesBySerial = {}
  const groupsById = {}
  const cachedGroupsClass = {}
  const deviceFields =
    'serial,' +
    'model,' +
    'version,' +
    'operator,' +
    'network.type,' +
    'network.subtype,' +
    'display.height,' +
    'display.width,' +
    'manufacturer,' +
    'sdk,' +
    'abi,' +
    'cpuPlatform,' +
    'openGLESVersion,' +
    'marketName,' +
    'phone.imei,' +
    'provider.name,' +
    'group.originName'
  const userFields =
    'email,' +
    'name,' +
    'privilege,' +
    'groups.subscribed,' +
    'groups.quotas.allocated,' +
    'groups.quotas.consumed'
  var rootGroupId

  function publishDevice(device) {
    if (!device.model) {
      device.display = device.phone = device.network = {}
    }
    else {
      device.displayStr = device.display.width + 'x' + device.display.height
      device.networkStr = $scope.computeNetwork(device)
    }
    return device
  }

  function initAvailableGroupDevices(group, availableDevices, availableDevicesBySerial) {
    $scope.groupsEnv[group.id].availableDevices = availableDevices
    $scope.groupsEnv[group.id].availableDevicesBySerial = availableDevicesBySerial
    $scope.groupsEnv[group.id].availableDevices.forEach(function(device) {
      publishDevice(device)
    })
  }

  function getAvailableGroupDevices(group) {
    if (group.class === 'bookable') {
      initAvailableGroupDevices(group, originDevices, originDevicesBySerial)
    }
    else if (group.class === 'standard') {
      initAvailableGroupDevices(group, standardizableDevices, standardizableDevicesBySerial)
    }
    else if ($scope.groupsEnv[group.id].showDevices) {
      GroupsService.getGroupDevices(group.id, true, deviceFields).then(function(response) {
        if (CommonService.isExisting($scope.groupsEnv[group.id])) {
          $scope.groupsEnv[group.id].availableDevicesBySerial = {}
          $scope.groupsEnv[group.id].availableDevices = []
          response.data.devices.forEach(function(device) {
            addAvailableGroupDevice(group.id, device, -1)
          })
          initAvailableGroupDevices(
            group
          , $scope.groupsEnv[group.id].availableDevices
          , $scope.groupsEnv[group.id].availableDevicesBySerial)
        }
      })
    }
  }

  function checkDurationQuota(group, deviceNumber, startDate, stopDate, repetitions) {
    if (CommonService.isOriginGroup(group.class)) {
      return true
    }
    if (CommonService.isExisting($scope.usersByEmail[group.owner.email])) {
      const duration =
        (group.devices.length + deviceNumber) *
        ((new Date(stopDate)) - (new Date(startDate))) *
        (repetitions + 1)

      if (duration <=
          $scope.users[$scope.usersByEmail[group.owner.email].index]
                .groups.quotas.allocated.duration) {
        return true
      }
    }
    return false
  }

  function isBookedDevice(serial) {
    if (CommonService.isExisting(originDevicesBySerial[serial])) {
      for(var i in $scope.groups) {
        if (!CommonService.isOriginGroup($scope.groups[i].class) &&
            $scope.groups[i].devices.indexOf(serial) > -1) {
          return true
        }
      }
    }
    return false
  }

  function addStandardizableDevicesIfNotBooked(devices, timeStamp) {
    devices.forEach(function(serial) {
      if (!isBookedDevice(serial)) {
        addStandardizableDevice(
          originDevices[originDevicesBySerial[serial].index]
        , timeStamp
        )
      }
    })
  }

  function updateStandardizableDeviceIfNotBooked(device, timeStamp) {
    if (!isBookedDevice(device.serial)) {
      updateStandardizableDevice(device, timeStamp)
    }
  }

  function initGroup(group) {
    cachedGroupsClass[group.id] = group.class
    if (typeof $scope.groupsEnv[group.id] === 'undefined') {
      $scope.groupsEnv[group.id] = {}
      initAvailableGroupDevices(group, [], {})
      if (group.privilege === 'root') {
        rootGroupId = group.id
      }
    }
    return group
  }

  function addGroup(group, timeStamp) {
    if (CommonService.add($scope.groups, groupsById, group, 'id', timeStamp)) {
      return initGroup(group)
    }
    return null
  }

  function updateGroup(group, timeStamp, noAdding) {
    if (CommonService.update($scope.groups, groupsById, group, 'id', timeStamp, noAdding)) {
      return initGroup($scope.groups[groupsById[group.id].index])
    }
    return null
  }

  function deleteGroup(id, timeStamp) {
    const group = CommonService.delete($scope.groups, groupsById, id, timeStamp)

    if (group) {
      delete $scope.groupsEnv[group.id]
    }
    return group
  }

  function addOriginDevice(device, timeStamp) {
    return CommonService.add(originDevices, originDevicesBySerial, device, 'serial', timeStamp)
  }

  function updateOriginDevice(device, timeStamp) {
    return CommonService.update(originDevices, originDevicesBySerial, device, 'serial', timeStamp)
  }

  function deleteOriginDevice(serial, timeStamp) {
    return CommonService.delete(originDevices, originDevicesBySerial, serial, timeStamp)
  }

  function addStandardizableDevice(device, timeStamp) {
    return CommonService.add(
      standardizableDevices, standardizableDevicesBySerial, device, 'serial', timeStamp)
  }

  function updateStandardizableDevice(device, timeStamp) {
    return CommonService.update(
      standardizableDevices, standardizableDevicesBySerial, device, 'serial', timeStamp)
  }

  function deleteStandardizableDevice(serial, timeStamp) {
    return CommonService.delete(
      standardizableDevices, standardizableDevicesBySerial, serial, timeStamp)
  }

  function addAvailableGroupDevice(id, device, timeStamp) {
    return CommonService.add(
      $scope.groupsEnv[id].availableDevices
    , $scope.groupsEnv[id].availableDevicesBySerial, device, 'serial', timeStamp)
  }

  function updateAvailableGroupDevice(id, device, timeStamp, noAdding) {
    return CommonService.update(
      $scope.groupsEnv[id].availableDevices
    , $scope.groupsEnv[id].availableDevicesBySerial, device, 'serial', timeStamp, noAdding)
  }

  function deleteAvailableGroupDevice(id, serial, timeStamp) {
    return CommonService.delete(
      $scope.groupsEnv[id].availableDevices
    , $scope.groupsEnv[id].availableDevicesBySerial, serial, timeStamp)
  }

  function addUser(user, timeStamp) {
    return CommonService.add($scope.users, $scope.usersByEmail, user, 'email', timeStamp)
  }

  function updateUser(user, timeStamp) {
    return CommonService.update($scope.users, $scope.usersByEmail, user, 'email', timeStamp)
  }

  function deleteUser(email, timeStamp) {
    return CommonService.delete($scope.users, $scope.usersByEmail, email, timeStamp)
  }

  function initScope() {
    GroupsService.getOboeMyGroups(function(group) {
      addGroup(group, -1)
    })
    .done(function() {
      $scope.$digest()
    })

    UsersService.getOboeUsers(userFields, function(user) {
      addUser(user, -1)
    })
    .done(function() {
      if (CommonService.isExisting($scope.usersByEmail[$scope.currentUser.email])) {
        $scope.users[$scope.usersByEmail[$scope.currentUser.email].index] = $scope.currentUser
      }
    })

    UserService.getUser().then(function(response) {
      CommonService.merge($scope.currentUser, response.data.user)
    })

    if ($scope.isAdmin()) {
      DevicesService.getOboeDevices('origin', deviceFields, function(device) {
        addOriginDevice(device, -1)
      })
      DevicesService.getOboeDevices('standardizable', deviceFields, function(device) {
        addStandardizableDevice(device, -1)
      })
    }
  }

  $scope.currentUser = CommonService.merge({}, UserService.currentUser)
  $scope.users = []
  $scope.usersByEmail = {}
  $scope.groups = []
  $scope.groupsEnv = {}
  $scope.confirmRemove = {value: true}
  $scope.scopeGroupsCtrl = $scope
  $scope.itemsPerPageOptions = ItemsPerPageOptionsService

  SettingsService.bind($scope, {
    target: 'groupItemsPerPage'
  , source: 'groupItemsPerPage'
  , defaultValue: $scope.itemsPerPageOptions[2]
  })

  $scope.userColumns = [
    {name: 'Name', property: 'name'}
  , {name: 'Email', property: 'email'}
  , {name: 'Privilege', property: 'privilege'}
  ]
  $scope.defaultUserData = {
    columns: [
      {name: 'Name', sort: 'sort-asc'}
    , {name: 'Email', sort: 'none'}
    , {name: 'Privilege', sort: 'none'}
    ]
  , sort: {index: 0, reverse: false}
  }
  SettingsService.bind($scope, {
    target: 'userData'
  , source: 'userData'
  , defaultValue: $scope.defaultUserData
  })
  SettingsService.bind($scope, {
    target: 'groupUserData'
  , source: 'groupUserData'
  , defaultValue: $scope.defaultUserData
  })

  $scope.conflictColumns = [
    {name: 'Serial', property: 'serial'}
  , {name: 'Starting Date', property: 'startDate'}
  , {name: 'Expiration Date', property: 'stopDate'}
  , {name: 'Group Name', property: 'group'}
  , {name: 'Group Owner', property: 'ownerName'}
  ]
  $scope.defaultConflictData = {
    columns: [
      {name: 'Serial', sort: 'sort-asc'}
    , {name: 'Starting Date', sort: 'none'}
    , {name: 'Expiration Date', sort: 'none'}
    , {name: 'Group Name', sort: 'none'}
    , {name: 'Group Owner', sort: 'none'}
    ]
  , sort: {index: 0, reverse: false}
  }
  SettingsService.bind($scope, {
    target: 'conflictData'
  , source: 'conflictData'
  , defaultValue: $scope.defaultConflictData
  })

  $scope.mailToGroupOwners = function(groups) {
    CommonService.copyToClipboard(_.uniq(groups.map(function(group) {
      return group.owner.email
    }))
    .join(SettingsService.get('emailAddressSeparator')))
    .url('mailto:?body=*** Paste the email addresses from the clipboard! ***')
  }

  $scope.mailToGroupUsers = function(group, users) {
    // group unused actually..
    $scope.mailToAvailableUsers(users)
  }

  $scope.mailToAvailableUsers = function(users) {
    CommonService.copyToClipboard(users.map(function(user) {
      return user.email
    })
    .join(SettingsService.get('emailAddressSeparator')))
    .url('mailto:?body=*** Paste the email addresses from the clipboard! ***')
  }

  $scope.getGroupIndex = function(relativeIndex) {
    return relativeIndex + ($scope.groupCurrentPage - 1) * $scope.groupItemsPerPage.value
  }

  $scope.computeDisplay = function(device) {
    return device.display.width * device.display.height
  }

  $scope.computeNetwork = function(device) {
    if (!device.network || !device.network.type) {
      return ''
    }
    else if (device.network.subtype) {
      return device.network.type + ' (' + device.network.subtype + ')'
    }
    return device.network.type
  }

  $scope.resetDeviceData = function() {
    $scope.deviceData = JSON.parse(JSON.stringify($scope.defaultDeviceData))
  }

  $scope.resetGroupDeviceData = function() {
    $scope.groupDeviceData = JSON.parse(JSON.stringify($scope.defaultDeviceData))
  }

  $scope.deviceColumns = [
    {name: 'Model', property: 'model'}
  , {name: 'Serial', property: 'serial'}
  , {name: 'Carrier', property: 'operator'}
  , {name: 'OS', property: 'version'}
  , {name: 'Network', property: $scope.computeNetwork}
  , {name: 'Screen', property: $scope.computeDisplay}
  , {name: 'Manufacturer', property: 'manufacturer'}
  , {name: 'SDK', property: 'sdk'}
  , {name: 'ABI', property: 'abi'}
  , {name: 'CPU Platform', property: 'cpuPlatform'}
  , {name: 'OpenGL ES version', property: 'openGLESVersion'}
  , {name: 'Market name', property: 'marketName'}
  , {name: 'Phone IMEI', property: 'phone.imei'}
  , {name: 'Location', property: 'provider.name'}
  , {name: 'Group Origin', property: 'group.originName'}
  ]
  $scope.defaultDeviceData = {
    columns: [
      {name: 'Model', selected: true, sort: 'sort-asc'}
    , {name: 'Serial', selected: true, sort: 'none'}
    , {name: 'Carrier', selected: false, sort: 'none'}
    , {name: 'OS', selected: true, sort: 'none'}
    , {name: 'Network', selected: false, sort: 'none'}
    , {name: 'Screen', selected: true, sort: 'none'}
    , {name: 'Manufacturer', selected: true, sort: 'none'}
    , {name: 'SDK', selected: true, sort: 'none'}
    , {name: 'ABI', selected: false, sort: 'none'}
    , {name: 'CPU Platform', selected: false, sort: 'none'}
    , {name: 'OpenGL ES version', selected: false, sort: 'none'}
    , {name: 'Market name', selected: true, sort: 'none'}
    , {name: 'Phone IMEI', selected: false, sort: 'none'}
    , {name: 'Location', selected: true, sort: 'none'}
    , {name: 'Group Origin', selected: true, sort: 'none'}
    ]
  , sort: {index: 0, reverse: false}
  }
  SettingsService.bind($scope, {
    target: 'deviceData'
  , source: 'deviceData'
  , defaultValue: $scope.defaultDeviceData
  })
  SettingsService.bind($scope, {
    target: 'groupDeviceData'
  , source: 'groupDeviceData'
  , defaultValue: $scope.defaultDeviceData
  })
  $scope.nameRegex = /^[0-9a-zA-Z-_./: ]{1,50}$/
  $scope.nameRegexStr = '/^[0-9a-zA-Z-_./: ]{1,50}$/'
  $scope.classOptions = CommonService.classOptions
  $scope.getClassName = CommonService.getClassName
  $scope.sortBy = CommonService.sortBy

  $scope.isAdmin = function() {
    return $scope.currentUser.privilege === 'admin'
  }

  $scope.getRepetitionsQuotas = function(email) {
    if (CommonService.isExisting($scope.usersByEmail[email])) {
      return $scope.users[$scope.usersByEmail[email].index].groups.quotas.repetitions
    }
    return null
  }

  $scope.initShowDevices = function(group, showDevices) {
    if (typeof $scope.groupsEnv[group.id].groupDeviceCurrentPage === 'undefined') {
      $scope.groupsEnv[group.id].groupDeviceCurrentPage = 1
      $scope.groupsEnv[group.id].groupDeviceItemsPerPage = $scope.itemsPerPageOptions[1]
      $scope.groupsEnv[group.id].availableDeviceCurrentPage = 1
      $scope.groupsEnv[group.id].availableDeviceItemsPerPage = $scope.itemsPerPageOptions[1]
    }
    $scope.groupsEnv[group.id].showDevices = showDevices
    getAvailableGroupDevices(group)
  }

  $scope.initShowUsers = function(group) {
    if (typeof $scope.groupsEnv[group.id].groupUserCurrentPage === 'undefined') {
      $scope.groupsEnv[group.id].groupUserCurrentPage = 1
      $scope.groupsEnv[group.id].groupUserItemsPerPage = $scope.itemsPerPageOptions[1]
      $scope.groupsEnv[group.id].availableUserCurrentPage = 1
      $scope.groupsEnv[group.id].availableUserItemsPerPage = $scope.itemsPerPageOptions[1]
    }
  }

  $scope.watchGroupClass = function(group) {
    if (CommonService.isNoRepetitionsGroup($scope.groupsEnv[group.id].tmpClass)) {
      $scope.groupsEnv[group.id].tmpRepetitions = 0
    }
    else if ($scope.groupsEnv[group.id].tmpRepetitions === 0) {
      $scope.groupsEnv[group.id].tmpRepetitions = 1
    }
  }

  $scope.initTemporaryName = function(group) {
    $scope.groupsEnv[group.id].tmpName = group.name
    $scope.groupsEnv[group.id].tmpNameTooltip = 'No change'
  }

  $scope.initTemporarySchedule = function(group) {
    $scope.groupsEnv[group.id].tmpClass = group.class
    $scope.groupsEnv[group.id].tmpRepetitions = group.repetitions
    $scope.groupsEnv[group.id].tmpStartDate = new Date(group.dates[0].start)
    $scope.groupsEnv[group.id].tmpStopDate = new Date(group.dates[0].stop)
    $scope.groupsEnv[group.id].tmpScheduleTooltip = 'No change'
  }

  $scope.conditionForDevicesAddition = function(group, deviceNumber) {
    return checkDurationQuota(
      group
    , deviceNumber
    , group.dates[0].start
    , group.dates[0].stop
    , group.repetitions
    )
  }

  $scope.conditionForGroupCreation = function() {
    return $scope.currentUser.groups.quotas.consumed.number <
           $scope.currentUser.groups.quotas.allocated.number
  }

  $scope.conditionForGroupUsersRemoving = function(group, users) {
    return !(users.length === 0 ||
             group.privilege === 'root' && users.length === 1 && users[0].privilege === 'admin' ||
             group.privilege !== 'root' &&
               (users.length === 2 &&
                (users[0].privilege === 'admin' && users[1].email === group.owner.email ||
                 users[0].email === group.owner.email && users[1].privilege === 'admin') ||
                users.length === 1 &&
                (users[0].email === group.owner.email || users[0].privilege === 'admin'))
            )
  }

  $scope.conditionForNameSaving = function(group, formInvalidStatus) {
    return !formInvalidStatus && $scope.groupsEnv[group.id].tmpName !== group.name
  }

  $scope.conditionForScheduleSaving = function(group, formInvalidStatus) {
    if (formInvalidStatus) {
      $scope.groupsEnv[group.id].tmpScheduleTooltip = 'Bad syntax'
      return false
    }
    if ($scope.groupsEnv[group.id].tmpClass !== group.class ||
        parseInt($scope.groupsEnv[group.id].tmpRepetitions, 10) !== group.repetitions ||
        $scope.groupsEnv[group.id].tmpStartDate.getTime() !==
        (new Date(group.dates[0].start)).getTime() ||
        $scope.groupsEnv[group.id].tmpStopDate.getTime() !==
        (new Date(group.dates[0].stop)).getTime()) {
      if (!CommonService.isNoRepetitionsGroup($scope.groupsEnv[group.id].tmpClass)) {
        if (parseInt($scope.groupsEnv[group.id].tmpRepetitions, 10) === 0) {
          $scope.groupsEnv[group.id].tmpScheduleTooltip = 'Repetitions must be > 0 for this Class'
          return false
        }
      }
      if ($scope.groupsEnv[group.id].tmpStartDate >= $scope.groupsEnv[group.id].tmpStopDate) {
        $scope.groupsEnv[group.id].tmpScheduleTooltip = 'Starting date >= Expiration date'
        return false
      }
      if (($scope.groupsEnv[group.id].tmpStopDate - $scope.groupsEnv[group.id].tmpStartDate) >
          CommonService.getClassDuration($scope.groupsEnv[group.id].tmpClass)) {
        $scope.groupsEnv[group.id].tmpScheduleTooltip =
          '(Expiration date - Starting date) must be <= Class duration'
        return false
      }
      if ($scope.isAdmin() &&
          group.devices.length &&
          (CommonService.isOriginGroup(group.class) &&
           !CommonService.isOriginGroup($scope.groupsEnv[group.id].tmpClass) ||
           CommonService.isOriginGroup($scope.groupsEnv[group.id].tmpClass) &&
           !CommonService.isOriginGroup(group.class))) {
        $scope.groupsEnv[group.id].tmpScheduleTooltip =
          'Unauthorized class while device list is not empty'
        return false
      }
      if (!checkDurationQuota(
        group
      , 0
      , $scope.groupsEnv[group.id].tmpStartDate
      , $scope.groupsEnv[group.id].tmpStopDate
      , $scope.groupsEnv[group.id].tmpRepetitions)) {
        $scope.groupsEnv[group.id].tmpScheduleTooltip = 'Group duration quotas is reached'
        return false
      }
      $scope.groupsEnv[group.id].tmpScheduleTooltip = ''
      return true
    }
    $scope.groupsEnv[group.id].tmpScheduleTooltip = 'No change'
    return false
  }

  $scope.conditionForRepetitions = function(group) {
    return !CommonService.isNoRepetitionsGroup($scope.groupsEnv[group.id].tmpClass)
  }

  $scope.addGroupDevice = function(group, device) {
    if (CommonService.isOriginGroup(group.class)) {
      CommonService.errorWrapper(
        DevicesService.addOriginGroupDevice
      , [group.id, device.serial])
    }
    else {
      CommonService.errorWrapper(
        GroupsService.addGroupDevice
        , [group.id, device.serial])
        .then(function(response) {
          if (!response.success &&
              response.status === 409 &&
              response.data.hasOwnProperty('conflicts')) {
            $scope.groupsEnv[group.id].showConflicts = true
            $scope.groupsEnv[group.id].conflicts = response.data.conflicts
          }
        })
    }
  }

  $scope.addGroupDevices = function(group, deviceSearch, filteredDevices) {
    CommonService.errorWrapper(
      CommonService.isOriginGroup(group.class) ?
        DevicesService.addOriginGroupDevices :
        GroupsService.addGroupDevices
    , deviceSearch ?
        [group.id, filteredDevices.map(function(device) { return device.serial }).join()] :
        [group.id])
  }

  $scope.removeGroupDevice = function(group, device) {
    CommonService.errorWrapper(
      CommonService.isOriginGroup(group.class) ?
        DevicesService.removeOriginGroupDevice :
        GroupsService.removeGroupDevice
    , [group.id, device.serial])
  }

  $scope.removeGroupDevices = function(group, deviceSearch, filteredDevices) {
    CommonService.errorWrapper(
      CommonService.isOriginGroup(group.class) ?
        DevicesService.removeOriginGroupDevices :
        GroupsService.removeGroupDevices
    , deviceSearch ?
        [group.id, filteredDevices.map(function(device) { return device.serial }).join()] :
        [group.id])
  }

  $scope.addGroupUser = function(group, user) {
    CommonService.errorWrapper(
      GroupsService.addGroupUser
    , [group.id, user.email])
  }

  $scope.addGroupUsers = function(group, userSearch, filteredUsers) {
    CommonService.errorWrapper(
      GroupsService.addGroupUsers
    , userSearch ?
        [group.id, filteredUsers.map(function(user) { return user.email }).join()] :
        [group.id])
  }

  $scope.removeGroupUser = function(group, user) {
    CommonService.errorWrapper(
      GroupsService.removeGroupUser
    , [group.id, user.email])
  }

  $scope.removeGroupUsers = function(group, userSearch, filteredUsers) {
    CommonService.errorWrapper(
      GroupsService.removeGroupUsers
    , userSearch ?
        [group.id, filteredUsers.map(function(user) { return user.email }).join()] :
        [group.id])
  }

  $scope.removeGroup = function(group, askConfirmation) {
    if (askConfirmation) {
      GenericModalService.open({
        message: 'Really delete this group?'
      , type: 'Warning'
      , size: 'sm'
      , cancel: true
      })
      .then(function() {
        CommonService.errorWrapper(
          GroupsService.removeGroup
        , [group.id])
      })
    }
    else {
      CommonService.errorWrapper(
        GroupsService.removeGroup
      , [group.id])
    }
  }

  $scope.removeGroups = function(search, filteredGroups, askConfirmation) {
    function removeGroups() {
      if (!search) {
        CommonService.errorWrapper(GroupsService.removeGroups)
      }
      else {
        CommonService.errorWrapper(
          GroupsService.removeGroups
        , [filteredGroups.map(function(group) { return group.id }).join()])
      }
    }

    if (askConfirmation) {
      GenericModalService.open({
        message: 'Really delete this selection of groups?'
      , type: 'Warning'
      , size: 'sm'
      , cancel: true
      })
      .then(function() {
        removeGroups()
      })
    }
    else {
      removeGroups()
    }
  }

  $scope.createGroup = function() {
    $scope.hideGroupCreation = true
    CommonService.errorWrapper(GroupsService.createGroup)
      .then(function() {
        delete $scope.hideGroupCreation
      })
  }

  $scope.updateGroupSchedule = function(group) {
    CommonService.errorWrapper(GroupsService.updateGroup, [group.id, {
      'class': $scope.groupsEnv[group.id].tmpClass
    , 'repetitions': parseInt($scope.groupsEnv[group.id].tmpRepetitions, 10)
    , 'startTime': $scope.groupsEnv[group.id].tmpStartDate
    , 'stopTime': $scope.groupsEnv[group.id].tmpStopDate
    }])
    .then(function(response) {
      if (!response.success &&
          response.status === 409 &&
          response.data.hasOwnProperty('conflicts')) {
        $scope.groupsEnv[group.id].conflicts = []
        response.data.conflicts.forEach(function(conflict) {
          conflict.devices.forEach(function(serial) {
            $scope.groupsEnv[group.id].conflicts.push({
              serial: serial
            , startDate: $filter('date')(conflict.date.start, SettingsService.get('dateFormat'))
            , stopDate: $filter('date')(conflict.date.stop, SettingsService.get('dateFormat'))
            , group: conflict.group
            , ownerName: conflict.owner.name
            , ownerEmail: conflict.owner.email
            })
          })
        })
        $scope.groupsEnv[group.id].showConflicts = true
      }
    })
  }

  $scope.updateGroupState = function(group) {
    CommonService.errorWrapper(
      GroupsService.updateGroup
    , [group.id, {'state': 'ready'}])
  }

  $scope.updateGroupName = function(group) {
    CommonService.errorWrapper(
      GroupsService.updateGroup
    , [group.id, {'name': $scope.groupsEnv[group.id].tmpName}])
  }

  $scope.$on('user.settings.groups.updated', function(event, message) {
    const isChangedSchedule = message.isChangedDates || message.isChangedClass
    const doGetDevices =
      !CommonService.isOriginGroup(message.group.class) &&
      (isChangedSchedule || message.devices.length)
    const isGroupOwner = $scope.isAdmin() || $scope.currentUser.email === message.group.owner.email
    const group = updateGroup(
      message.group
    , message.timeStamp
    , !isGroupOwner)

    if (group) {
      if ($scope.isAdmin()) {
        if (!CommonService.isOriginGroup(group.class)) {
          if (message.devices.length) {
            if (!message.isAddedDevice) {
              addStandardizableDevicesIfNotBooked(message.devices, message.timeStamp)
            }
            else {
              message.devices.forEach(function(serial) {
                deleteStandardizableDevice(serial, message.timeStamp)
              })
            }
          }
        }
        else if (message.isChangedClass) {
          getAvailableGroupDevices(group)
        }
      }
      if (isChangedSchedule) {
        $scope.initTemporarySchedule(group)
      }
      if (doGetDevices) {
        $scope.groups.forEach(function(group) {
          if (group.id !== message.group.id || isChangedSchedule) {
            getAvailableGroupDevices(group)
          }
        })
      }
    }
    else if (!isGroupOwner && doGetDevices) { // a completer ... soit propriétaire et event obsolete, soit non propriétaire donc non admin
      $scope.groups.forEach(function(group) {
        getAvailableGroupDevices(group)
      })
    }
  })

  $scope.$on('user.settings.groups.created', function(event, message) {
    addGroup(message.group, message.timeStamp)
  })

  $scope.$on('user.settings.groups.deleted', function(event, message) {
    const group = message.group

    if (deleteGroup(group.id, message.timeStamp)) {
      if ($scope.isAdmin() && !CommonService.isOriginGroup(group.class)) {
        addStandardizableDevicesIfNotBooked(group.devices, message.timeStamp)
      }
    }
    if (!CommonService.isOriginGroup(group.class) && group.devices.length) {
      $scope.groups.forEach(function(group) {
        if (!CommonService.isOriginGroup(group.class)) {
          getAvailableGroupDevices(group)
        }
      })
    }
  })

  $scope.$on('user.settings.users.updated', function(event, message) {
    function getGroupClass(id) {
      if (CommonService.isExisting(groupsById[id])) {
        return Promise.resolve($scope.groups[groupsById[id].index].class)
      }
      else if (cachedGroupsClass[id]) {
        return Promise.resolve(cachedGroupsClass[id])
      }
      else {
        return GroupsService.getGroup(id).then(function(response) {
          cachedGroupsClass[id] = response.data.group.class
          return cachedGroupsClass[id]
        })
        .catch(function(error) {
          return false
        })
      }
    }

    if (($scope.isAdmin() &&
         CommonService.isExisting($scope.usersByEmail[message.user.email]) ||
         message.user.email === $scope.currentUser.email
        ) &&
        updateUser(message.user, message.timeStamp) &&
        message.groups.length) {

      Promise.map(message.groups, function(groupId) {
        return getGroupClass(groupId).then(function(_class) {
          return !_class || _class === 'bookable'
        })
      })
      .then(function(results) {
        if (_.without(results, false).length) {
          Promise.map($scope.groups, function(group) {
            if (group.owner.email === message.user.email &&
                !CommonService.isOriginGroup(group.class)) {
              getAvailableGroupDevices(group)
            }
          })
        }
      })
    }
  })

  $scope.$on('user.settings.users.created', function(event, message) {
    addUser(message.user, message.timeStamp)
  })

  $scope.$on('user.settings.users.deleted', function(event, message) {
    deleteUser(message.user.email, message.timeStamp)
  })

  $scope.$on('user.settings.devices.deleted', function(event, message) {
    if ($scope.isAdmin()) {
      deleteOriginDevice(message.device.serial, message.timeStamp)
      deleteStandardizableDevice(message.device.serial, message.timeStamp)
    }
    $scope.groups.forEach(function(group) {
      if (!CommonService.isOriginGroup(group.class)) {
        deleteAvailableGroupDevice(group.id, message.device.serial, message.timeStamp)
      }
    })
  })

  $scope.$on('user.settings.devices.created', function(event, message) {
    const device = publishDevice(message.device)

    if ($scope.isAdmin()) {
      addOriginDevice(device, message.timeStamp)
      addStandardizableDevice(device, message.timeStamp)
    }
  })

  $scope.$on('user.settings.devices.updated', function(event, message) {
    const device = publishDevice(message.device)

    if ($scope.isAdmin()) {
      updateOriginDevice(device, message.timeStamp)
      updateStandardizableDeviceIfNotBooked(device, message.timeStamp)
    }
    $scope.groups.forEach(function(group) {
      if (!CommonService.isOriginGroup(group.class)) {
        if (device.group.origin !== message.oldOriginGroupId) {
          if ($scope.currentUser.groups.subscribed.indexOf(device.group.origin) > -1) {
            getAvailableGroupDevices(group, message.timeStamp)
          }
          else {
            deleteAvailableGroupDevice(group.id, device.serial, message.timeStamp)
          }
        }
        else {
          updateAvailableGroupDevice(group.id, device, message.timeStamp, true)
        }
      }
    })
  })

  initScope()
}
