/*
 * Everything related to MySQL database
 * Created by: Michael Pastushkov <michael@pastushkov.com>
 */

'use strict';

const config = require('config');
const util = require('util');
const mysql = require('mysql2');
const utils = require('../utils/utils');

const pool = mysql.createPool(config.mysql);
pool.query = util.promisify(pool.query); // for await

async function db_query_one(sql, values = null) {

    return (await db_query(sql, values)) [0];

}

async function db_query(sql, values = null) {

    utils.log(sql, 5);

    if (!utils.is_empty(values))
        utils.log(values, 6);

    // regular exception trace does not point the code SQL is trigered from
    // we store the stack positon before making request, and log it on errors
    var stack = new Error().stack;
    try {
        return await pool.query(sql, values);
    } catch (e) {
        utils.log_error(e.message);
        utils.log_error(stack);
//        throw [ 500, 'Internal database error'];
        // Temporarily showing the actuaal SQL error
        throw [500, e.message];
    }

}

//// COMMON DATA OPERATIONS ////

// INSERT record in the table
async function table_insert(table, fields, data) {

    let sql = `INSERT INTO ${table} (`;
    let vls = ' VALUES (';

    if (data.id == 'new')
        data.id = null;

    // Fields
    for (let i = 0; i < fields.length; i++) {

        // Do not insert value that has no data (to prevent NULLs from being inserted)
        if (data[fields[i]] === undefined)
            continue;

        sql += fields[i] + ',';
        vls += '?,';
    }

    // Remove trailing commas
    sql = sql.slice(0, -1);
    vls = vls.slice(0, -1);

    sql += ')';
    vls += ')';
    sql += vls;

    // Values 
    let values = [];
    for (let f of fields) {

        if (data[f] === undefined)
            continue;

        values.push(data[f]);
    }

    // Query
    let result = await db_query(sql, values);
    data.id = result.insertId;
    return result;
}

// Update (UPDATE) record in the table
async function table_update(table, fields, data) {

    if (!data.id)
        throw new Error(`Cannot update ${table}: no 'id' in the data`);

    let sql = `UPDATE ${table} SET`;

    // Fields & values
    let keys = Object.keys(data);
    let values = [];
    for (let k of keys) {
        if (!fields.includes(k))
            continue;
        if (k != 'id') {
            sql += ` ${k} = ?,`;
            values.push(data[k]);
        }
    }
    sql = sql.substring(0, sql.length - 1);

    // WHERE clause
    sql += ' WHERE id = ?';
    values.push(data.id);

    // Query
    let result = await db_query(sql, values);
    return result;
}

// Select (SELECT) record(s) from the table
async function table_select(table, options = '', values = []) {
    let sql = `SELECT * FROM ${table} ${options}`;
    let result = await db_query(sql, values);
    return result;
}

// Remove (DELETE) record from the table
async function table_delete(table, options, values) {
    let sql = `DELETE FROM ${table} ${options}`;
    let result = await db_query(sql, values);
    return result;
}

function db_close() {
    pool.end();
    utils.log('Database connection closed');
}

//module.exports = pool;
module.exports.db_query = db_query;
module.exports.db_query_one = db_query_one;
module.exports.db_close = db_close;
module.exports.table_insert = table_insert;
module.exports.table_update = table_update;
module.exports.table_select = table_select;
module.exports.table_delete = table_delete;

/*

// Future enhancements: transaction suppot 
async function db_transaction(callback) {
    const connection = await pool.getConnection();
    try {
        await connection.beginTransaction();
        const result = await callback(connection);
        await connection.commit();
        return result;
    } catch (err) {
        await connection.rollback();
        throw err;
    } finally {
        connection.release();
    }
}

await db_transaction(async (conn) => {
    await db_query("UPDATE ...", conn);
    await db_query("UPDATE ...", conn);
    await db_query("UPDATE ...", conn);
});

async function db_query(sql, values = null, connection = null) {
    utils.log(sql, 5);
    if (!utils.is_empty(values)) 
        utils.log(values, 6);

    const stack = new Error().stack;
    try {
        if (connection) {
            return await connection.query(sql, values);
        } else {
            return await pool.query(sql, values);
        }
    } catch (e) {
        throw [500, e.message];
    }
}

*/