import { timingSafeEqual } from "node:crypto";
import { Router } from "express";
import { and, count, eq, gt, inArray, isNull, sql } from "drizzle-orm";
import { heartbeatRuns, instanceUserRoles, invites } from "@paperclipai/db";
import { readPersistedDevServerStatus, toDevServerHealthStatus, writeDevServerRestartRequest } from "../dev-server-status.js";
import { logger } from "../middleware/logger.js";
import { instanceSettingsService } from "../services/instance-settings.js";
import { serverVersion } from "../version.js";
function shouldExposeFullHealthDetails(actorType, deploymentMode) {
    if (deploymentMode !== "authenticated")
        return true;
    return actorType === "board" || actorType === "agent";
}
function hasDevServerStatusToken(providedToken) {
    const expectedToken = process.env.PAPERCLIP_DEV_SERVER_STATUS_TOKEN?.trim();
    const token = providedToken?.trim();
    if (!expectedToken || !token)
        return false;
    const expected = Buffer.from(expectedToken);
    const provided = Buffer.from(token);
    if (expected.length !== provided.length)
        return false;
    return timingSafeEqual(expected, provided);
}
export function healthRoutes(db, opts = {
    deploymentMode: "local_trusted",
    deploymentExposure: "private",
    authReady: true,
    companyDeletionEnabled: true,
}) {
    const router = Router();
    router.post("/dev-server/restart", async (req, res) => {
        const actorType = "actor" in req ? req.actor?.type : null;
        if (opts.deploymentMode === "authenticated" && actorType !== "board") {
            res.status(403).json({ error: "board_access_required" });
            return;
        }
        const persistedDevServerStatus = readPersistedDevServerStatus();
        if (!persistedDevServerStatus) {
            res.status(404).json({ error: "dev_server_supervisor_unavailable" });
            return;
        }
        const restartRequired = persistedDevServerStatus.dirty ||
            persistedDevServerStatus.changedPathCount > 0 ||
            persistedDevServerStatus.pendingMigrations.length > 0;
        if (!restartRequired) {
            res.status(409).json({ error: "restart_not_required" });
            return;
        }
        const written = writeDevServerRestartRequest({
            requestedAt: new Date().toISOString(),
            reason: "manual_restart_now",
        });
        if (!written) {
            res.status(404).json({ error: "dev_server_supervisor_unavailable" });
            return;
        }
        res.status(202).json({ status: "restart_requested" });
    });
    router.get("/", async (req, res) => {
        const actorType = "actor" in req ? req.actor?.type : null;
        const exposeFullDetails = shouldExposeFullHealthDetails(actorType, opts.deploymentMode);
        const exposeDevServerDetails = exposeFullDetails || hasDevServerStatusToken(req.get("x-paperclip-dev-server-status-token"));
        if (!db) {
            res.json(exposeFullDetails
                ? { status: "ok", version: serverVersion }
                : { status: "ok", deploymentMode: opts.deploymentMode });
            return;
        }
        try {
            await db.execute(sql `SELECT 1`);
        }
        catch (error) {
            logger.warn({ err: error }, "Health check database probe failed");
            res.status(503).json({
                status: "unhealthy",
                version: serverVersion,
                error: "database_unreachable"
            });
            return;
        }
        let bootstrapStatus = "ready";
        let bootstrapInviteActive = false;
        if (opts.deploymentMode === "authenticated") {
            const roleCount = await db
                .select({ count: count() })
                .from(instanceUserRoles)
                .where(sql `${instanceUserRoles.role} = 'instance_admin'`)
                .then((rows) => Number(rows[0]?.count ?? 0));
            bootstrapStatus = roleCount > 0 ? "ready" : "bootstrap_pending";
            if (bootstrapStatus === "bootstrap_pending") {
                const now = new Date();
                const inviteCount = await db
                    .select({ count: count() })
                    .from(invites)
                    .where(and(eq(invites.inviteType, "bootstrap_ceo"), isNull(invites.revokedAt), isNull(invites.acceptedAt), gt(invites.expiresAt, now)))
                    .then((rows) => Number(rows[0]?.count ?? 0));
                bootstrapInviteActive = inviteCount > 0;
            }
        }
        const persistedDevServerStatus = readPersistedDevServerStatus();
        let devServer;
        if (exposeDevServerDetails && persistedDevServerStatus && typeof db.select === "function") {
            const instanceSettings = instanceSettingsService(db);
            const experimentalSettings = await instanceSettings.getExperimental();
            const activeRunCount = await db
                .select({ count: count() })
                .from(heartbeatRuns)
                .where(inArray(heartbeatRuns.status, ["queued", "running"]))
                .then((rows) => Number(rows[0]?.count ?? 0));
            devServer = toDevServerHealthStatus(persistedDevServerStatus, {
                autoRestartEnabled: experimentalSettings.autoRestartDevServerWhenIdle ?? false,
                activeRunCount,
            });
        }
        if (!exposeFullDetails) {
            res.json({
                status: "ok",
                deploymentMode: opts.deploymentMode,
                deploymentExposure: opts.deploymentExposure,
                bootstrapStatus,
                bootstrapInviteActive,
                ...(devServer ? { devServer } : {}),
            });
            return;
        }
        res.json({
            status: "ok",
            version: serverVersion,
            deploymentMode: opts.deploymentMode,
            deploymentExposure: opts.deploymentExposure,
            authReady: opts.authReady,
            bootstrapStatus,
            bootstrapInviteActive,
            features: {
                companyDeletionEnabled: opts.companyDeletionEnabled,
            },
            ...(devServer ? { devServer } : {}),
        });
    });
    return router;
}
//# sourceMappingURL=health.js.map