/**
 * Healthz client — types and functions for probing OwlCoda daemon health.
 * Extracted from cli-core.ts for modularity.
 */
import { createHash } from 'node:crypto';
import { get as httpGet } from 'node:http';
// ─── Host resolution ───
export function resolveClientHost(bindHost) {
    const wildcards = ['0.0.0.0', '::', ':::', ''];
    if (wildcards.includes(bindHost))
        return '127.0.0.1';
    return bindHost;
}
export function configIdentityFingerprint(config) {
    const identity = {
        host: resolveClientHost(config.host),
        port: config.port,
        routerUrl: config.routerUrl,
        localRuntimeProtocol: config.localRuntimeProtocol ?? 'auto',
        responseModelStyle: config.responseModelStyle ?? 'platform',
        models: (config.models ?? []).map(model => ({
            id: model.id ?? '',
            backendModel: model.backendModel ?? '',
            endpoint: model.endpoint ?? '',
            apiKeySet: Boolean(model.apiKey),
            apiKeyEnv: model.apiKeyEnv ?? '',
            default: model.default === true,
            aliases: [...(model.aliases ?? [])].sort(),
        })).sort((a, b) => a.id.localeCompare(b.id)),
    };
    return createHash('sha256').update(JSON.stringify(identity)).digest('hex').slice(0, 16);
}
export function healthzMatchesConfig(healthz, config, options = {}) {
    if (healthz.port !== config.port)
        return false;
    if (healthz.routerUrl !== config.routerUrl)
        return false;
    const healthzClient = resolveClientHost(healthz.host);
    const configClient = resolveClientHost(config.host);
    if (healthzClient !== configClient)
        return false;
    if (options.requireConfigFingerprint) {
        return healthz.configFingerprint === configIdentityFingerprint(config);
    }
    return true;
}
export function healthzMatchesRuntimeMeta(healthz, meta) {
    // Status check is about identity, not health — accept any valid status
    const validStatuses = ['ok', 'healthy', 'degraded', 'unhealthy'];
    if (!validStatuses.includes(healthz.status))
        return false;
    if (healthz.pid !== meta.pid)
        return false;
    if (healthz.runtimeToken !== meta.runtimeToken)
        return false;
    if (healthz.port !== meta.port)
        return false;
    if (healthz.routerUrl !== meta.routerUrl)
        return false;
    return resolveClientHost(healthz.host) === resolveClientHost(meta.host);
}
// ─── Healthz HTTP client ───
export function fetchHealthz(baseUrl, timeoutMs = 2000) {
    return new Promise(resolve => {
        const url = `${baseUrl}/healthz`;
        let req;
        try {
            req = httpGet(url, res => {
                const chunks = [];
                res.on('data', (chunk) => chunks.push(chunk));
                res.on('end', () => {
                    try {
                        const body = JSON.parse(Buffer.concat(chunks).toString('utf-8'));
                        resolve(body);
                    }
                    catch {
                        resolve(null);
                    }
                });
            });
        }
        catch {
            resolve(null);
            return;
        }
        req.on('error', () => resolve(null));
        req.setTimeout(timeoutMs, () => { req.destroy(); resolve(null); });
    });
}
// ─── Healthz waiters ───
export async function waitForVerifiedHealthz(baseUrl, matcher, timeoutMs = 5000) {
    const start = Date.now();
    while (Date.now() - start <= timeoutMs) {
        const healthz = await fetchHealthz(baseUrl, 500);
        if (healthz && matcher(healthz))
            return healthz;
        await new Promise(resolve => setTimeout(resolve, 150));
    }
    return null;
}
export function waitForHealthzGone(baseUrl, timeoutMs = 3000) {
    const start = Date.now();
    return new Promise(resolve => {
        const check = () => {
            let req;
            try {
                req = httpGet(`${baseUrl}/healthz`, _res => {
                    _res.resume();
                    if (Date.now() - start > timeoutMs) {
                        resolve(false);
                    }
                    else {
                        setTimeout(check, 150);
                    }
                });
            }
            catch {
                resolve(true);
                return;
            }
            req.on('error', () => {
                resolve(true);
            });
            req.setTimeout(500, () => { req.destroy(); });
        };
        check();
    });
}
//# sourceMappingURL=healthz-client.js.map