"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.HotDBPostgres = void 0;
const pg = __importStar(require("pg"));
const HotDB_1 = require("../HotDB");
const HotIO_1 = require("../HotIO");
/**
 * The Postgres database connection.
 */
class HotDBPostgres extends HotDB_1.HotDB {
    constructor(db = null, type = HotDB_1.HotDBType.Postgres, schema = null) {
        super(db, type, schema);
    }
    /**
     * Connect to the database.
     */
    connect(connectionInfo) {
        return __awaiter(this, void 0, void 0, function* () {
            return (new Promise((resolve, reject) => __awaiter(this, void 0, void 0, function* () {
                try {
                    if (process.env['DATABASE_CONNECTIONS_LIMIT'] != null)
                        this.connectionLimit = parseInt(process.env['DATABASE_CONNECTIONS_LIMIT']);
                    let poolConfig = {
                        host: connectionInfo.server,
                        user: connectionInfo.username,
                        password: connectionInfo.password,
                        port: connectionInfo.port,
                        database: connectionInfo.database,
                        maxUses: this.connectionLimit
                    };
                    if (connectionInfo.ssl != null) {
                        poolConfig.ssl = {
                            "rejectUnauthorized": true
                        };
                        if (connectionInfo.ssl.rejectUnauthorized != null)
                            poolConfig.ssl.rejectUnauthorized = connectionInfo.ssl.rejectUnauthorized;
                        if (connectionInfo.ssl.ca != null)
                            poolConfig.ssl.ca = yield HotIO_1.HotIO.readTextFile(connectionInfo.ssl.ca);
                        if (connectionInfo.ssl.cert != null)
                            poolConfig.ssl.cert = yield HotIO_1.HotIO.readTextFile(connectionInfo.ssl.cert);
                        if (connectionInfo.ssl.key != null)
                            poolConfig.ssl.key = yield HotIO_1.HotIO.readTextFile(connectionInfo.ssl.key);
                    }
                    if (connectionInfo.connectionObjectOverride != null) {
                        poolConfig = Object.assign(Object.assign({}, poolConfig), connectionInfo.connectionObjectOverride);
                    }
                    this.connectionStatus = HotDB_1.ConnectionStatus.Connecting;
                    this.db = new pg.Pool(poolConfig);
                    this.connectionStatus = HotDB_1.ConnectionStatus.Connected;
                    resolve([true]);
                }
                catch (err) {
                    this.connectionStatus = HotDB_1.ConnectionStatus.Error;
                    reject(err);
                }
            })));
        });
    }
    /**
     * Checks to see if this has a database connection.
     */
    dbCheck() {
        if (this.db == null)
            throw new Error("Not connected to a database!");
    }
    /**
     * Synchronize all tables.
     */
    /*async syncAllTables (migrationsDirectory: string, throwErrors: boolean = true): Promise<boolean>
    {
        this.dbCheck ();

        const migrationsPath: string = ppath.normalize (migrationsDirectory);

        if (await HotIO.exists (migrationsPath) === false)
            throw new Error (`Migrations directory ${migrationsDirectory} does not exist.`);

        let files: string[] = await HotIO.listFiles (migrationsPath);
        let versions: {
                version: number;
                fileContent: string;
            }[] = [];

        for (let iIdx = 0; iIdx < files.length; iIdx++)
        {
            let file: string = ppath.normalize (files[iIdx]);
            let fileContent: string = await HotIO.readTextFile (file);

            let context = { HotDBMigration: HotDBMigration, db: this, migrationPath: file };
            vm.createContext (context);
            let migrationVersion: any = vm.runInContext (
`${fileContent}
let migration = new Migration ();
return (migration.version);
`, context);

            versions.push ({ version: migrationVersion, fileContent: fileContent });
        }

        versions.sort ();

        await this.syncMigrationsTableTracker ();

        let madeModifications: boolean = false;

        for (let tableName in this.schema.tables)
        {
            let tempResult: boolean = await this.syncTable (tableName, throwErrors);

            if (tempResult === true)
                madeModifications = true;
        }

        return (madeModifications);
    }*/
    /**
     * Synchronize a table. This will create/modify the table based on whether it
     * exists, and if there's been any changes to any fields.
     */
    /*async syncTable (tableName: string, throwErrors: boolean = true): Promise<boolean>
    {
        this.dbCheck ();

        let tableExists: boolean =  await this.tableCheck (tableName);
        let madeModifications: boolean = false;

        if (tableExists === false)
        {
            let structure: string[] = await this.schema.generateTableStructure (tableName);
            // This should always be structure[0]. There should only be 1 string to process
            // for a newly created table.
            let tempResults = await this.query (structure[0], []);

            if (tempResults.error == null)
                madeModifications = true;
            else
            {
                if (throwErrors === true)
                    throw new Error (`Error while creating table ${tableName}: ${tempResults.error.message}`);
            }
        }
        else
        {*/
    /*let structure: string[] = await this.schema.generateTableStructure (
                                tableName, HotDBGenerationType.Modify, this);
    let tempResults = await this.multiQuery (structure);

    for (let iIdx = 0; iIdx < tempResults.length; iIdx++)
    {
        let results = tempResults[iIdx];

        if (results.error != null)
        {
            madeModifications = false;

            if (throwErrors === true)
                throw new Error (`Error while creating table ${tableName}: ${results.error.message}`);
        }
        else
            madeModifications = true;
    }*/
    /*}

    return (madeModifications);
}*/
    /**
     * Sync the migrations table tracker. This keeps track of all database table migrations.
     * If the "migrations" table is missing, it will be created.
     */
    /*async syncMigrationsTableTracker (): Promise<void>
    {
        if (await this.tableCheck ("migrations") === false)
        {
            await new Promise<void> ((resolve, reject) =>
                {
                    this.db.getConnection (async (err: NodeJS.ErrnoException, connection: mysql.PoolConnection) =>
                        {
                            if (err)
                            {
                                reject (err);

                                return;
                            }
    
                            /// @todo Verify that this actually created.
                            await this.db.query (`create table if not exists migrations (
                                    version datetime not null
                                );`).on ("end", () =>
                                    {
                                        connection.release ();
                                    });

                            resolve ();
                        });
                });
        }
    }*/
    /**
     * Checks if the table exists.
     */
    tableCheck(tableName) {
        return __awaiter(this, void 0, void 0, function* () {
            this.dbCheck();
            try {
                const query = `
			SELECT table_name 
			FROM information_schema.tables
			WHERE table_name = $1;
			`;
                const result = yield this.db.query(query, [tableName]);
                return (result.rows.length > 0);
            }
            catch (err) {
                throw err;
            }
        });
    }
    /**
     * The query to make.
     */
    query(queryString_1) {
        return __awaiter(this, arguments, void 0, function* (queryString, values = []) {
            this.dbCheck();
            try {
                const result = yield this.db.query(queryString, values);
                return ({
                    error: null,
                    results: result.rows,
                    rawResults: result,
                    fields: result.fields
                });
            }
            catch (err) {
                return ({
                    error: err,
                    results: null,
                    rawResults: null,
                    fields: []
                });
            }
        });
    }
    /**
     * Make a single query. If there are no results, null will be in MySQLResults.results
     */
    queryOne(queryString_1) {
        return __awaiter(this, arguments, void 0, function* (queryString, values = []) {
            this.dbCheck();
            try {
                const result = yield this.db.query(queryString, values);
                let tempResults = null;
                if (result != null) {
                    if (result.rows != null) {
                        if (result.rows.length > 0)
                            tempResults = result.rows[0];
                    }
                }
                return ({
                    error: null,
                    results: tempResults,
                    rawResults: result,
                    fields: result.fields
                });
            }
            catch (err) {
                return ({
                    error: err,
                    results: null,
                    rawResults: null,
                    fields: []
                });
            }
        });
    }
    /**
     * Make multiple queries.
     *
     * This is not implemented, and will not be.
     *
     * @deprecated
     */
    multiQuery(queryStrings) {
        return __awaiter(this, void 0, void 0, function* () {
            throw new Error("Not implemented");
        });
    }
    /**
     * Disconnect from the server.
     */
    disconnect() {
        return __awaiter(this, void 0, void 0, function* () {
            this.dbCheck();
            return (this.db.end());
        });
    }
}
exports.HotDBPostgres = HotDBPostgres;
//# sourceMappingURL=HotDBPostgres.js.map