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 { dataSourceOptions, ingestDataSourceOptions, testDataSourceOptions, } from "./ClickhouseConfig";
import { createClient } from "@clickhouse/client";
import DatabaseNotConnectedException from "../../Types/Exception/DatabaseNotConnectedException";
import Sleep from "../../Types/Sleep";
import API from "../../Utils/API";
import URL from "../../Types/API/URL";
import HTTPErrorResponse from "../../Types/API/HTTPErrorResponse";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
import GracefulShutdown, { ShutdownPriority } from "../Utils/GracefulShutdown";
class ClickhouseDatabase {
    constructor(options = dataSourceOptions) {
        this.instanceId = ++ClickhouseDatabase.instanceCounter;
        this.options = options;
    }
    getDatasourceOptions() {
        return this.options;
    }
    getTestDatasourceOptions() {
        return testDataSourceOptions;
    }
    getDataSource() {
        return this.dataSource;
    }
    isConnected() {
        return Boolean(this.dataSource);
    }
    async connect(dataSourceOptions) {
        let retry = 0;
        try {
            const connectToDatabase = async () => {
                var _a;
                try {
                    const defaultDbClient = createClient(Object.assign(Object.assign({}, dataSourceOptions), { database: "default" }));
                    await defaultDbClient.exec({
                        query: `CREATE DATABASE IF NOT EXISTS ${dataSourceOptions.database}`,
                    });
                    await defaultDbClient.close();
                    const clickhouseClient = createClient(dataSourceOptions);
                    this.dataSource = clickhouseClient;
                    const result = await clickhouseClient.ping();
                    if (result.success === false) {
                        throw new DatabaseNotConnectedException("Clickhouse Database is not connected");
                    }
                    logger.debug(`Clickhouse Database Connected: ${(_a = dataSourceOptions.host) === null || _a === void 0 ? void 0 : _a.toString()}`);
                    return clickhouseClient;
                }
                catch (err) {
                    if (retry < 3) {
                        logger.debug("Cannot connect to Clickhouse. Retrying again in 5 seconds");
                        // sleep for 5 seconds.
                        await Sleep.sleep(5000);
                        retry++;
                        return await connectToDatabase();
                    }
                    throw err;
                }
            };
            const client = await connectToDatabase();
            // Close this Clickhouse pool on shutdown.
            GracefulShutdown.registerHandler(`ClickhouseDatabase#${this.instanceId}`, ShutdownPriority.DataStores, () => {
                return this.disconnect();
            });
            return client;
        }
        catch (err) {
            logger.error("Clickhouse Database Connection Failed");
            logger.error(err);
            throw err;
        }
    }
    async disconnect() {
        if (this.dataSource) {
            await this.dataSource.close();
            this.dataSource = null;
        }
    }
    async checkConnnectionStatus() {
        // Ping clickhouse to check if the connection is still alive
        try {
            logger.debug("Checking Clickhouse Connection Status - pinging clickhouse");
            const dbUrl = this.getDatasourceOptions().url;
            if (!dbUrl) {
                throw new DatabaseNotConnectedException("Clickhouse URL not found");
            }
            const result = await API.get({
                url: URL.fromString(dbUrl.toString()),
            });
            logger.debug("Clickhouse Connection Status Result");
            logger.debug(result);
            if (!result) {
                throw new DatabaseNotConnectedException("Clickhouse Database is not connected");
            }
            if (result instanceof HTTPErrorResponse) {
                throw new DatabaseNotConnectedException("Clickhouse Database is not connected");
            }
            if (result.data &&
                result.data["data"] &&
                result.data["data"].toString().trim() ===
                    "Ok.") {
                return true;
            }
            throw new DatabaseNotConnectedException("Clickhouse Database is not connected");
        }
        catch (err) {
            logger.error("Clickhouse Connection Lost");
            logger.error(err);
            return false;
        }
    }
}
/*
 * Each instance owns its own pool (App vs. Ingest), so each needs a
 * distinct shutdown-handler name. The two instances share a database name,
 * so a per-instance counter is what makes the names unique.
 */
ClickhouseDatabase.instanceCounter = 0;
export default ClickhouseDatabase;
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Promise)
], ClickhouseDatabase.prototype, "connect", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], ClickhouseDatabase.prototype, "disconnect", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", []),
    __metadata("design:returntype", Promise)
], ClickhouseDatabase.prototype, "checkConnnectionStatus", null);
export const ClickhouseAppInstance = new ClickhouseDatabase(dataSourceOptions);
/*
 * Separate pool for high-volume telemetry inserts. Reads/DDL keep using
 * ClickhouseAppInstance so dashboard queries are not starved of HTTP
 * sockets when ingest is bursting.
 */
export const ClickhouseIngestInstance = new ClickhouseDatabase(ingestDataSourceOptions);
//# sourceMappingURL=ClickhouseDatabase.js.map