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 BadRequestException from "../../Types/Exception/BadRequestException";
import InMemoryTTLCache from "../Infrastructure/InMemoryTTLCache";
import LocalCache from "../Infrastructure/LocalCache";
import Express from "../Utils/Express";
import logger, { getLogAttributesFromRequest } from "../Utils/Logger";
import Response from "../Utils/Response";
import Telemetry from "../Utils/Telemetry";
import Exception from "../../Types/Exception/Exception";
import ServerException from "../../Types/Exception/ServerException";
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
class StatusAPI {
    /**
     * Runs `checkFn` with two layers of protection:
     *   1. TTL cache — if the same check ran in the last HEALTH_CHECK_CACHE_TTL_MS
     *      ms, reuse its result (success or failure) without re-running.
     *   2. Single-flight — if a check is already in flight, concurrent callers
     *      await the same promise instead of starting their own.
     *
     * On cache hit this is effectively free; on cache miss we run the check
     * exactly once regardless of how many requests arrived concurrently.
     */
    static async runCachedCheck(checkName, checkFn) {
        const cached = this.checkResultCache.get(checkName);
        if (cached) {
            if (cached.ok) {
                return;
            }
            throw cached.error;
        }
        let inflight = this.inflightChecks.get(checkName);
        if (!inflight) {
            inflight = (async () => {
                try {
                    await checkFn();
                    this.checkResultCache.set(checkName, { ok: true }, this.HEALTH_CHECK_CACHE_TTL_MS);
                }
                catch (e) {
                    const error = e instanceof Error ? e : new Error(String(e));
                    this.checkResultCache.set(checkName, { ok: false, error }, this.HEALTH_CHECK_CACHE_TTL_MS);
                    throw error;
                }
                finally {
                    this.inflightChecks.delete(checkName);
                }
            })();
            this.inflightChecks.set(checkName, inflight);
        }
        await inflight;
    }
    static init(options) {
        const statusCheckSuccessCounter = Telemetry.getCounter({
            name: "status.check.success",
            description: "Status check counter",
        });
        // ready counter
        const stausReadySuccess = Telemetry.getCounter({
            name: "status.ready.success",
            description: "Ready check counter",
        });
        // live counter
        const stausLiveSuccess = Telemetry.getCounter({
            name: "status.live.success",
            description: "Live check counter",
        });
        // ready failed counter
        const stausReadyFailed = Telemetry.getCounter({
            name: "status.ready.failed",
            description: "Ready check counter",
        });
        // live failed counter
        const stausLiveFailed = Telemetry.getCounter({
            name: "status.live.failed",
            description: "Live check counter",
        });
        const router = Express.getRouter();
        router.get("/app-name", (_req, res) => {
            res.send({ app: LocalCache.getString("app", "name") });
        });
        // General status
        router.get("/status", (req, res) => {
            statusCheckSuccessCounter.add(1);
            logger.info("Status check: ok", getLogAttributesFromRequest(req));
            Response.sendJsonObjectResponse(req, res, {
                status: "ok",
            });
        });
        //Healthy probe
        router.get("/status/ready", (req, res) => {
            return this.handleReadyCheck(options, stausReadySuccess, stausReadyFailed, req, res);
        });
        //Liveness probe
        router.get("/status/live", (req, res) => {
            return this.handleLiveCheck(options, stausLiveSuccess, stausLiveFailed, req, res);
        });
        // Global cache check
        router.get("/status/global-cache", (req, res) => {
            return this.handleGlobalCacheCheck(options, req, res);
        });
        // Analytics database check
        router.get("/status/analytics-database", (req, res) => {
            return this.handleAnalyticsDatabaseCheck(options, req, res);
        });
        // Database check
        router.get("/status/database", (req, res) => {
            return this.handleDatabaseCheck(options, req, res);
        });
        return router;
    }
    static async handleReadyCheck(options, stausReadySuccess, stausReadyFailed, req, res) {
        try {
            /*
             * Cached for HEALTH_CHECK_CACHE_TTL_MS so k8s probe traffic does not
             * hammer the underlying check on every request.
             */
            await this.runCachedCheck("ready", options.readyCheck);
            logger.info("Ready check: ok", getLogAttributesFromRequest(req));
            stausReadySuccess.add(1);
            Response.sendJsonObjectResponse(req, res, {
                status: "ok",
            });
        }
        catch (e) {
            logger.error("Ready check: failed", getLogAttributesFromRequest(req));
            logger.error(e, getLogAttributesFromRequest(req));
            stausReadyFailed.add(1);
            Response.sendErrorResponse(req, res, e instanceof Exception ? e : new ServerException("Server is not ready"));
        }
    }
    static async handleLiveCheck(options, stausLiveSuccess, stausLiveFailed, req, res) {
        try {
            // Cached for HEALTH_CHECK_CACHE_TTL_MS — see runCachedCheck for rationale.
            await this.runCachedCheck("live", options.liveCheck);
            logger.info("Live check: ok", getLogAttributesFromRequest(req));
            stausLiveSuccess.add(1);
            Response.sendJsonObjectResponse(req, res, {
                status: "ok",
            });
        }
        catch (e) {
            logger.error("Live check: failed", getLogAttributesFromRequest(req));
            logger.error(e, getLogAttributesFromRequest(req));
            stausLiveFailed.add(1);
            Response.sendErrorResponse(req, res, e instanceof Exception ? e : new ServerException("Server is not ready"));
        }
    }
    static async handleGlobalCacheCheck(options, req, res) {
        try {
            logger.debug("Global cache check", getLogAttributesFromRequest(req));
            if (options.globalCacheCheck) {
                // Cached — see runCachedCheck for rationale.
                await this.runCachedCheck("global-cache", options.globalCacheCheck);
            }
            else {
                throw new BadRequestException("Global cache check not implemented");
            }
            logger.info("Global cache check: ok", getLogAttributesFromRequest(req));
            Response.sendJsonObjectResponse(req, res, {
                status: "ok",
            });
        }
        catch (e) {
            Response.sendErrorResponse(req, res, e instanceof Exception
                ? e
                : new ServerException("Global cache is not ready"));
        }
    }
    static async handleAnalyticsDatabaseCheck(options, req, res) {
        try {
            logger.debug("Analytics database check", getLogAttributesFromRequest(req));
            if (options.analyticsDatabaseCheck) {
                // Cached — see runCachedCheck for rationale.
                await this.runCachedCheck("analytics-database", options.analyticsDatabaseCheck);
            }
            else {
                throw new BadRequestException("Analytics database check not implemented");
            }
            logger.info("Analytics database check: ok", getLogAttributesFromRequest(req));
            Response.sendJsonObjectResponse(req, res, {
                status: "ok",
            });
        }
        catch (e) {
            Response.sendErrorResponse(req, res, e instanceof Exception
                ? e
                : new ServerException("Analytics database is not ready"));
        }
    }
    static async handleDatabaseCheck(options, req, res) {
        try {
            logger.debug("Database check", getLogAttributesFromRequest(req));
            if (options.databaseCheck) {
                // Cached — see runCachedCheck for rationale.
                await this.runCachedCheck("database", options.databaseCheck);
            }
            else {
                throw new BadRequestException("Database check not implemented");
            }
            logger.info("Database check: ok", getLogAttributesFromRequest(req));
            Response.sendJsonObjectResponse(req, res, {
                status: "ok",
            });
        }
        catch (e) {
            Response.sendErrorResponse(req, res, e instanceof Exception
                ? e
                : new ServerException("Database is not ready"));
        }
    }
}
/**
 * Cache of recent health check results, keyed by check name. Each entry
 * lives for HEALTH_CHECK_CACHE_TTL_MS. Bounded to a small max size — there
 * are only ~5 distinct check names in this API.
 */
