import { Meteor } from 'meteor/meteor';
import { ValidatedMethod } from 'meteor/mdg:validated-method';
import SimpleSchema from 'simpl-schema';
import _ from 'underscore';
import fs from 'fs';
import { CallPromiseMixin } from 'meteor/didericis:callpromise-mixin';
import pAll from 'p-all';

import Nodes from '../nodes/nodes.js';
import Subscribers from '../subscribers/subscribers.js';
import Parameters from '../parameters/parameters.js';
import monitoringConfTools from './monitoringConfTools.js';
import getParameter from '../parameters/getParameter.js';

export const generateIcingaConf = new ValidatedMethod({
  name: 'monitoring.generateIcingaConf',
  validate: null,
  run() {
    if (Meteor.isServer) {
      const nodes = Nodes.find({}).fetch();
      const icingaConfiguration = getParameter('icinga_configuration');
      let icingaEndpoints;
      if (icingaConfiguration.endpoints && icingaConfiguration.endpoints.length > 0) {
        icingaEndpoints = {};
        icingaConfiguration.endpoints.forEach((endpoint) => {
          icingaEndpoints[endpoint.gatewayNodeId] = endpoint.endpointFQDN;
        });
        icingaEndpoints.default = icingaConfiguration.endpoints[0].endpointFQDN;
      }

      const hostArray = [];
      const hostArrayNagmap = [];
      nodes.forEach(function (node) {
        if (_.where(nodes, { _id: node._id }).length > 1) {
          throw new Meteor.Error('duplicated-node', 'The node ' + node.name + ' seems to be duplicated in the database.');
        }

        if (node.parent) {
          const parentInExportedNodes = _.findWhere(nodes, { _id: node.parent });
          if (!parentInExportedNodes) {
            const parentInDb = Nodes.findOne({ _id: node.parent });
            if (!parentInDb) {
              throw new Meteor.Error('missing-parent', 'The parent of the node ' + node.name + ' is missing from the database');
            } else {
              throw new Meteor.Error('missing-node', 'The node ' + parentInDb.name + ' is not exported for an unknown reason but has one or more children.');
            }
          } else if (parentInExportedNodes.type === 'cpe') {
            throw new Meteor.Error('cpe-with-children', 'The node ' + parentInExportedNodes.name + ' is a CPE but has one or more children. Please look for a type change on this node.');
          }
        }

        hostArray.push(monitoringConfTools.getIcingaHost(node, nodes, icingaEndpoints));
        if (!node.solarScheduled && !node.intermittentPower) {
          let isNagMapHost = true;
          if (node.type === 'cpe' && node.subscriberId) {
            const sub = Subscribers.findOne({ _id: node.subscriberId });
            if (
              sub.status === 'suspended'
              || sub.status === 'disconnected'
              || sub.plan === 'hold'
              || sub.plan === 'disconnected'
            ) {
              isNagMapHost = false;
            }
          }
          if (node.type === 'cpe' && !node.subscriberId) {
            isNagMapHost = false;
          }
          // Nagmap hosts file is different, it doesnt include
          // solarScheduled and intermittentPower nodes
          // It also does not included suspended/disconnected and no subscriber CPE's
          if (isNagMapHost) {
            hostArrayNagmap.push(monitoringConfTools.getIcingaHost(node, nodes, icingaEndpoints));
          }
        }
      });

      // Add PPP hosts
      const pppHosts = Parameters.findOne({ _id: 'customerPortalPPPInfo' }).value;
      pppHosts.forEach((host) => {
        const name = host.name;
        hostArray.push(monitoringConfTools.getPPPHosts(name, host[name + '-IP'], host[name + '-GW'], icingaEndpoints));
      });

      const fileFullPath = 'assets/app/hosts.conf';
      if (fs.existsSync(fileFullPath)) {
        fs.unlinkSync(fileFullPath);
      }
      fs.writeFile(fileFullPath, hostArray.join('\n'), function (err) {
        if (err) {
          console.error('Error inserting new icinga conf file.');
          console.error(err);
          throw new Meteor.Error('file-save-failed', 'An error has occurred while saving the file. Please retry.');
        }
      });

      // Write nagmap conf file.
      // Nagmap conf file does not include solarScheduled and intermittentPower hosts
      const fileFullPathNagmap = 'assets/app/nagmap-hosts';
      if (fs.existsSync(fileFullPathNagmap)) {
        fs.unlinkSync(fileFullPathNagmap);
      }
      fs.writeFile(fileFullPathNagmap, hostArrayNagmap.join('\n'), function (err) {
        if (err) {
          console.error('Error inserting new nagmap conf file.');
          console.error(err);
          throw new Meteor.Error('nagmap-conf-file-save-failed', 'An error has occurred while saving the file. Please retry.');
        }
      });
    }
  },
});

