/**
 * Hosting info and deploy status from platform IPC.
 */
import fs from "fs";
import path from "path";
import { getContext } from "../client.js";
import { readJsonFile, writeIpcFile, pollJsonFile } from "../ipc.js";
/**
 * Request a fresh hosting snapshot from the host via IPC, then read the result.
 * Falls back to reading the existing (possibly stale) hosting.json on timeout.
 */
export async function getHostingInfo(ipcDir) {
    const dir = ipcDir ?? getContext().ipcDir;
    const hostingFile = path.join(dir, "hosting.json");
    // Request fresh snapshot via IPC (only when running inside a container with IPC)
    const ipcMessagesDir = path.join(dir, "messages");
    if (fs.existsSync(ipcMessagesDir)) {
        const before = getFileMtime(hostingFile);
        writeIpcFile(ipcMessagesDir, { type: "hosting-snapshot" });
        try {
            await pollJsonFile(hostingFile, () => getFileMtime(hostingFile) > before, 5000, 500);
        }
        catch { /* timeout — fall through to stale read */ }
    }
    const data = readJsonFile(hostingFile);
    return data ?? {};
}
function getFileMtime(filePath) {
    try {
        return fs.statSync(filePath).mtimeMs;
    }
    catch {
        return 0;
    }
}
export async function getAppStatus(app, ipcDir) {
    const dir = ipcDir ?? getContext().ipcDir;
    const statusDir = path.join(dir, "hosting-status");
    if (!fs.existsSync(statusDir))
        return [];
    if (app) {
        const data = readJsonFile(path.join(statusDir, `${app}.json`));
        return data ? [data] : [];
    }
    const files = fs.readdirSync(statusDir).filter((f) => f.endsWith(".json"));
    return files.map((f) => {
        const data = readJsonFile(path.join(statusDir, f));
        return data;
    }).filter(Boolean);
}
export async function getDeployErrors(limit = 5, ipcDir) {
    const dir = ipcDir ?? getContext().ipcDir;
    const errorsDir = path.join(dir, "errors");
    if (!fs.existsSync(errorsDir))
        return [];
    const files = fs.readdirSync(errorsDir)
        .filter((f) => f.endsWith(".json"))
        .sort()
        .reverse()
        .slice(0, limit);
    return files.map((f) => {
        const data = readJsonFile(path.join(errorsDir, f));
        return data;
    }).filter(Boolean);
}
/**
 * Wait for an app-apply to complete, then return the final deploy status.
 * Polls {ipcDir}/hosting-status/{app}.json every 2s until stage !== building or timeout.
 */
export async function waitForDeploy(app, timeoutSeconds, ipcDir) {
    const dir = ipcDir ?? getContext().ipcDir;
    const statusFile = path.join(dir, "hosting-status", `${app}.json`);
    const timeoutMs = (timeoutSeconds ?? 120) * 1000;
    return pollJsonFile(statusFile, (d) => {
        const data = d;
        return typeof data.stage === "string" && data.stage !== "building";
    }, timeoutMs);
}
/**
 * Returns port occupancy for this tenant's allocated port range.
 * Requests a fresh hosting snapshot via IPC, then cross-references with usedPorts.
 * Identifies zombie containers (running on a port that isn't registered to them).
 */
export async function getPortOccupancy(ipcDir) {
    const dir = ipcDir ?? getContext().ipcDir;
    // getHostingInfo already requests a fresh snapshot via IPC
    const snapshot = await getHostingInfo(dir);
    if (!snapshot || Object.keys(snapshot).length === 0)
        return [];
    const usedPorts = Array.isArray(snapshot.usedPorts) ? snapshot.usedPorts : [];
    const liveContainers = Array.isArray(snapshot.liveContainers)
        ? snapshot.liveContainers
        : [];
    const allPorts = new Set([
        ...usedPorts,
        ...liveContainers.map((c) => c.port),
    ]);
    const result = [];
    for (const port of [...allPorts].sort((a, b) => a - b)) {
        const container = liveContainers.find((c) => c.port === port) ?? null;
        // declaredBy: scan app status files for an app registered on this port
        let declaredBy = null;
        const statusDir = path.join(dir, "hosting-status");
        if (fs.existsSync(statusDir)) {
            const files = fs.readdirSync(statusDir).filter((f) => f.endsWith(".json") && !/-(?:logs|exec|result)/.test(f));
            for (const file of files) {
                const data = readJsonFile(path.join(statusDir, file));
                if (data?.hostPort === port) {
                    declaredBy = String(data.appName ?? file.replace(".json", ""));
                    break;
                }
            }
        }
        const conflict = container !== null && declaredBy !== null && container.app !== declaredBy;
        result.push({ port, declaredBy, container, conflict });
    }
    return result;
}
//# sourceMappingURL=hosting.js.map