const mysql = require('mysql2');
const fs = require('fs-extra');

module.exports = {
    defaultDataSource: 'vn',

    init(dataSource) {
        if (!this.connections) {
            this.connections = [];

            const dataSources = ['vn', 'osticket'];
            for (const name of dataSources)
                this.connections[name] = dataSource[name].connector.client;

            this.pool = this.connections[this.defaultDataSource];
        }

        return this.connections;
    },

    /**
     * Retuns a pool connection from specific cluster node
     * @param {String} name - The cluster name
     *
     * @return {Object} - Pool connection
     */
    getConnection(name = this.defaultDataSource) {
        return this.connections[name];
    },

    /**
     * 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) {
        return new Promise((resolve, reject) => {
            let db = this.getConnection();
            if (connection) db = connection;

            db.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
     * @param {Object} connection - Optional pool connection
     *
     * @return {Object} - Result promise
     */
    findOne(query, params, connection) {
        return this.rawSql(query, params, connection)
            .then(([row]) => row);
    },

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

    /**
     * Returns the first property from a given raw sql
     * @param {String} query - The raw SQL query
     * @param {Object} params - Parameterized values
     * @param {Object} connection - Optional pool connection
     *
     * @return {Object} - Result promise
     */
    findValue(query, params, connection) {
        return this.findOne(query, params, connection).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
     * @param {Object} connection - Optional pool connection
     *
     * @return {Object} - Result promise
     */
    findValueFromDef(queryName, params, connection) {
        return this.findOneFromDef(queryName, params, connection)
            .then(row => 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;
    }
};