import {
  findKey,
  isEqual,
  partial
} from 'lodash';
import debug from 'debug';
import nodemiral from 'nodemiral';

const log = debug('mup:docker:swarm');

export function findNodeId(nodeIDs, serverName) {
  return findKey(nodeIDs, partial(isEqual, serverName));
}

export function initSwarm(manager, host, api) {
  const list = nodemiral.taskList('Setting Up Docker Swarm');
  const sessions = api.getSessionsForServers([manager]);

  list.executeScript('Creating Manager', {
    script: api.resolvePath(__dirname, 'assets/init-swarm.sh'),
    vars: {
      host
    }
  });

  return api.runTaskList(list, sessions, { verbose: api.getVerbose() });
}

export function promoteNodes(manager, nodeIds, api) {
  const list = nodemiral.taskList('Promoting Nodes to Managers');
  const sessions = api.getSessionsForServers([manager]);

  log('promoting nodes:', nodeIds);
  list.executeScript('Promoting Nodes', {
    script: api.resolvePath(__dirname, 'assets/swarm-promote.sh'),
    vars: {
      nodeIds
    }
  });

  return api.runTaskList(list, sessions, { verbose: api.getVerbose() });
}

export function demoteManagers(manager, nodeIds, api) {
  const list = nodemiral.taskList('Demoting Swarm Managers');
  const sessions = api.getSessionsForServers([manager]);

  log('demoting nodes:', nodeIds, manager);

  list.executeScript('Demoting Managers', {
    script: api.resolvePath(__dirname, 'assets/swarm-demote.sh'),
    vars: {
      nodeIds
    }
  });

  return api.runTaskList(list, sessions, { verbose: api.getVerbose() });
}

export function joinNodes(servers, token, managerIP, api) {
  const list = nodemiral.taskList('Add Swarm Nodes');
  const sessions = api.getSessionsForServers(servers);

  list.executeScript('Joining node', {
    script: api.resolvePath(__dirname, 'assets/swarm-join.sh'),
    vars: {
      token,
      managerIP
    }
  });

  return api.runTaskList(list, sessions, { verbose: api.getVerbose() });
}

export function diffLabels(currentLabels, desiredLabels) {
  const toRemove = [];
  const toAdd = [];

  // check for labels to add or update
  Object.keys(desiredLabels).forEach(server => {
    for (const [label, value] of Object.entries(desiredLabels[server])) {
      if (!currentLabels[server] || currentLabels[server][label] !== value) {
        toAdd.push({ server, label, value });
      }
    }
  });

  // check for labels no longer used
  Object.keys(currentLabels).forEach(server => {
    for (const [label] of Object.entries(currentLabels[server])) {
      if (!desiredLabels[server] || !(label in desiredLabels[server])) {
        toRemove.push({ server, label });
      }
    }
  });

  return { toRemove, toAdd };
}

export function updateLabels(api, manager, toAdd, toRemove) {
  const list = nodemiral.taskList('Update Swarm Labels');
  const session = api.getSessionsForServers([manager]);

  log(`Adding labels ${JSON.stringify(toAdd)}`);

  list.executeScript('Update Labels', {
    script: api.resolvePath(__dirname, 'assets/swarm-labels.sh'),
    vars: {
      toAdd,
      toRemove
    }
  });

  return api.runTaskList(list, session, { verbose: api.getVerbose() });
}

@@ -260,24 +260,535 @@
 rName));%0A%7D%0A%0A
+export function swarmErrorSuggestions(err) %7B%0A  if (err.message.indexOf('certificate has expired or is not yet valid') %3E -1) %7B%0A    err.solution = 'Make sure the servers all have the same time.';%0A  %7D%0A%0A  throw err;%0A%7D%0A%0Aexport function executeSwarmTaskList(list, manager, api) %7B%0A  const servers = manager instanceof Array ? manager : %5Bmanager%5D;%0A  const sessions = api.getSessionsForServers(servers);%0A%0A  return api%0A    .runTaskList(list, sessions, %7B verbose: api.getVerbose() %7D)%0A    .catch(swarmErrorSuggestions);%0A%7D%0A%0A
 export funct
@@ -889,66 +889,45 @@
 ');%0A
+%0A
   
-const sessions = api.getSessionsForServers(%5B
+log('creating manager', 
 manager
-%5D
 );%0A
-%0A
   li
@@ -1066,39 +1066,44 @@
   %7D);%0A%0A  return 
-api.run
+executeSwarm
 TaskList(list, s
@@ -1105,47 +1105,20 @@
 st, 
-sessions, %7B verbose: api.getVerbose() %7D
+manager, api
 );%0A%7D
@@ -1242,65 +1242,8 @@
 s');
-%0A  const sessions = api.getSessionsForServers(%5Bmanager%5D);
 %0A%0A  
@@ -1425,39 +1425,44 @@
   %7D);%0A%0A  return 
-api.run
+executeSwarm
 TaskList(list, s
@@ -1464,47 +1464,20 @@
 st, 
-sessions, %7B verbose: api.getVerbose() %7D
+manager, api
 );%0A%7D
@@ -1599,65 +1599,8 @@
 s');
-%0A  const sessions = api.getSessionsForServers(%5Bmanager%5D);
 %0A%0A  
@@ -1792,39 +1792,44 @@
   %7D);%0A%0A  return 
-api.run
+executeSwarm
 TaskList(list, s
@@ -1831,47 +1831,20 @@
 st, 
-sessions, %7B verbose: api.getVerbose() %7D
+manager, api
 );%0A%7D
@@ -1962,63 +1962,8 @@
 s');
-%0A  const sessions = api.getSessionsForServers(servers);
 %0A%0A  
@@ -2118,39 +2118,44 @@
   %7D);%0A%0A  return 
-api.run
+executeSwarm
 TaskList(list, s
@@ -2159,45 +2159,18 @@
 , se
-ssions, %7B verbose: api.getVerbose() %7D
+rvers, api
 );%0A%7D
@@ -3026,64 +3026,8 @@
 s');
-%0A  const session = api.getSessionsForServers(%5Bmanager%5D);
 %0A%0A  
@@ -3246,15 +3246,20 @@
 urn 
-api.run
+executeSwarm
 Task
@@ -3273,46 +3273,20 @@
 st, 
-session, %7B verbose: api.getVerbose() %7D
+manager, api
 );%0A%7D

Add solution for error joining nodes due to servers having different time