var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
    var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
    if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
    else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
    return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
    if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
import logger from "../Utils/Logger";
import DatabaseDataSourceOptions from "./Postgres/DataSourceOptions";
import Sleep from "../../Types/Sleep";
import { DataSource } from "typeorm";
import { createDatabase, dropDatabase } from "typeorm-extension";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import GracefulShutdown, { ShutdownPriority } from "../Utils/GracefulShutdown";
class Database {
    static getDatasourceOptions() {
        this.dataSourceOptions = DatabaseDataSourceOptions;
        return this.dataSourceOptions;
    }
    static getDataSource() {
        return this.dataSource;
    }
    static isConnected() {
        return Boolean(this.dataSource);
    }
    static async connect() {
        /*
         * Idempotent: a second connect() must not overwrite (and thereby orphan)
         * the existing pool. Return the live DataSource instead of building a new
         * one.
         */
        if (this.dataSource) {
            return this.dataSource;
        }
        let retry = 0;
        const dataSourceOptions = this.getDatasourceOptions();
        try {
            const connectToDatabase = async () => {
                try {
                    const PostgresDataSource = new DataSource(dataSourceOptions);
                    const dataSource = await PostgresDataSource.initialize();
                    logger.debug("Postgres Database Connected");
                    this.dataSource = dataSource;
                    return dataSource;
                }
                catch (err) {
                    if (retry < 3) {
                        logger.debug("Cannot connect to Postgres. Retrying again in 5 seconds");
                        // sleep for 5 seconds.
                        await Sleep.sleep(5000);
                        retry++;
                        return await connectToDatabase();
                    }
                    throw err;
                }
            };
            const dataSource = await connectToDatabase();
            /*
             * Drain the pool on shutdown. Registered here (after a successful
             * connect) so we never register cleanup for a pool that was never
             * created, and — thanks to GracefulShutdown deduping by name — exactly
             * once even if connect() is somehow reached twice.
             */
            GracefulShutdown.registerHandler("PostgresDatabase", ShutdownPriority.DataStores, () => {
                return this.disconnect();
            });
            return dataSource;
        }
        catch (err) {
            logger.error("Postgres Database Connection Failed");
            logger.error(err);
            throw err;
        }
    }
    static async disconnect() {
        if (this.dataSource) {
            await this.dataSource.destroy();
            this.dataSource = null;
        }
    }
    static async checkConnnectionStatus() {
        var _a;
        // SELECT 1 round-trips a connection without scanning any user table.
        try {
            const result = await ((_a = this.dataSource) === null || _a === void 0 ? void 0 : _a.query(`SELECT 1`));
            if (!result) {
                return false;
            }
            return true;
        }
        catch (err) {
            logger.error("Postgres Connection Lost");
            logger.error(err);
            return false;
        }
    }
    static async dropDatabase() {
        await dropDatabase({
            options: this.getDatasourceOptions(),
        });
        this.dataSource = null;
        this.dataSourceOptions = null;
    }
    static async createDatabase() {
        await createDatabase({
            options: this.getDatasourceOptions(),
            ifNotExist: true,
        });
    }
    static async createAndConnect() {
        await this.createDatabase();
        await this.connect();
    }
    static async disconnectAndDropDatabase() {
        // Drop the database. Since this is the in-mem db, it will be destroyed.
        await this.disconnect();
        await this.dropDatabase();
    }
}
Database.dataSourceOptions = null;
Database.dataSource = null;
export default Database;
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Object)
], Database, "getDatasourceOptions", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Object)
], Database, "getDataSource", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Boolean)
], Database, "isConnected", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Database, "connect", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Database, "disconnect", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Database, "checkConnnectionStatus", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Database, "dropDatabase", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Database, "createDatabase", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Database, "createAndConnect", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], Database, "disconnectAndDropDatabase", null);
//# sourceMappingURL=PostgresDatabase.js.map