export const uploadIcingaConf = new ValidatedMethod({
  name: 'monitoring.uploadIcingaConf',
  validate: null,
  run() {
    if (Meteor.isServer) {
      this.unblock();
      /* eslint-disable global-require */
      const scpClient = require('scp2');
      /* eslint-enable global-require */
      const icingaConfiguration = getParameter('icinga_configuration');
      const scpSync = Meteor.wrapAsync(scpClient.scp);
      scpSync('assets/app/hosts.conf', {
        host: icingaConfiguration.serverName,
        username: icingaConfiguration.managementUser,
        privateKey: fs.readFileSync(icingaConfiguration.sshPrivateKeyPath),
        path: icingaConfiguration.configurationDirectory
      });

      // Copy nagmap hosts file
      scpSync('assets/app/nagmap-hosts', {
        host: icingaConfiguration.serverName,
        username: icingaConfiguration.managementUser,
        privateKey: fs.readFileSync(icingaConfiguration.sshPrivateKeyPath),
        path: icingaConfiguration.configurationDirectory
      });
    }
  }
});

export const reloadIcingaServer = new ValidatedMethod({
  name: 'monitoring.reloadIcingaServer',
  validate: null,
  run() {
    if (Meteor.isServer) {
      this.unblock();
      const icingaConfiguration = getParameter('icinga_configuration');
      const connectionInfo = {
        host: icingaConfiguration.serverName,
        username: icingaConfiguration.managementUser,
        privateKey: fs.readFileSync(icingaConfiguration.sshPrivateKeyPath)
      };
      const sshExecSync = Meteor.wrapAsync(monitoringConfTools.sshExec);
      sshExecSync(connectionInfo, icingaConfiguration.reloadCommand);
    }
  }
});

export const generateSmokepingConf = new ValidatedMethod({
  name: 'monitoring.generateSmokepingConf',
  validate: null,
  run() {
    if (Meteor.isServer) {
      let routers = Nodes.find({ type: 'router' }, { sort: { name: 1 } }).fetch();
      routers = _.sortBy(routers, function (node) {
        return monitoringConfTools.ipv4ToLong(node.managementIp);
      });

      const hostArray = [];
      /* eslint-disable no-undef */
      hostArray.push(Assets.getText('smokepingGenericTargets'));
      /* eslint-enable no-undef */
      let probeIndex = 0;
      let devicesOnProbe = 0;
      routers.forEach((router) => {
        hostArray.push(monitoringConfTools.getSmokepingGroup(router));
        hostArray.push(monitoringConfTools.getSmokepingHost(router, probeIndex));
        devicesOnProbe += 1;
        const dependentNonRouterNodeIds = monitoringConfTools.getDependentNonRouterNodeIds(router);
        dependentNonRouterNodeIds.forEach((nodeId) => {
          devicesOnProbe += 1;
          if (devicesOnProbe >= 500) {
            devicesOnProbe = 0;
            probeIndex += 1;
          }
          const child = Nodes.findOne(nodeId);
          hostArray.push(monitoringConfTools.getSmokepingHost(child, probeIndex));
        });
      });

      const fileFullPath = 'assets/app/Targets';
      if (fs.existsSync(fileFullPath)) {
        fs.unlinkSync(fileFullPath);
      }
      fs.writeFile(fileFullPath, hostArray.join('\n'), function (err) {
        if (err) {
          console.error('Error inserting new smokeping conf file.');
          console.error(err);
          throw new Meteor.Error('file-save-failed', 'An error has occurred while saving the file. Please retry.');
        }
      });
    }
  }
});

export const uploadSmokepingConf = new ValidatedMethod({
  name: 'monitoring.uploadSmokepingConf',
  mixins: [CallPromiseMixin],
  validate: null,
  run() {
    let result;
    if (Meteor.isServer) {
      /* eslint-disable global-require */
      const scpClient = require('scp2');
      /* eslint-enable global-require */
      this.unblock();
      const smokepingConfiguration = getParameter('smokeping_configuration');
      const copyPromises = [];
      smokepingConfiguration.servers.forEach((server) => {
        copyPromises.push(() => new Promise((resolve, reject) => {
          scpClient.scp('assets/app/Targets', {
            host: server.serverName,
            username: smokepingConfiguration.managementUser,
            privateKey: fs.readFileSync(smokepingConfiguration.sshPrivateKeyPath),
            path: smokepingConfiguration.configurationDirectory
          }, function (err) { if (err) { reject(err); } else { resolve(); } });
        }));
      });

      result = pAll(copyPromises);
    }
    return result;
  }
});