StatusAPI.checkResultCache = new InMemoryTTLCache(64);
/**
 * In-flight check promises keyed by check name. When a cache miss occurs
 * and multiple concurrent requests arrive, they all attach to the same
 * promise instead of each triggering their own DB query. The entry is
 * cleared as soon as the check settles.
 */
StatusAPI.inflightChecks = new Map();
/**
 * Cache TTL for health-check results. Chosen so that:
 *   - Two-thirds of typical k8s probes (default periodSeconds=10) hit
 *     the cache, removing constant DB load from liveness/readiness traffic.
 *   - Time-to-detect for a failing dependency only grows by ≤5s, which is
 *     well within the failureThreshold window k8s probes already tolerate.
 */
StatusAPI.HEALTH_CHECK_CACHE_TTL_MS = 5000;
export default StatusAPI;
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object]),
    __metadata("design:returntype", Function)
], StatusAPI, "init", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, Object, Object, Object]),
    __metadata("design:returntype", Promise)
], StatusAPI, "handleReadyCheck", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, Object, Object, Object]),
    __metadata("design:returntype", Promise)
], StatusAPI, "handleLiveCheck", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, Object]),
    __metadata("design:returntype", Promise)
], StatusAPI, "handleGlobalCacheCheck", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, Object]),
    __metadata("design:returntype", Promise)
], StatusAPI, "handleAnalyticsDatabaseCheck", null);
__decorate([
    CaptureSpan(),
    __metadata("design:type", Function),
    __metadata("design:paramtypes", [Object, Object, Object]),
    __metadata("design:returntype", Promise)
], StatusAPI, "handleDatabaseCheck", null);
//# sourceMappingURL=StatusAPI.js.map