import * as fs from 'fs';
import { join, dirname, isAbsolute, resolve, sep } from 'path';
import { fileURLToPath } from 'url';
import * as crypto from 'crypto';
import request from 'request-promise';
import { Logger, LoggerService } from './logger.js';
import { ApplicationConfig, SelectedNode } from '../models/config.model.js';

export class CommonService {

  public logger: LoggerService = Logger;
  public nodes: SelectedNode[] = [];
  public selectedNode: SelectedNode = null;
  public ssoInit = { rtlSso: 0, rtlCookiePath: '', logoutRedirectLink: '', cookieValue: '' };
  public appConfig: ApplicationConfig = { defaultNodeIndex: 0, selectedNodeIndex: 0, rtlConfFilePath: '', dbDirectoryPath: join(dirname(fileURLToPath(import.meta.url)), '..', '..'), rtlPass: '', allowPasswordUpdate: true, enable2FA: false, secret2FA: '', SSO: this.ssoInit, nodes: [] };
  public port = 3000;
  public host = '';
  public secret_key = crypto.randomBytes(64).toString('hex');
  public read_dummy_data = false;
  public baseHref = '/rtl';
  private dummy_data_array_from_file = [];
  private MONTHS = [
    { name: 'JAN', days: 31 }, { name: 'FEB', days: 28 }, { name: 'MAR', days: 31 }, { name: 'APR', days: 30 }, { name: 'MAY', days: 31 }, { name: 'JUN', days: 30 },
    { name: 'JUL', days: 31 }, { name: 'AUG', days: 31 }, { name: 'SEP', days: 30 }, { name: 'OCT', days: 31 }, { name: 'NOV', days: 30 }, { name: 'DEC', days: 31 }
  ];

  constructor() {}

  public maskPasswords = (obj) => {
    const keys = Object.keys(obj);
    const length = keys.length;
    if (length !== 0) {
      for (let i = 0; i < length; i++) {
        if (typeof obj[keys[i]] === 'object') {
          keys[keys[i]] = this.maskPasswords(obj[keys[i]]);
        }
        if (typeof keys[i] === 'string' &&
          ((keys[i].toLowerCase().includes('password') && keys[i] !== 'allowPasswordUpdate') || keys[i].toLowerCase().includes('multipass') ||
            keys[i].toLowerCase().includes('rpcpass') || keys[i].toLowerCase().includes('rpcpassword') ||
            keys[i].toLowerCase().includes('rpcuser'))
        ) {
          obj[keys[i]] = '*'.repeat(20);
        }
      }
    }
    return obj;
  };

  public removeAuthSecureData = (node: SelectedNode) => {
    delete node.authentication.macaroonPath;
    delete node.authentication.runePath;
    delete node.authentication.runeValue;
    delete node.authentication.lnApiPassword;
    delete node.authentication.options;
    return node;
  };

  public removeSecureData = (config: ApplicationConfig) => {
    delete config.rtlConfFilePath;
    delete config.rtlPass;
    delete config.multiPass;
    delete config.multiPassHashed;
    delete config.secret2FA;
    config.nodes?.map((node) => this.removeAuthSecureData(node));
    return config;
  };

  public addSecureData = (config: ApplicationConfig) => {
    config.rtlConfFilePath = this.appConfig.rtlConfFilePath;
    config.rtlPass = this.appConfig.rtlPass;
    config.multiPassHashed = this.appConfig.multiPassHashed;
    config.SSO.rtlCookiePath = this.appConfig.SSO.rtlCookiePath;
    if (this.appConfig.multiPass) {
      config.multiPass = this.appConfig.multiPass;
    }
    if (config.secret2FA === this.appConfig.secret2FA) {
      config.secret2FA = this.appConfig.secret2FA;
    }
    config.nodes.map((node, i) => {
      if (this.appConfig && this.appConfig.nodes && this.appConfig.nodes.length > i && this.appConfig.nodes[i].authentication) {
        if (this.appConfig.nodes[i].authentication.macaroonPath) {
          node.authentication.macaroonPath = this.appConfig.nodes[i].authentication.macaroonPath;
        }
        if (this.appConfig.nodes[i].authentication.runePath) {
          node.authentication.runePath = this.appConfig.nodes[i].authentication.runePath;
        }
        if (this.appConfig.nodes[i].authentication.lnApiPassword) {
          node.authentication.lnApiPassword = this.appConfig.nodes[i].authentication.lnApiPassword;
        }
      }
      return node;
    });
    return config;
  };

