const wait_valid = (proxy, callback) => {
  proxy.wait(function() {
    if (proxy.valid) {
      callback();
    }
  });
}

export function CheckIfNode(callback) {
  let cmd = ["which",
            "nodectl"]
  let proc = cockpit.spawn(
     cmd,
     {err: "message"}
  )
  .done(function() {
      callback(true)
  })
  .fail(function(err, resp) {
    console.log("nodectl is not installed. Disabling node functionality")
    callback(false)
  })
}

export function SshHostKey(type, callback) {
  let path = `/etc/ssh/ssh_host_${type}_key`
  let host_key = cockpit.file(path, {superuser: 'try'})
  host_key.read()
    .done(function(resp) {
      resp = resp.split('\n').join('<br />')
      callback(resp)
    })
    .fail(function(error) {
      console.log(`Error opening ${path}`)
      console.log(error)
    })
    .always(function() {
      host_key.close()
    })
}

export class VirtualMachines {
  constructor() {
    var client = cockpit.dbus('org.freedesktop.machine1')
    this.proxy = client.proxy('org.freedesktop.machine1.Manager',
                              '/org/freedesktop/machine1');
  }
  getVms(callback) {
    var proxy = this.proxy
    wait_valid(proxy, function() {
      var filter_host = (value) => {
        return value[0] != '.host'
      }
      proxy.ListMachines().done(function(result) {
        callback(result.filter(filter_host))
      })
    })
  }
}

export class NodeStatus {
  constructor() {
  }
  call(callback, args, failOnly = false) {
    let cmd = ["/usr/sbin/nodectl",
               "--machine-readable"]
    cmd = cmd.concat(args)
    var self = this
    let proc = cockpit.spawn(
       cmd,
       {err: "message",
        superuser: "require"}
    )
    .done(function(json) {
      if (!failOnly) {
        callback(self.sortObject(JSON.parse(json)))
      } else {
        callback({"success": true})
      }
    })
    .fail(function(err, resp) {
      let msg = `Failed to check "nodectl ${args}"`
      let ret = {
        failure: err.message
      }
      callback(ret)
    })
  }
  checkPermissions(callback) {
    this.call(callback, ["--version"], true)
  }
  info(callback) {
    this.call(callback, ["info"])
  }
  check(callback) {
    this.call(callback, ["check"])
  }
  rollback(callback, layer) {
    this.call(callback,
      ["rollback",
       "--nvr",
        layer
      ]
    )
  }
  sortObject(object) {
    var sortedObj = {},
        keys = Object.keys(object);

    keys.sort(function(key1, key2){
        key1 = key1.toLowerCase(), key2 = key2.toLowerCase();
        if(key1 < key2) return -1;
        if(key1 > key2) return 1;
        return 0;
    });

    for(var index in keys){
        var key = keys[index];
        if(typeof object[key] == 'object' && !(object[key] instanceof Array)){
            sortedObj[key] = this.sortObject(object[key]);
        } else {
            sortedObj[key] = object[key];
        }
    }
    return sortedObj;
  }
}

export class NetworkInterfaces {
  constructor() {
    this.client = cockpit.dbus('org.freedesktop.NetworkManager')
    this.model = {}
    this.seen = {}
    this.refresh()
    this.promises = {}
    this.modelCallbacks = new Set()
  }
  call(iface, prop, method, args, callback) {
    this.client.call(iface, prop, method, args)
      .done(function(reply, options) {
        if (method === "Get") {
          callback(reply.pop().v)
        } else {
          console.log()
          callback(reply)
        }
      })
      .fail(function(reason) {
        console.log("Failed: " + reason)
      })
  }
  refresh() {
    var self = this
    this.call('/org/freedesktop/NetworkManager',
              'org.freedesktop.DBus.Properties', 'Get',
              ['org.freedesktop.NetworkManager', 'ActiveConnections'],
              function(reply) {
                self.buildModel(reply)
              })
    return this.model
  }
  getAll(path, type, callback) {
    this.call(path, 'org.freedesktop.DBus.Properties',
              'GetAll', [type], callback)
  }
  buildModel(active_nics) {
    var self = this
    self.model = {}
    var objList = []
    active_nics.forEach(function (nic) {
      var obj = self.buildObject(nic)
      obj.then((val) => {
        self.model[val.Id] = val
      })
        objList.push(obj)
    })
    Promise.all(objList).then(() => {
      // model is now ready for consumption
      this.modelCallbacks.forEach((callback) => {
        callback(this.model)
      })
    })
  }
  addModelCallback(callback) {
    this.modelCallbacks.add(callback)
      return() => {
        this.modelCallbacks.delete(callback)
      }
  }
  buildObject(path) {
    var self = this
    var obj = {}
    var base = 'org.freedesktop.NetworkManager.'
    var types = {
      '/AccessPoint': 'AccessPoint',
      '/ActiveConnection': 'Connection.Active',
      '/DHCP4Config': 'DHCP4Config',
      '/Devices': 'Device',
      '/IP4Config': 'IP4Config',
      '/IP6Config': 'IP6Config',
      '/Settings': 'Settings.Connection'
    }
    var iface
    for (var key in types) {
      if (path.indexOf(key) >= 0) {
        iface = `${base}${types[key]}`
      }
    }
    var prom = new Promise(function(resolve, reject) {
      self.getAll(path, iface, function(reply) {
        reply = reply.pop()
        for (let key in reply) {
          let val = reply[key].v
          if (key === 'Devices') {
            var devices = {}
            val.forEach(function(dev) {
              let dfd = self.buildObject(dev)
              dfd.then((res) => devices[res.IpInterface] = res)
            })
            obj['Devices'] = devices
          } else if (typeof val === 'string' &&
              val.indexOf("/org/freedesktop") >=0) {
            if (!(val in self.seen)) {
              let dfd = self.buildObject(val)
              dfd.then(function(res) {
                obj[key] = res
              })
            }
          } else {
            obj[key] = val
          }
        }
        self.seen[path] = obj
        resolve(obj)
      })
    })
    return prom
  }
  listNics() {
    return this.model
  }
  refreshNics() {
    return new Promise((resolve, reject) => {
      let model = this.model;
      this.refresh();

      if (model !== {}) {
        resolve(this.model);
      } else {
        reject(Error("There was an error retrieving the list of network interfaces."));
      }
    })
  }
}
