const mysql = require('mysql2');
const config = require('./config.js');
const fs = require('fs-extra');
const PoolConnection = mysql.PoolConnection;

module.exports = {
    init() {
        if (!this.pool) {
            const datasources = config.datasources;
            const pool = mysql.createPoolCluster();

            for (let datasource of datasources)
                pool.add(datasource.name, datasource.options);

            this.pool = pool;
        }

        return this.pool;
    },

    /**
     * Retuns a pool connection from specific cluster node
     * @param {String} name - The cluster name
     *
     * @return {Object} - Pool connection
     */
    getConnection(name = 'default') {
        let pool = this.pool;
        return new Promise((resolve, reject) => {
            pool.getConnection(name, function(error, connection) {
                if (error) return reject(error);

                resolve(connection);
            });
        });
    },

    /**
     * Makes a query from a raw sql
     * @param {String} query - The raw SQL query
     * @param {Object} params - Parameterized values
     * @param {Object} connection - Optional pool connection
     *
     * @return {Object} - Result promise
     */
    rawSql(query, params, connection) {
        let pool = this.pool;
        if (params instanceof PoolConnection)
            connection = params;
        if (connection) pool = connection;

        return new Promise((resolve, reject) => {
            if (!connection) {
                pool.getConnection('default', function(error, conn) {
                    if (error) return reject(error);

                    conn.query(query, params, (error, rows) => {
                        if (error) return reject(error);
                        conn.release();
                        resolve(rows);
                    });
                });
            } else {
                connection.query(query, params, (error, rows) => {
                    if (error) return reject(error);
                    resolve(rows);
                });
            }
        });
    },

    /**
     * Makes a query from a SQL file
     * @param {String} queryName - The SQL file name
     * @param {Object} params - Parameterized values
     * @param {Object} connection - Optional pool connection
     *
     * @return {Object} - Result promise
     */
    rawSqlFromDef(queryName, params, connection) {
        const query = fs.readFileSync(`${queryName}.sql`, 'utf8');

        return this.rawSql(query, params, connection);
    },

    /**
     * Returns the first row from a given raw sql
     * @param {String} query - The raw SQL query
     * @param {Object} params - Parameterized values
     *
     * @return {Object} - Result promise
     */
    findOne(query, params) {
        return this.rawSql(query, params).then(([row]) => row);
    },

    /**
     * Returns the first row from a given SQL file
     * @param {String} queryName - The SQL file name
     * @param {Object} params - Parameterized values
     *
     * @return {Object} - Result promise
     */
    findOneFromDef(queryName, params) {
        return this.rawSqlFromDef(queryName, params)
            .then(([row]) => row);
    },

    /**
     * Returns the first property from a given raw sql
     * @param {String} query - The raw SQL query
     * @param {Object} params - Parameterized values
     *
     * @return {Object} - Result promise
     */
    findValue(query, params) {
        return this.findOne(query, params).then(row => {
            return Object.values(row)[0];
        });
    },

    /**
     * Returns the first property from a given SQL file
     * @param {String} queryName - The SQL file name
     * @param {Object} params - Parameterized values
     *
     * @return {Object} - Result promise
     */
    findValueFromDef(queryName, params) {
        return this.findOneFromDef(queryName, params).then(row => {
            return Object.values(row)[0];
        });
    },

    /**
     * Returns the content of a SQL file
     * @param {String} queryName - The SQL file name
     *
     * @return {Object} - SQL
     */
    getSqlFromDef(queryName) {
        return fs.readFileSync(`${queryName}.sql`, 'utf8');
    },

    /**
     * Merges two SQL strings
     *
     * @param {String} query - Input SQL
     * @param {String} sql - String to merge
     *
     * @return {String} - Merged SQL string
     */
    merge(query, sql) {
        return query += `\r\n ${sql}`;
    },

    /**
     * Builds where string
     *
     * @param {Object} args - Query params
     * @param {Function} callback - Callback
     *
     * @return {String} - Where Sql string
     */
    buildWhere(args, callback) {
        let sql = '';

        for (let prop in args) {
            const value = args[prop];
            if (!value) continue;

            const sanitizedValue = mysql.escape(value);
            const conditional = callback(prop, sanitizedValue);

            if (!conditional) continue;

            if (sql) sql += ' AND ';
            sql += conditional;
        }
        let where = '';
        if (sql)
            where = `WHERE ${sql}`;

        return where;
    }
};