  public setSwapServerOptions = (req) => {
    const swapOptions = {
      baseUrl: req.session.selectedNode.settings.swapServerUrl,
      uri: '',
      rejectUnauthorized: false,
      json: true,
      headers: { 'Grpc-Metadata-macaroon': '' }
    };
    if (req.session.selectedNode.authentication.swapMacaroonPath) {
      try {
        swapOptions.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.authentication.swapMacaroonPath, 'loop.macaroon')).toString('hex') };
      } catch (err) {
        this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Loop macaroon Error', error: err });
      }
    }
    this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Swap Options', data: swapOptions });
    return swapOptions;
  };

  public getBoltzServerOptions = (req) => {
    const boltzOptions = {
      url: req.session.selectedNode.settings.boltzServerUrl,
      rejectUnauthorized: false,
      json: true,
      headers: { 'Grpc-Metadata-macaroon': '' }
    };
    if (req.session.selectedNode.authentication.boltzMacaroonPath) {
      try {
        boltzOptions.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.authentication.boltzMacaroonPath, 'admin.macaroon')).toString('hex') };
      } catch (err) {
        this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Boltz macaroon Error', error: err });
      }
    }
    this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Boltz Options', data: boltzOptions });
    return boltzOptions;
  };

  public getOptions = (req) => {
    if (req.session.selectedNode && req.session.selectedNode.authentication.options) {
      req.session.selectedNode.authentication.options.method = (req.session.selectedNode.lnImplementation && req.session.selectedNode.lnImplementation.toUpperCase() === 'LND') ? 'GET' : 'POST';
      delete req.session.selectedNode.authentication.options.form;
      delete req.session.selectedNode.authentication.options.body;
      req.session.selectedNode.authentication.options.qs = {};
      return req.session.selectedNode.authentication.options;
    }
    return this.handleError({ statusCode: 401, message: 'Session expired after a day\'s inactivity' }, 'Session Expired', 'Session Expiry Error', this.selectedNode);
  };

  public updateSelectedNodeOptions = (req) => {
    if (!req.session.selectedNode) {
      req.session.selectedNode = {};
    }
    req.session.selectedNode.authentication.options = {
      url: '',
      rejectUnauthorized: false,
      json: true,
      form: null
    };
    try {
      if (req.session.selectedNode && req.session.selectedNode.lnImplementation) {
        switch (req.session.selectedNode.lnImplementation.toUpperCase()) {
          case 'CLN':
            try {
              if (!req.session.selectedNode.authentication.runeValue) {
                req.session.selectedNode.authentication.runeValue = this.getRuneValue(req.session.selectedNode.authentication.runePath);
              }
              req.session.selectedNode.authentication.options.headers = { rune: req.session.selectedNode.authentication.runeValue };
            } catch (err) {
              throw new Error(err);
            }
            break;

          case 'ECL':
            req.session.selectedNode.authentication.options.headers = { authorization: 'Basic ' + Buffer.from(':' + req.session.selectedNode.authentication.lnApiPassword).toString('base64') };
            break;

          default:
            req.session.selectedNode.authentication.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(req.session.selectedNode.authentication.macaroonPath, 'admin.macaroon')).toString('hex') };
            break;
        }
      }
      if (req.session.selectedNode) {
        this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Updated Node Options for ' + req.session.selectedNode.lnNode, data: req.session.selectedNode.authentication.options });
      }
      return { status: 200, message: 'Updated Successfully' };
    } catch (err) {
      req.session.selectedNode.authentication.options = {
        url: '',
        rejectUnauthorized: false,
        json: true,
        form: null
      };
      this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Update Selected Node Options Error', error: err });
      return { status: 502, message: err };
    }
  };

  public getRuneValue = (rune_path) => {
    const data = fs.readFileSync(rune_path, 'utf8');
    const pattern = /LIGHTNING_RUNE="(?<runeValue>[^"]+)"/;
    const match = data.match(pattern);
    if (match.groups.runeValue) {
      return match.groups.runeValue;
    } else {
      throw new Error('Rune not found in the file.');
    }
  };

  public setOptions = (req) => {
    if (this.nodes[0].authentication.options && this.nodes[0].authentication.options.headers) { return; }
    if (this.nodes && this.nodes.length > 0) {
      this.nodes.forEach((node) => {
        node.authentication.options = {
          url: '',
          rejectUnauthorized: false,
          json: true,
          form: null
        };
        try {
          if (node.lnImplementation) {
            switch (node.lnImplementation.toUpperCase()) {
              case 'CLN':
                try {
                  if (!node.authentication.runeValue) {
                    node.authentication.runeValue = this.getRuneValue(node.authentication.runePath);
                  }
                  node.authentication.options.headers = { rune: node.authentication.runeValue };
                } catch (err) {
                  throw new Error(err);
                }
                break;

              case 'ECL':
                node.authentication.options.headers = { authorization: 'Basic ' + Buffer.from(':' + node.authentication.lnApiPassword).toString('base64') };
                break;

              default:
                node.authentication.options.headers = { 'Grpc-Metadata-macaroon': fs.readFileSync(join(node.authentication.macaroonPath, 'admin.macaroon')).toString('hex') };
                break;
            }
          }
        } catch (err) {
          this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Common Set Options Error', error: err });
          node.authentication.options = {
            url: '',
            rejectUnauthorized: false,
            json: true,
            form: ''
          };
        }
        this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Set Node Options for ' + node.lnNode, data: node.authentication.options });
      });
      this.updateSelectedNodeOptions(req);
    }
  };

  public findNode = (selNodeIndex) => this.nodes.find((node) => node.index === selNodeIndex);

  public replaceNode = (req, newNode) => {
    const foundIndex = this.nodes.findIndex((node) => node.index === req.session.selectedNode.index);
    this.nodes.splice(foundIndex, 1, newNode);
    req.session.selectedNode = this.findNode(req.session.selectedNode.index);
  };

  public convertTimeToEpoch = (timeToConvert: Date) => Math.floor(timeToConvert.getTime() / 1000);

  public convertTimestampToTime = (num) => {
    const myDate = new Date(+num * 1000);
    let days = myDate.getDate().toString();
    days = +days < 10 ? '0' + days : days;
    let hours = myDate.getHours().toString();
    hours = +hours < 10 ? '0' + hours : hours;
    let minutes = myDate.getMinutes().toString();
    minutes = +minutes < 10 ? '0' + minutes : minutes;
    let seconds = myDate.getSeconds().toString();
    seconds = +seconds < 10 ? '0' + seconds : seconds;
    return days + '/' + this.MONTHS[myDate.getMonth()].name + '/' + myDate.getFullYear() + ' ' + hours + ':' + minutes + ':' + seconds;
  };

  public sortAscByKey = (array, key) => array.sort((a, b) => {
    const x = +a[key];
    const y = +b[key];
    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  });

  public sortAscByStrKey = (array, key) => array.sort((a, b) => {
    const x = a[key] ? a[key].toUpperCase() : '';
    const y = b[key] ? b[key].toUpperCase() : '';
    return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  });

  public sortDescByKey = (array, key) => {
    const temp = array.sort((a, b) => {
      const x = +a[key] ? +a[key] : 0;
      const y = +b[key] ? +b[key] : 0;
      return (x > y) ? -1 : ((x < y) ? 1 : 0);
    });
    return temp;
  };

  public sortDescByStrKey = (array, key) => {
    const temp = array.sort((a, b) => {
      const x = a[key] ? a[key].toUpperCase() : '';
      const y = b[key] ? b[key].toUpperCase() : '';
      return (x > y) ? -1 : ((x < y) ? 1 : 0);
    });
    return temp;
  };

  public newestOnTop = (array, key, value) => {
    const newlyAddedRecord = array.splice(array.findIndex((item) => item[key] === value), 1);
    array?.unshift(newlyAddedRecord[0]);
    return array;
  };

  public camelCase = (str: string) => str?.replace(/(?:^\w|[A-Z]|\b\w)/g, (word, index) => (word.toUpperCase()))?.replace(/\s+/g, '')?.replace(/-/g, ' ');

  public titleCase = (str: string) => {
    if (str.indexOf('!\n') > 0 || str.indexOf('.\n') > 0) {
      return str.split('\n')?.reduce((accumulator, currentStr) => accumulator + currentStr.charAt(0).toUpperCase() + currentStr.substring(1).toLowerCase() + '\n', '');
    } else {
      if (str.indexOf(' ') > 0) {
        return str.split(' ')?.reduce((accumulator, currentStr) => accumulator + currentStr.charAt(0).toUpperCase() + currentStr.substring(1).toLowerCase() + ' ', '');
      } else {
        return str.charAt(0).toUpperCase() + str.substring(1).toLowerCase();
      }
    }
  };

  public handleError = (errRes, fileName, errMsg, selectedNode: SelectedNode) => {
    let err = JSON.parse(JSON.stringify(errRes));
    if (err && err.error && Object.keys(err.error).length === 0 && errRes.error && (errRes.error.stack || errRes.error.message)) {
      errRes.error = errRes.error.stack || errRes.error.message;
      err = JSON.parse(JSON.stringify(errRes));
    } else if (errRes.message || errRes.stack) {
      errRes.error = errRes.message || errRes.stack;
      err = JSON.parse(JSON.stringify(errRes));
    }
    if (!selectedNode) { selectedNode = this.selectedNode; }
    switch (selectedNode.lnImplementation) {
      case 'LND':
        if (err.options && err.options.headers && err.options.headers['Grpc-Metadata-macaroon']) {
          delete err.options.headers['Grpc-Metadata-macaroon'];
        }
        if (err.response && err.response.request && err.response.request.headers && err.response.request.headers['Grpc-Metadata-macaroon']) {
          delete err.response.request.headers['Grpc-Metadata-macaroon'];
        }
        break;

      case 'CLN':
        if (err.options && err.options.headers && err.options.headers.macaroon) {
          delete err.options.headers.macaroon;
        }
        if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.macaroon) {
          delete err.response.request.headers.macaroon;
        }
        break;

      case 'ECL':
        if (err.options && err.options.headers && err.options.headers.authorization) {
          delete err.options.headers.authorization;
        }
        if (err.response && err.response.request && err.response.request.headers && err.response.request.headers.authorization) {
          delete err.response.request.headers.authorization;
        }
        break;

      default:
        if (err.options && err.options.headers) { delete err.options.headers; }
        break;
    }
    this.logger.log({ selectedNode: selectedNode, level: 'ERROR', fileName: fileName, msg: errMsg, error: (typeof err === 'object' ? JSON.stringify(err) : err) });
    let newErrorObj = { statusCode: 500, message: '', error: '' };
    if (err.code && err.code === 'ENOENT') {
      newErrorObj = {
        statusCode: 500,
        message: 'No such file or directory ' + (err.path ? err.path : ''),
        error: 'No such file or directory ' + (err.path ? err.path : '')
      };
    } else {
      newErrorObj = {
        statusCode: err.statusCode ? err.statusCode : err.status ? err.status : (err.error && err.error.code && err.error.code === 'ECONNREFUSED') ? 503 : 500,
        message: (err.error && err.error.message) ? err.error.message : err.message ? err.message : errMsg,
        error: (
          (err.error && err.error.error && err.error.error.error && typeof err.error.error.error === 'string') ? err.error.error.error :
            (err.error && err.error.error && typeof err.error.error === 'string') ? err.error.error :
              (err.error && err.error.error && err.error.error.message && typeof err.error.error.message === 'string') ? err.error.error.message :
                (err.error && err.error.message && typeof err.error.message === 'string') ? err.error.message :
                  (err.error && typeof err.error === 'string') ? err.error :
                    (err.message && typeof err.message === 'string') ? err.message : (typeof err === 'string') ? err : 'Unknown Error'
        )
      };
    }
    if (selectedNode.lnImplementation === 'ECL' && err.message && err.message.indexOf('Authentication Error') < 0 && err.name && err.name === 'StatusCodeError') { newErrorObj.statusCode = 500; }
    return newErrorObj;
  };

  public getRequestIP = (req) => ((typeof req.headers['x-forwarded-for'] === 'string' && req.headers['x-forwarded-for'].split(',').shift()) ||
    req.ip ||
    req.connection.remoteAddress ||
    req.socket.remoteAddress ||
    (req.connection.socket ? req.connection.socket.remoteAddress : null));

  public getDummyData = (dataKey, lnImplementation) => {
    const dummyDataFile = this.appConfig.rtlConfFilePath + sep + 'ECLDummyData.log';
    return new Promise((resolve, reject) => {
      if (this.dummy_data_array_from_file.length === 0) {
        fs.readFile(dummyDataFile, 'utf8', (err, data) => {
          if (err) {
            if (err.code === 'ENOENT') {
              this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Dummy data file does not exist' });
            } else {
              this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Getting dummy data failed' });
            }
          } else {
            this.dummy_data_array_from_file = data.split('\n');
            resolve(this.filterData(dataKey, lnImplementation));
          }
        });
      } else {
        resolve(this.filterData(dataKey, lnImplementation));
      }
    });
  };

  public readCookie = () => {
    const exists = fs.existsSync(this.appConfig.SSO.rtlCookiePath);
    if (exists) {
      try {
        this.appConfig.SSO.cookieValue = fs.readFileSync(this.appConfig.SSO.rtlCookiePath, 'utf-8');
      } catch (err) {
        this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while reading cookie: \n' + err });
        throw new Error(err);
      }
    } else {
      try {
        const directoryName = dirname(this.appConfig.SSO.rtlCookiePath);
        this.createDirectory(directoryName);
        fs.writeFileSync(this.appConfig.SSO.rtlCookiePath, crypto.randomBytes(64).toString('hex'));
        this.appConfig.SSO.cookieValue = fs.readFileSync(this.appConfig.SSO.rtlCookiePath, 'utf-8');
      } catch (err) {
        this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while reading the cookie: \n' + err });
        throw new Error(err);
      }
    }
  };

  public refreshCookie = () => {
    try {
      fs.writeFileSync(this.appConfig.SSO.rtlCookiePath, crypto.randomBytes(64).toString('hex'));
      this.appConfig.SSO.cookieValue = fs.readFileSync(this.appConfig.SSO.rtlCookiePath, 'utf-8');
    } catch (err) {
      this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Something went wrong while refreshing cookie', error: err });
      throw new Error(err);
    }
  };

  public createDirectory = (directoryName) => {
    const initDir = isAbsolute(directoryName) ? sep : '';
    directoryName.split(sep)?.reduce((parentDir, childDir) => {
      const curDir = resolve(parentDir, childDir);
      try {
        if (!fs.existsSync(curDir)) {
          fs.mkdirSync(curDir);
        }
      } catch (err) {
        if (err.code !== 'EEXIST') {
          if (err.code === 'ENOENT') {
            throw new Error(`ENOENT: No such file or directory, mkdir '${directoryName}'. Ensure that the path separator is '${sep}'`);
          } else {
            throw err;
          }
        }
      }
      return curDir;
    }, initDir);
  };

  public replacePasswordWithHash = (multiPassHashed) => {
    this.appConfig.rtlConfFilePath = process.env.RTL_CONFIG_PATH ? process.env.RTL_CONFIG_PATH : join(dirname(fileURLToPath(import.meta.url)), '../..');
    try {
      const RTLConfFile = this.appConfig.rtlConfFilePath + sep + 'RTL-Config.json';
      const config = JSON.parse(fs.readFileSync(RTLConfFile, 'utf-8'));
      config.multiPassHashed = multiPassHashed;
      delete config.multiPass;
      fs.writeFileSync(RTLConfFile, JSON.stringify(config, null, 2), 'utf-8');
      this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Please note that, RTL has encrypted the plaintext password into its corresponding hash' });
      return config.multiPassHashed;
    } catch (err) {
      this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Password hashing failed', error: err });
    }
  };

  public getAllNodeAllChannelBackup = (node: SelectedNode) => {
    const channel_backup_file = node.settings.channelBackupPath + sep + 'channel-all.bak';
    const options = {
      url: node.settings.lnServerUrl + '/v1/channels/backup',
      rejectUnauthorized: false,
      json: true,
      headers: { 'Grpc-Metadata-macaroon': fs.readFileSync(node.authentication.macaroonPath + '/admin.macaroon').toString('hex') }
    };
    this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Getting Channel Backup for Node ' + node.lnNode + '..' });
    request(options).then((body) => {
      fs.writeFile(channel_backup_file, JSON.stringify(body), (err) => {
        if (err) {
          if (node.lnNode) {
            this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for Node ' + node.lnNode, error: err });
          } else {
            this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for File ' + channel_backup_file, error: err });
          }
        } else {
          if (node.lnNode) {
            this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Successful in Channel Backup for Node ' + node.lnNode, data: body });
          } else {
            this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Successful in Channel Backup for File ' + channel_backup_file, data: body });
          }
        }
      });
    }, (err) => {
      this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Error in Channel Backup for Node ' + node.lnNode, error: err });
      fs.writeFile(channel_backup_file, '', () => { });
    });
  };

  public isVersionCompatible = (currentVersion, checkVersion) => {
    if (currentVersion && currentVersion !== '') {
      // eslint-disable-next-line prefer-named-capture-group
      const pattern = /v?(\d+(\.\d+)*)/;
      const match = currentVersion.match(pattern);
      if (match && match.length && match.length > 1) {
        this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Global Version ' + match[1] });
        this.logger.log({ selectedNode: this.selectedNode, level: 'INFO', fileName: 'Common', msg: 'Checking Compatiblility with Version ' + checkVersion });
        const currentVersionArr = match[1].split('.') || [];
        currentVersionArr[1] = currentVersionArr[1].substring(0, 2);
        const checkVersionsArr = checkVersion.split('.');
        checkVersionsArr[1] = checkVersionsArr[1].substring(0, 2);
        return (+currentVersionArr[0] > +checkVersionsArr[0]) ||
        (+currentVersionArr[0] === +checkVersionsArr[0] && +currentVersionArr[1] > +checkVersionsArr[1]) ||
        (+currentVersionArr[0] === +checkVersionsArr[0] && +currentVersionArr[1] === +checkVersionsArr[1] && +currentVersionArr[2] >= +checkVersionsArr[2]);
      } else {
        this.logger.log({ selectedNode: this.selectedNode, level: 'ERROR', fileName: 'Common', msg: 'Invalid Version String ' + currentVersion });
        return false;
      }
    }
    return false;
  };

  public getMonthDays = (selMonth, selYear) => ((selMonth === 1 && selYear % 4 === 0) ? (this.MONTHS[selMonth].days + 1) : this.MONTHS[selMonth].days);

  public logEnvVariables = (req) => {
    const selNode = <SelectedNode>req.session.selectedNode;
    if (selNode && selNode.index) {
      this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup:', msg: JSON.stringify(this.removeSecureData(JSON.parse(JSON.stringify(this.appConfig)))) });
      this.logger.log({ selectedNode: selNode, level: 'INFO', fileName: 'Config Setup Variable', msg: 'SSO: ' + this.appConfig.SSO.rtlSso });
    }
  };

  public filterData = (dataKey, lnImplementation) => {
    let search_string = '';
    if (lnImplementation === 'ECL') {
      switch (dataKey) {
        case 'GetInfo': search_string = 'INFO: GetInfo => Get Info Response: '; break;
        case 'Fees': search_string = 'INFO: Fees => Fee Response: '; break;
        case 'Payments': search_string = 'INFO: Fees => Payments Response: '; break;
        case 'Invoices': search_string = 'INFO: Invoice => Invoices List Received: '; break;
        case 'OnChainBalance': search_string = 'INFO: Onchain => Balance Received: '; break;
        case 'Peers': search_string = 'INFO: Peers => Peers with Alias: '; break;
        case 'Channels': search_string = 'INFO: Channels => Simplified Channels with Alias: '; break;
        default: search_string = 'Random Line'; break;
      }
    } else if (lnImplementation === 'CLN') {
      switch (dataKey) {
        case 'GetInfo': search_string = 'DEBUG: GetInfo => Node Information. '; break;
        case 'Fees': search_string = 'DEBUG: Fees => Fee Received. '; break;
        case 'Payments': search_string = 'DEBUG: Payments => Payment List Received: '; break;
        case 'Invoices': search_string = 'DEBUG: Invoice => Invoices List Received. '; break;
        case 'ChannelBalance': search_string = 'DEBUG: Channels => Local Remote Balance. '; break;
        case 'Peers': search_string = 'DEBUG: Peers => Peers with Alias: '; break;
        case 'Channels': search_string = 'DEBUG: Channels => List Channels: '; break;
        case 'Balance': search_string = 'DEBUG: Balance => Balance Received. '; break;
        case 'ForwardingHistory': search_string = 'DEBUG: Channels => Forwarding History Received: '; break;
        case 'UTXOs': search_string = 'DEBUG: OnChain => List Funds Received. '; break;
        case 'FeeRateperkb': search_string = 'DEBUG: Network => Network Fee Rates Received for perkb. '; break;
        case 'FeeRateperkw': search_string = 'DEBUG: Network => Network Fee Rates Received for perkw. '; break;
        default: search_string = 'Random Line'; break;
      }
    }
    const foundDataLine = this.dummy_data_array_from_file.find((dataItem) => dataItem.includes(search_string));
    const dataStr = foundDataLine ? foundDataLine.substring((foundDataLine.indexOf(search_string)) + search_string.length) : '{}';
    return JSON.parse(dataStr);
  };

}

export const Common = new CommonService();
