const exec = require('child_process').exec;
const axios = require('axios');

const IRI_SERVICE = require('../service/iri.service');
const DB_SERVICE = require('../service/db.service');
const AUTH_SERVICE = require('../service/auth.service');
const NODE_STATE = require('../state/node.state');
const GLOBALS = require('../state/globals');

class NodeResource {
  init(app) {
    app.get(`${GLOBALS.BASE_URL}/node-info`, (req, res) => {
      if (!NODE_STATE.iriIp) {
        res.status(404).send('NODE_NOT_SET');
        return;
      }
      const pingStart = new Date();
      axios(IRI_SERVICE.createIriRequest('getNodeInfo'))
        .then(response => {
          const ping = new Date() - pingStart;
          NODE_STATE.currentOwnNodeInfo = response.data;
          NODE_STATE.currentOwnNodeInfo.ping = ping;
          res.json(NODE_STATE.currentOwnNodeInfo);
        })
        .catch(error => {
          console.log('Could not fetch node info.', error.message);
          if (!NODE_STATE.iriIp) {
            res.status(500).send('NODE_NOT_SET');
          } else {
            res.status(500).send('NODE_INACCESSIBLE');
          }
        });
    });

    app.post(`${GLOBALS.BASE_URL}/host-node-ip`, (req, res) => {
      const {
        protocol,
        nodeIp,
        port,
        password,
        iriPath,
        restartNodeCommand
      } = req.body;
      NODE_STATE.restartNodeCommand = restartNodeCommand;

      if (!nodeIp || !port) {
        res.status(404).send();
        return;
      }
      if (!NODE_STATE.hashedPw && password)
        NODE_STATE.hashedPw = AUTH_SERVICE.hashPassword(password);

      if (
        password &&
        AUTH_SERVICE.isPasswordCorrect(password, NODE_STATE.hashedPw)
      ) {
        NODE_STATE.protocol = protocol;
        NODE_STATE.iriIp = nodeIp;
        NODE_STATE.iriPort = port;
        NODE_STATE.iriFileLocation = iriPath;
        NODE_STATE.loginToken = new Date()
          .toString()
          .split('')
          .reverse()
          .join('');
        DB_SERVICE.setupHost(
          NODE_STATE.protocol,
          NODE_STATE.iriIp,
          NODE_STATE.iriPort,
          NODE_STATE.hashedPw,
          NODE_STATE.iriFileLocation,
          NODE_STATE.loginToken,
          NODE_STATE.restartNodeCommand
        );

        res.json({
          token: NODE_STATE.loginToken
        });
      } else if (!password) {
        NODE_STATE.protocol = protocol;
        NODE_STATE.iriIp = nodeIp;
        NODE_STATE.iriPort = port;

        DB_SERVICE.changeHostAddress(
          NODE_STATE.protocol,
          NODE_STATE.iriIp,
          NODE_STATE.iriPort
        );
        res.status(200).send();
      } else {
        res.status(403).send();
      }
    });

    app.get(`${GLOBALS.BASE_URL}/iri-details`, (req, res) => {
      if (!AUTH_SERVICE.isUserAuthenticated(NODE_STATE.loginToken, req)) {
        res.status(401).send();
        return;
      }
      res.send({
        protocol: NODE_STATE.protocol,
        nodeIp: NODE_STATE.iriIp,
        port: NODE_STATE.iriPort,
        iriFileLocation: NODE_STATE.iriFileLocation,
        restartNodeCommandAvailable: NODE_STATE.restartNodeCommand
          ? true
          : false
      });
    });

    app.get(`${GLOBALS.BASE_URL}/persisted-neighbors`, async (req, res) => {
      if (!AUTH_SERVICE.isUserAuthenticated(NODE_STATE.loginToken, req)) {
        res.status(401).send();
        return;
      }
      try {
        NODE_STATE.persistedNeighbors = await IRI_SERVICE.readPersistedNeighbors();
        res.send(NODE_STATE.persistedNeighbors);
      } catch (e) {
        console.log(
          'Failed to fetch persisted neighbors. The path is probably wrong or not set.',
          e.message
        );
        res.status(404).send();
      }
    });

    app.post(`${GLOBALS.BASE_URL}/restart-node`, async (req, res) => {
      if (!AUTH_SERVICE.isUserAuthenticated(NODE_STATE.loginToken, req)) {
        res.status(401).send();
        return;
      }
      exec(NODE_STATE.restartNodeCommand, (error, stdout, stderr) => {
        if (error || stderr) {
          console.log('Unsuccessful restart node attempt', error.message);
          res.status(500).send();
          return;
        }
        DB_SERVICE.deleteWholeNeighborHistory();
        res.status(200).send();
      });
    });

    app.post(`${GLOBALS.BASE_URL}/reset-database`, async (req, res) => {
      if (!AUTH_SERVICE.isUserAuthenticated(NODE_STATE.loginToken, req)) {
        res.status(401).send();
        return;
      }
      try {
        await DB_SERVICE.dropAllTables();
        NODE_STATE.initialize();
        DB_SERVICE.createAndInitializeTables();
        res.status(200).send();
      } catch (e) {
        console.log('Error while dropping and reinitializing DB.', e.message);
      }
    });
  }
}

const nodeResource = new NodeResource();
module.exports = nodeResource;