export const reloadSmokepingServer = new ValidatedMethod({
  name: 'monitoring.reloadSmokepingServer',
  mixins: [CallPromiseMixin],
  validate: null,
  run() {
    let result;
    if (Meteor.isServer) {
      this.unblock();
      const smokepingConfiguration = getParameter('smokeping_configuration');
      const execPromises = [];
      smokepingConfiguration.servers.forEach((server) => {
        execPromises.push(() => new Promise((resolve, reject) => {
          const connectionInfo = {
            host: server.serverName,
            username: smokepingConfiguration.managementUser,
            privateKey: fs.readFileSync(smokepingConfiguration.sshPrivateKeyPath),
          };
          monitoringConfTools.sshExec(
            connectionInfo, smokepingConfiguration.reloadCommand,
            function (err) { if (err) { reject(err); } else { resolve(); } }
          );
        }));
      });

      result = pAll(execPromises);
    }
    return result;
  }
});

export const getSmokepingTarget = new ValidatedMethod({
  name: 'monitoring.getSmokepingTarget',
  validate: new SimpleSchema({
    node: { type: Object, blackbox: true },
    'node._id': Nodes.simpleSchema().schema('_id'),
    'node.type': Nodes.simpleSchema().schema('type'),
    'node.parent': Nodes.simpleSchema().schema('parent'),
  }).validator(),
  run({ node }) {
    let result;
    if (Meteor.isServer) {
      const routerMacId = monitoringConfTools.getUpstreamRouterId(node);
      result = routerMacId + 'group.' + node._id;
    }
    return result;
  },
});
export const getSmokepingServerData = new ValidatedMethod({
  name: 'monitoring.getSmokepingServerData',
  validate: new SimpleSchema({
    node: { type: Object, blackbox: true },
    'node._id': Nodes.simpleSchema().schema('_id'),
    'node.internetGatewayId': Nodes.simpleSchema().schema('_id')
  }).validator(),
  run({ node }) {
    let result;
    if (Meteor.isServer) {
      const smokepingConfiguration = getParameter('smokeping_configuration');
      if (smokepingConfiguration && smokepingConfiguration.servers.length > 0) {
        let server = _.find(smokepingConfiguration.servers, function (serverItem) {
          return serverItem.gatewayNodeId === node.internetGatewayId ||
            serverItem.gatewayNodeId === node._id;
        });
        if (!server) {
          server = smokepingConfiguration.servers[0];
        }
        result = {
          urlBase: server.urlBase,
          serverName: server.serverName
        };
      }
    }
    return result;
  }
});
export const getSmokepingUrl = new ValidatedMethod({
  name: 'monitoring.getSmokepingUrl',
  validate: new SimpleSchema({
    nodeId: Nodes.simpleSchema().schema('_id'),
  }).validator(),
  run({ nodeId }) {
    let result;
    if (Meteor.isServer) {
      const smokepingConfiguration = getParameter('smokeping_configuration');
      if (smokepingConfiguration && smokepingConfiguration.servers.length > 0) {
        const node = Nodes.findOne(nodeId);
        const routerMacId = monitoringConfTools.getUpstreamRouterId(node);
        let server = _.find(smokepingConfiguration.servers, function (serverItem) {
          return serverItem.gatewayNodeId === node.internetGatewayId ||
            serverItem.gatewayNodeId === nodeId;
        });
        if (!server) {
          server = smokepingConfiguration.servers[0];
        }
        result = server.urlBase + '/?target=' + routerMacId + 'group.' + node._id;
      }
    }
    return result;
  },
});
export const copyIcingaNotification = new ValidatedMethod({
  name: 'monitoring.copyIcingaNotification',
  validate: new SimpleSchema({
    filePath: { type: String }
  }).validator(),
  run({ filePath }) {
    if (Meteor.isServer) {
      /* eslint-disable global-require */
      const scpClient = require('scp2');
      /* eslint-enable global-require */
      const icingaConfiguration = getParameter('icinga_configuration');
      const scpSync = Meteor.wrapAsync(scpClient.scp);
      scpSync(filePath, {
        host: icingaConfiguration.serverName,
        username: icingaConfiguration.managementUser,
        privateKey: fs.readFileSync(icingaConfiguration.sshPrivateKeyPath),
        path: icingaConfiguration.configurationDirectory + '/notifications.conf'
      });
      reloadIcingaServer.call();
    }
  }
});
