/*
 * Data Access Layer
 * Created by: Michael Pastushkov <max@pastushkov.com>
 */

const db = require('./database');
const utils = require('../utils/utils');

let objects = {};

async function init() {

    let classes = {};
    const tables = await db.db_query("SHOW TABLES");
    for (let t of tables) {
        const table = Object.values(t)[0];
        const columns =  await db.db_query(`SHOW COLUMNS FROM \`${table}\``); 
        const className = table.charAt(0).toUpperCase() + table.slice(1);
        classes[className] = class extends DataObject {
        constructor() {
            super(table, columns);
        }
    }
    objects[className] = new classes[className]();
  }

  // This where we replace exports with objects
  //module.exports = objects;
  Object.assign(module.exports, objects);
}

class DataObject {

    constructor(table, columns) {

        this.table = table;
        this.columns = columns;
        this.fields = [];
        for (let c of columns)
            this.fields.push(c.Field);

        this.post = this.post.bind(this);
        this.patch = this.patch.bind(this);
        this.get = this.get.bind(this);
        this.del = this.del.bind(this);
    }

    json_stringify(d) {
        for (let c of this.columns.filter(col => col.Type === 'json')) {
            if (d[c.Field])
                d[c.Field] = JSON.stringify(d[c.Field]);
            else
                d[c.Field] = {}; // MySQL doesn't allow default values for JSON fields
        }
        return d;
    }

    strings2dates(d) {
        for (let c of this.columns.filter(col => col.Type === 'date' || col.Type === 'datetime')) {
            if (d[c.Field])
                d[c.Field] = new Date(d[c.Field]);
        }
    }

    // Direct access functions

    async create(data) {
        data.created = new Date();
        return await db.table_insert(this.table, this.fields, this.json_stringify(data));
    }

    async update(data) {
        data.updated = new Date();
        return await db.table_update(this.table, this.fields, this.json_stringify(data));
    }

    async select_one(options, values) {
        return (await db.table_select(this.table, options, values)) [0];
    }

    async select(options, values) {
        return await db.table_select(this.table, options, values);
    }

    async remove(options, values) {
        return await db.table_delete(this.table, options, values);
    }

    // REST access functions

    async post(req, res) {
        this.strings2dates(req.body);
        res.send(await this.create(req.body));
    }

    async patch(req, res) {
        this.strings2dates(req.body);
        res.send(await this.update(req.body));
    }

    async get(req, res) {
        let id = req.params.id || 'ORDER BY id DESC';
        if (id.startsWith('WHERE') || id.startsWith('ORDER BY'))
            res.send(await this.select(id));
        else
            res.send(await this.select_one("WHERE id = ?", id));
    }

    async del(req, res) {
        res.send(await this.remove("WHERE id = ?", req.params.id));
    }

}

// First, we export only init, then it would be 
// replaced with objects during init execution
module.exports.init = init;