"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.db = exports.init = void 0;
const util_1 = __importDefault(require("util"));
const graceful_fs_1 = __importDefault(require("graceful-fs"));
const glob_1 = __importDefault(require("glob"));
const path_1 = __importDefault(require("path"));
const config_1 = require("../config");
const logger_1 = __importDefault(require("../logger"));
const sqlite_1 = __importDefault(require("sqlite"));
const pglob = util_1.default.promisify(glob_1.default);
const pReadFile = util_1.default.promisify(graceful_fs_1.default.readFile);
async function applyPatch(db, file) {
    logger_1.default.tag('database', 'migration').info('Checking if patch need to be applied: %s', file);
    const contents = await pReadFile(file);
    const version = path_1.default.basename(file, '.sql');
    const row = await db.get('SELECT * FROM schema_version WHERE version = ?', version);
    if (row) {
        // patch is already applied. skip!
        logger_1.default.tag('database', 'migration').info('Patch already applied, skipping: %s', file);
        return;
    }
    const sql = 'BEGIN TRANSACTION;\n' +
        contents.toString() + '\n' +
        'INSERT INTO schema_version (version) VALUES (\'' + version + '\');\n' +
        'END TRANSACTION;';
    await db.exec(sql);
    logger_1.default.tag('database', 'migration').info('Patch successfully applied: %s', file);
}
async function applyMigrations(db) {
    logger_1.default.tag('database', 'migration').info('Migrating database...');
    const sql = 'BEGIN TRANSACTION; CREATE TABLE IF NOT EXISTS schema_version (\n' +
        '    version VARCHAR(255) PRIMARY KEY ASC,\n' +
        '    applied_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL\n' +
        '); END TRANSACTION;';
    await db.exec(sql);
    const files = await pglob(__dirname + '/patches/*.sql');
    for (const file of files) {
        await applyPatch(db, file);
    }
}
const dbPromise = new Promise((resolve, reject) => {
    sqlite_1.default.open(config_1.config.server.databaseFile)
        .then(resolve)
        .catch(reject);
});
async function init() {
    logger_1.default.tag('database').info('Setting up database: %s', config_1.config.server.databaseFile);
    let db;
    try {
        db = await dbPromise;
    }
    catch (error) {
        logger_1.default.tag('database').error('Error initialzing database:', error);
        throw error;
    }
    db.on('profile', (sql, time) => logger_1.default.tag('database').profile('[%sms]\t%s', time, sql));
    try {
        await applyMigrations(db);
    }
    catch (error) {
        logger_1.default.tag('database').error('Error migrating database:', error);
        throw error;
    }
}
exports.init = init;
/**
 * Wrapper around a Promise<Database> providing the same interface as the Database itself.
 */
class DatabasePromiseWrapper {
    constructor(db) {
        this.db = db;
        db.catch(err => {
            logger_1.default.tag('database', 'init').error('Error initializing database: ', err);
            process.exit(1);
        });
    }
    async close() {
        const db = await this.db;
        // @ts-ignore
        return await db.close.apply(db, arguments);
    }
    async run() {
        const db = await this.db;
        // @ts-ignore
        return await db.run.apply(db, arguments);
    }
    async get() {
        const db = await this.db;
        // @ts-ignore
        return await db.get.apply(db, arguments);
    }
    async all() {
        const db = await this.db;
        // @ts-ignore
        return await db.all.apply(db, arguments);
    }
    async exec() {
        const db = await this.db;
        // @ts-ignore
        return await db.exec.apply(db, arguments);
    }
    async each() {
        const db = await this.db;
        // @ts-ignore
        return await db.each.apply(db, arguments);
    }
    async prepare() {
        const db = await this.db;
        // @ts-ignore
        return await db.prepare.apply(db, arguments);
    }
    async configure() {
        const db = await this.db;
        // @ts-ignore
        return await db.configure.apply(db, arguments);
    }
    async migrate() {
        const db = await this.db;
        // @ts-ignore
        return await db.migrate.apply(db, arguments);
    }
    async on() {
        const db = await this.db;
        // @ts-ignore
        return await db.on.apply(db, arguments);
    }
}
exports.db = new DatabasePromiseWrapper(dbPromise);
//# sourceMappingURL=database.js.map