/**
 * k8s-context-discovery.ts — kubeconfig context enumeration + reachability + idleness probes.
 *
 * Used by `olam setup` Phase 0.5 (ADR 021) to detect existing reachable
 * Kubernetes contexts and offer to reuse them instead of provisioning a
 * new k3d cluster.
 *
 * All external I/O is injected via the `KubeContextDeps` interface for
 * testability. Production callers use the default implementations which
 * shell out to `kubectl`.
 */
import { spawnSync } from 'node:child_process';
// ── k3d-managed context names we own — excluded from the reuse offer ──────────
/**
 * Context names provisioned by `olam setup` — never offered for reuse.
 * These are ours to manage (create/destroy via k3d).
 */
export const OLAM_MANAGED_CONTEXT_PREFIX = 'k3d-olam-';
export function isOlamManagedContext(name) {
    return name.startsWith(OLAM_MANAGED_CONTEXT_PREFIX);
}
// ── Default implementations ───────────────────────────────────────────────────
/**
 * Default implementation: enumerate kubeconfig contexts via
 * `kubectl config get-contexts -o name`.
 * Returns an empty array when kubectl is absent or returns no contexts.
 */
export function defaultGetContexts() {
    const r = spawnSync('kubectl', ['config', 'get-contexts', '-o', 'name'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
    if (r.status !== 0 || !r.stdout)
        return [];
    return r.stdout
        .split('\n')
        .map((l) => l.trim())
        .filter(Boolean);
}
/**
 * Default implementation: probe a context via
 * `kubectl --context=<name> cluster-info --request-timeout=3s`.
 *
 * Returns reachable:true when exit status is 0.
 * Returns reachable:false + the stderr text otherwise.
 */
export function defaultClusterInfo(context) {
    const r = spawnSync('kubectl', ['--context', context, 'cluster-info', '--request-timeout=3s'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
    if (r.status === 0) {
        // Extract server URL from stdout if present (first line looks like
        // "Kubernetes control plane is running at https://127.0.0.1:6443")
        const match = r.stdout.match(/running at (https?:\/\/\S+)/i);
        return { reachable: true, serverUrl: match?.[1] };
    }
    const err = (r.stderr || '').trim().slice(0, 200);
    return { reachable: false, error: err || 'cluster-info failed' };
}
/**
 * Default implementation: a context is idle when there is no `olam`
 * namespace OR the namespace exists but has no running pods.
 *
 * "Idle" means we haven't deployed Olam there yet — safe to offer for reuse.
 */
export function defaultIsIdle(context) {
    // Check whether the `olam` namespace exists
    const nsResult = spawnSync('kubectl', ['--context', context, 'get', 'namespace', 'olam', '--ignore-not-found', '-o', 'name'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
    const nsLine = (nsResult.stdout || '').trim();
    if (!nsLine) {
        // No `olam` namespace → definitely idle
        return true;
    }
    // Namespace exists — check if any pods are running
    const podsResult = spawnSync('kubectl', ['--context', context, 'get', 'pods', '-n', 'olam', '--no-headers'], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
    const podsOut = (podsResult.stdout || '').trim();
    // If output is empty or just "No resources found..." the ns is empty → idle
    return !podsOut || /no resources found/i.test(podsOut);
}
/**
 * Discover reachable idle kubeconfig contexts that could be offered for reuse.
 *
 * Steps:
 *   1. Enumerate all contexts from kubeconfig.
 *   2. Filter out k3d-olam-* (olam-managed).
 *   3. For each remaining context, probe reachability (3s timeout).
 *   4. For reachable contexts, check idleness.
 *   5. Return the offerable set.
 */
export function discoverOfferableContexts(deps = {}) {
    const getContextsFn = deps.getContexts ?? defaultGetContexts;
    const clusterInfoFn = deps.clusterInfo ?? defaultClusterInfo;
    const isIdleFn = deps.isIdle ?? defaultIsIdle;
    const allContexts = getContextsFn();
    const offerable = [];
    for (const ctx of allContexts) {
        if (isOlamManagedContext(ctx))
            continue;
        const probe = clusterInfoFn(ctx);
        if (!probe.reachable)
            continue;
        if (isIdleFn(ctx))
            offerable.push(ctx);
    }
    return { offerable, allContexts };
}
//# sourceMappingURL=k8s-context-discovery.js.map