import angular from 'angular'
import angularCookies from 'angular-cookies'
import cloneDeep from 'lodash.clonedeep'
import forEach from 'lodash.foreach'
import indexOf from 'lodash.indexof'
import View from 'xo-collection/view'
import xoLib from 'xo-lib'

const {defineProperty} = Object
const {isArray, isString} = angular

// ===================================================================

// Low level XO API for Angular.
export default angular.module('xo-api', [
  angularCookies
])
  .run(function ($rootScope) {
    // Ensure correct integration with Angular.
    xoLib.setScheduler(function (fn) {
      $rootScope.$evalAsync(fn)
    })
  })
  .service('xoApi', function (
    $cookieStore,
    $rootScope,
    $timeout
  ) {
    const xo = new xoLib.Xo()

    // Notifies Angular about changes in the collection.
    xo.objects.on('finish', () => {
      $rootScope.$applyAsync()
    })

    try {
      const token = $cookieStore.get('token')

      // If there is a token, sign in with it.
      if (token) {
        xo.signIn({ token })
      }
    } catch (e) {
      if (e instanceof SyntaxError) {
        $cookieStore.remove('token')
      } else {
        throw e
      }
    }

    // ---------------------------------------------------------------

    const getObject = (function (objects) {
      const {
        all: byIds,
        indexes: {
          ref: byRefs
        }
      } = objects

      return function getObject (id, type) {
        const object = byIds[id] || byRefs[id]

        if (
          // The object has been found and …
          object && (
            // … no type specified.
            !type ||

            // … is of the expected type.
            (type === object.type) ||

            // … is of one of the allowed types.
            isArray(type) && (indexOf(type, object.type) === -1)
          )
        ) {
          return object
        }
      }
    })(xo.objects)

    // ---------------------------------------------------------------

    // TODO: should probably be merged in the main collection in xo-lib.
    let currentAcls = Object.create(null)

    ;(function updateCurrentAcls () {
      xo.call('acl.getCurrent').then(acls => {
        currentAcls = Object.create(null)

        forEach(acls, acl => {
          const object = getObject(acl.object)
          if (object) {
            currentAcls[object.id] = true
          }
        })

        $timeout(updateCurrentAcls, 1e4)
      })
    })()

    function canAccess (id) {
      // Auto unbox.
      if (id.id) {
        id = id.id
      }

      const {user} = xo
      let object
      return (
        // Administrators can access everything.
        user && (user.permission === 'admin') ||

        // Check if the id is in the ACLs table.
        (id in currentAcls) ||

        // Check if the id is in fact not a true id (maybe a ref or a
        // UUID) and if we can resolve it to an id.
        (object = getObject(id)) && (object.id in currentAcls)
      )
    }

    // ---------------------------------------------------------------

    const views = Object.create(null)

    function getView (viewName) {
      let view = views[viewName]
      if (!view) {
        // The view name can be plural (ex VMs) but the type is
        // singular.
        const type = viewName[viewName.length - 1] === 's' ?
          viewName.slice(0, -1) :
          viewName

        const predicate = (object) => object.type === type
        view = views[viewName] = new View(xo.objects, predicate)
      }
      return view
    }

    function registerLazyView (name, predicate, collection) {
      defineProperty(views, name, {
        configurable: true,
        enumerable: true,
        get () {
          if (!collection) {
            collection = xo.objects
          } else if (isString(collection)) {
            collection = getView(collection)
          }

          const view = new View(collection, predicate)
          delete views[name]
          views[name] = view

          return view
        }
      })
    }

    const RUNNING_VM_STATUSES = {
      Running: true,
      Paused: true
    }
    registerLazyView(
      'runningVms',
      (vm) => {
        console.log(vm.name_label, vm.power_state)
        return RUNNING_VM_STATUSES[vm.power_state]
      },
      'VMs'
    )

    const RUNNING_TASK_STATUSES = {
      cancelling: true,
      pending: true
    }
    registerLazyView(
      'runningTasks',
      (task) => RUNNING_TASK_STATUSES[task.status] && canAccess(task.$host),
      'tasks'
    )

    // ---------------------------------------------------------------

    return {
      // -----------------
      // Session
      // -----------------

      logIn (email, password, persist) {
        return xo.signIn({ email, password }).then(() => {
          if (persist) {
            xo.call('token.create').then(function (token) {
              $cookieStore.put('token', token)
            })
          }
        })
      },
      logOut () {
        $cookieStore.remove('token')

        return xo.signOut()
      },
      get status () {
        return xo.status
      },
      get user () {
        return xo.user
      },

      // -----------------
      // RPC
      // -----------------

      call (method, params) {
        // The params need to be cloned to prevent them from being
        // changed before the method has really been sent.
        return xo.call(method, cloneDeep(params))
      },

      // -----------------
      // Objects
      // -----------------

      get (id, types) {
        if (isArray(id)) {
          const objects = []

          forEach(id, id => {
            const object = getObject(id, types)
            if (object) {
              objects.push(object)
            }
          })

          return objects
        }

        return getObject(id, types)
      },

      getView,

      canAccess,

      all: xo.objects.all,

      byTypes: xo.objects.indexes.type
    }
  })
  .name

@@ -4195,76 +4195,8 @@
 ) =%3E
- %7B%0A        console.log(vm.name_label, vm.power_state)%0A        return
  RUN
@@ -4227,24 +4227,16 @@
 r_state%5D
-%0A      %7D
 ,%0A      

Remove a console.log.