/*=========================================================
	MODULE zNodeMariaDBClient
	----------------------------------------------------
	Classe Pool Connector
		Création d'un pool MariaDB et gestion des connexion
		et requêtes
	V1.0 :
=========================================================*/
import mariadb from 'mariadb';
import { toSqlFormat } from './zNodeLibrary.mjs';

export class PoolMariaDB {
	
	constructor(dbConfig) {
		this.pool = mariadb.createPool({
			host: dbConfig.host,
			port: dbConfig.port || 3306,
			user: dbConfig.user, 
			password: dbConfig.password,
			database: dbConfig.database,
			connectionLimit: dbConfig.cnxMax,
			decimalAsNumber: true
		});
		this.database = dbConfig.database;
	}

	async executeQuery(query, params=[]) {
		//console.log("> [PoolMariaDB]executeQuery:",query);
		//if(params) console.log("   with ",params);
		
		return new Promise(async (resolve, reject) => {
			// Connexion au pool		
			const cnx = await this.pool.getConnection();
			// Exécution de la requête
			cnx.query(query, params)
			.then(result => { 
				//console.log(result);
				resolve(result) })
			.catch(err => { reject(err) })
			.finally(cnx.release());
		})
	}

	async insertData(table, data) {
		//console.log("> [PoolMariaDB]insertData:");
		//console.log("> ... table :", table);
		//console.log("> ... data  :", data);

		return new Promise(async (resolve, reject) => {
			// Connexion au pool		
			const cnx = await this.pool.getConnection();
			// Création des tables champs et valeurs
			const champs = Object.keys(data).join(', ');
			const valeurs = Object.values(data).map(val => cnx.escape(val)).join(', ');
			//console.log('champs:', champs);
			
			// Construction de la requête
			const query = `INSERT INTO ${table} (${champs}) VALUES (${valeurs})`;
			// Exécution de la requête
			cnx.query(query)
			.then(result => { 
				//console.log(result);
				resolve(result) })
			.catch(err => { reject(err) })
			.finally(cnx.release());
		})
	}

	async updateData(table, data, filter) {
		//console.log("> [PoolMariaDB]updateData:");
		//console.log("> ... table :", table);
		//console.log("> ... data  :", data);
		//console.log("> ... filter:", filter);

		return new Promise(async (resolve, reject) => {
			// Connexion au pool		
			const cnx = await this.pool.getConnection();
			// Création de l'ensemble des tuples à modifier
			const setValues = Object.entries(data).map(
				([key, value]) => { 
					if (value === null) {
						return `${key}=NULL`;						// SQL NULL
					} else if (value === true) {
						return `${key}=TRUE`;						// SQL TRUE
					} else if (value === false) {
						return `${key}=FALSE`;						// SQL FALSE
					} else if (typeof value === "number") {
						return `${key}=${value}`;					// nombre sans quotes
					} else {
						return `${key}='${value}'`;				// par défaut chaîne de caractères
	 				}
				}
			).join(", ");
			// Création de la requête
			const query = `UPDATE ${table} SET ${setValues} WHERE ${filter}`
			//console.log("... query:", query);
			// Exécution de la requête
			cnx.query(query)
			.then(result => { 
				//console.log(result);
				resolve(result) })
			.catch(err => { reject(err) })
			.finally(cnx.release());
		})
	}

	async dbQuery(queryObj) {
		//console.log("> [PoolMariaDB]dbQuery:",queryObj);

		return new Promise(async (resolve, reject) => {
			var query = null;
			if(queryObj.type) {
				switch(queryObj.type.toLowerCase()) {
					case "query":
						if(!queryObj.query) reject({CR:'fail', msg:'! Query missing'});
						query = queryObj.query;
						break;

					case "insert":
						if(!queryObj.table) reject({CR:'fail', msg:'! Table missing'});
						if(!queryObj.dataset) reject({CR:'fail', msg:'! Dataset missing'});

						// Récupère les noms de champs depuis le premier objet
						const fields = Object.keys(queryObj.dataset[0]);
						const fieldList = fields.join(', ');

						// Génère les valeurs à insérer
						const valuesList = queryObj.dataset.map(row => {
							const vals = fields.map(f => "'"+toSqlFormat(row[f])+"'");
							return `(${vals.join(', ')})`;
						}).join(',\n');

						// Construction de la requête
						query = `INSERT INTO ${queryObj.table} (${fieldList}) VALUES\n${valuesList};`;
						break;

					case "update":
						if(!queryObj.table) reject({CR:'fail', msg:'! Table missing'});
						if(!queryObj.dataset) reject({CR:'fail', msg:'! Dataset missing'});
						// Création de l'ensemble des tuples à modifier
						const setValues = Object.entries(queryObj.dataset).map(
							([key, value]) => { 
								if (value === null) {
									return `${key}=NULL`;						// SQL NULL
								} else if (value === true) {
									return `${key}=TRUE`;						// SQL TRUE
								} else if (value === false) {
									return `${key}=FALSE`;						// SQL FALSE
								} else if (typeof value === "number") {
									return `${key}=${value}`;					// nombre sans quotes
								} else {
									return `${key}='${value}'`;				// par défaut chaîne de caractères
								}
							}
						).join(", ");
						// Création de la requête
						query = `UPDATE ${queryObj.table} SET ${setValues} WHERE ${queryObj.filter}`
						break;

				}
			} else {
				if(queryObj.query) query = queryObj.query;
			}
			//console.log("...query:",query);
			if(!query) reject({CR:'fail', msg:'Impossible de créer une requête'});

			// Connexion au pool		
			const cnx = await this.pool.getConnection();
			// Exécution de la requête
			cnx.query(query)
			.then(result => { resolve(result); })
			.catch(err => { reject(err); })
			.finally(cnx.release());

		});
	}


	async getTables() {
		return new Promise(async (resolve, reject) => {
			const cnx = await this.pool.getConnection();
			// Exécution de la requête
			const query = `SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${this.database}'`;
			// Connexion au pool		
			cnx.query(query)
			.then(result => { 
				const tableList = result.map(obj => obj.TABLE_NAME);
				resolve(tableList); })
			.catch(err => { reject(err); })
			.finally(cnx.release());
		})
	}

	async getStream(query) {
		return new Promise(async (resolve, reject) => {
			const cnx = await this.pool.getConnection();
			try {
				// Exécution de la requête
				const stream = cnx.queryStream(query);
				resolve(stream);
			} catch (err) {
				reject(err);
			}
		})
	}

	async close() {
		await this.pool.end();
		this.pool = undefined;
	}
}

