const fs      = require('fs');
const NodeRSA = require('node-rsa');
const { config } = require('process');
const logger  = require('../logger').global;

const keysFile = '/data/keys.json';

let instance = null;

// 1. Load from config file first (not recommended anymore)
// 2. Use config env variables next
const configure = () => {
	const filename = (process.env.NODE_CONFIG_DIR || './config') + '/' + (process.env.NODE_ENV || 'default') + '.json';
	if (fs.existsSync(filename)) {
		let configData;
		try {
			configData = require(filename);
		} catch (err) {
			// do nothing
		}
		if (configData?.database && configData?.database?.engine) {
			logger.info(`Using configuration from file: ${filename}`);
			instance = configData;
			return;
		}
	}

	const envMysqlHost = process.env.DB_MYSQL_HOST || null;
	const envMysqlUser = process.env.DB_MYSQL_USER || null;
	const envMysqlName = process.env.DB_MYSQL_NAME || null;
	if (envMysqlHost && envMysqlUser && envMysqlName) {
		// we have enough mysql creds to go with mysql
		logger.info('Using MySQL configuration');
		instance = {
			database: {
				engine:   'mysql',
				host:     envMysqlHost,
				port:     process.env.DB_MYSQL_PORT || 3306,
				user:     envMysqlUser,
				password: process.env.DB_MYSQL_PASSWORD,
				name:     envMysqlName,
			}
		};
		return;
	}

	const envSqliteFile = process.env.DB_SQLITE_FILE || '/data/database.sqlite';
	logger.info(`Using Sqlite: ${envSqliteFile}`);
	instance = {
		database: {
			engine:  'knex-native',
			knex:    {
				client:     'sqlite3',
				connection: {
					filename: envSqliteFile
				},
				useNullAsDefault: true
			}
		}
	};

	// Get keys from file
	if (!fs.existsSync(keysFile)) {
		generateKeys();
	} else if (!!process.env.DEBUG) {
		logger.info('Keys file exists OK');
	}
	try {
		instance.keys = require(keysFile);
	} catch (err) {
		logger.error('Could not read JWT key pair from config file: ' + keysFile, err);
		process.exit(1);
	}

	logger.debug('Configuration: ' + JSON.stringify(instance, null, 2));
};

const generateKeys = () => {
	logger.info('Creating a new JWT key pair...');
	// Now create the keys and save them in the config.
	const key = new NodeRSA({ b: 2048 });
	key.generateKeyPair();

	const keys = {
		key: key.exportKey('private').toString(),
		pub: key.exportKey('public').toString(),
	};

	// Write keys config
	try {
		fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
	} catch (err) {
		logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' . err.message);
		process.exit(1);
	}
	logger.info('Wrote JWT key pair to config file: ' + keysFile);
};

module.exports = {

	/**
	 *
	 * @param   {string}  key   ie: 'database' or 'database.engine'
	 * @returns {boolean}
	 */
	has: function(key) {
		instance === null && configure();
		const keys = key.split('.');
		let level = instance;
		let has = true;
		keys.forEach((keyItem) =>{
			if (typeof level[keyItem] === 'undefined') {
				has = false;
			} else {
				level = level[keyItem];
			}
		});

		return has;
	},

	/**
	 * Gets a specific key from the top level
	 *
	 * @param {string} key
	 * @returns {*}
	 */
	get: function (key) {
		instance === null && configure();
		if (key && typeof instance[key] !== 'undefined') {
			return instance[key];
		}
		return instance;
	},

	/**
	 * Is this a sqlite configuration?
	 *
	 * @returns {boolean}
	 */
	isSqlite: function () {
		instance === null && configure();
		return instance.database?.knex && instance.database?.knex?.client === 'sqlite3';
	},

	/**
	 * Are we running in debug mdoe?
	 *
	 * @returns {boolean}
	 */
	debug: function () {
		return !!process.env.DEBUG;
	},

	/**
	 * Returns a public key
	 *
	 * @returns {string}
	 */
	getPublicKey: function () {
		instance === null && configure();
		return instance?.keys?.pub
	},

	/**
	 * Returns a private key
	 *
	 * @returns {string}
	 */
	getPrivateKey: function () {
		instance === null && configure();
		return instance?.keys?.key;
	},

	/**
	 * @returns {boolean}
	 */
	useLetsencryptStaging: function () {
		return !!process.env.LE_STAGING;
	}
};