var EventEmitter = require('eventemitter3')
var Promise = require('bluebird')
Promise.longStackTraces()

module.exports = function InstallService(
  $rootScope
, $http
, $filter
, StorageService
) {
  var installService = Object.create(null)

  function Installation(state) {
    this.progress = 0
    this.state = state
    this.settled = false
    this.success = false
    this.error = null
    this.href = null
    this.manifest = null
    this.launch = true
  }

  Installation.prototype = Object.create(EventEmitter.prototype)
  Installation.prototype.constructor = Installation

  Installation.prototype.apply = function($scope) {
    function changeListener() {
      $scope.safeApply()
    }

    this.on('change', changeListener)

    $scope.$on('$destroy', function() {
      this.removeListener('change', changeListener)
    }.bind(this))

    return this
  }

  Installation.prototype.update = function(progress, state) {
    this.progress = Math.floor(progress)
    this.state = state
    this.emit('change')
  }

  Installation.prototype.okay = function(state) {
    this.settled = true
    this.progress = 100
    this.success = true
    this.state = state
    this.emit('change')
  }

  Installation.prototype.fail = function(err) {
    this.settled = true
    this.progress = 100
    this.success = false
    this.error = err
    this.emit('change')
  }

  installService.installUrl = function (control, url) {
    var installation = new Installation('downloading')
    $rootScope.$broadcast('installation', installation)
    return control.uploadUrl(url)
      .progressed(function (uploadResult) {
        installation.update(uploadResult.progress / 2, uploadResult.lastData)
      })
      .then(function (uploadResult) {
        installation.update(uploadResult.progress / 2, uploadResult.lastData)
        installation.manifest = uploadResult.body
        return control.install({
            href: installation.href
          , manifest: installation.manifest
          , launch: installation.launch
          })
          .progressed(function (result) {
            installation.update(50 + result.progress / 2, result.lastData)
          })
      })
      .then(function() {
        installation.okay('installed')
      })
      .catch(function(err) {
        installation.fail(err.code || err.message)
      })
  }

  installService.installFile = function (control, $files) {
    var installation = new Installation('uploading')
    $rootScope.$broadcast('installation', installation)
    return StorageService.storeFile('apk', $files, {
        filter: function(file) {
          return /\.apk$/i.test(file.name)
        }
      })
      .progressed(function(e) {
        if (e.lengthComputable) {
          installation.update(e.loaded / e.total * 100 / 2, 'uploading')
        }
      })
      .then(function(res) {
        installation.update(100 / 2, 'processing')
        installation.href = res.data.resources.file.href
        return $http.get(installation.href + '/manifest')
          .then(function(res) {
            if (res.data.success) {
              installation.manifest = res.data.manifest
              return control.install({
                  href: installation.href
                , manifest: installation.manifest
                , launch: installation.launch
                })
                .progressed(function (result) {
                  installation.update(50 + result.progress / 2, result.lastData)
                })
            }
            else {
              throw new Error('Unable to retrieve manifest')
            }
          })
      })
      .then(function() {
        installation.okay('installed')
      })
      .catch(function(err) {
        installation.fail(err.code || err.message)
      })
  }

  return installService
}
