/**
 * olam substrate — Manage the operator-facing deployment substrate.
 *
 * Phase 1b A2 of olam-host-suite-phase-1b-k3s-beta-flavour (plan
 * ~/.claude/plans/olam-host-suite-phase-1b-k3s-beta-flavour.md).
 *
 * Subcommands:
 *   olam substrate get               — print active substrate (+ flavor + beta-expiry)
 *   olam substrate set <substrate>   — switch to compose | kubernetes
 *
 * Decisions consumed:
 *   D1 — `~/.olam/config.json` schema-versioned seam (host.substrate)
 *   D2 — user-facing vocab is `compose | kubernetes` (NOT docker | k3s)
 *   D7 — telemetry is opt-in stdout-emission (stderr `OLAM_INSTR`)
 *   D21 — canonical instrumentation event schema (olam.instr.v1)
 *
 * Risk mitigations:
 *   T2 — pre-flight via `runDoctor` programmatically with `engine: <target>` override.
 *        Refuses with actionable remediation if prereqs fail.
 *   T3 — refuses if worlds exist on current substrate (operator must `olam destroy --all`).
 *
 * Beta-expiry date: 2026-11-16 (Phase 1b ship-date + 6 months per Falsifiable
 * signal section; surfaced in `get` output so operators see the commitment).
 */
import pc from 'picocolors';
import { readConfig, writeConfig } from '../lib/config.js';
import { emitSubstrateSet } from '../lib/instrumentation.js';
import { runDoctor, runK8sPreflight, runComposePreflight } from './doctor.js';
import { loadContext } from '../context.js';
import { printError, printInfo, printSuccess, printWarning } from '../output.js';
import { registerSubstrateAuditLog } from './substrate-audit-log.js';
export const BETA_EXPIRY_DATE = '2026-11-16';
async function defaultCountWorlds() {
    const { ctx } = await loadContext();
    if (!ctx)
        return 0;
    return ctx.worldManager.listWorlds().length;
}
function formatBetaLine(cfg) {
    if (cfg.host.substrate === 'compose') {
        return `Substrate: ${pc.bold('compose')}`;
    }
    // flavor is informational only; left to D11 doctor probe to detect at runtime.
    // Static get-time output names `k3s` as the first-supported flavor per the plan.
    return `Substrate: ${pc.bold('kubernetes')} (beta until ${BETA_EXPIRY_DATE}, flavor=k3s)`;
}
/**
 * Implementation of `olam substrate get`. Reads the config + prints a single
 * human-readable line. Exported for testability.
 */
export async function handleGet(deps = {}) {
    const read = deps.readConfig ?? readConfig;
    const stdout = deps.stdout ?? process.stdout;
    const cfg = read();
    stdout.write(formatBetaLine(cfg) + '\n');
    return 0;
}
/**
 * Implementation of `olam substrate set <substrate>`. Validates target, runs
 * pre-flight via doctor with the target engine, refuses on worlds-exist OR
 * doctor-fail, persists config, emits instrumentation. Exported for testability.
 *
 * Returns the desired process exit code (0 = success, 1 = refused/fail).
 */
export async function handleSet(target, deps = {}) {
    const stderr = deps.stderr ?? process.stderr;
    const stdout = deps.stdout ?? process.stdout;
    const read = deps.readConfig ?? readConfig;
    const write = deps.writeConfig ?? writeConfig;
    const emit = deps.emit ?? emitSubstrateSet;
    const runDoctorFn = deps.runDoctor ?? runDoctor;
    const k8sPreflightFn = deps.runK8sPreflightFn ?? runK8sPreflight;
    const composePreflightFn = deps.runComposePreflightFn ?? runComposePreflight;
    const countWorlds = deps.countWorlds ?? defaultCountWorlds;
    // Suppress unused-var lint when the handler doesn't currently call runDoctorFn.
    // It stays on the deps surface for tests that assert it was NOT called.
    void runDoctorFn;
    if (target !== 'compose' && target !== 'kubernetes') {
        stderr.write(`${pc.red('error:')} unsupported substrate "${target}". Expected: compose | kubernetes.\n`);
        return 1;
    }
    const targetSubstrate = target;
    const current = read();
    if (current.host.substrate === targetSubstrate) {
        stdout.write(`Substrate already set to ${pc.bold(targetSubstrate)}. No change.\n`);
        return 0;
    }
    // T3: refuse if worlds exist on the current substrate. Operator must
    // explicitly `olam destroy --all` to avoid bisected worlds.db state.
    let worldsCount;
    try {
        worldsCount = await countWorlds();
    }
    catch (err) {
        stderr.write(`${pc.yellow('warn:')} could not list worlds (${err instanceof Error ? err.message : String(err)}); assuming none exist and proceeding.\n`);
        worldsCount = 0;
    }
    if (worldsCount > 0) {
        stderr.write(`${pc.red('error:')} ${worldsCount} world(s) exist on the current substrate (${pc.bold(current.host.substrate)}). Switching substrates abandons their state.\n`);
        stderr.write(`       Run ${pc.bold('olam destroy --all')} first, then re-run this command.\n`);
        return 1;
    }
    // T2: pre-flight for the target engine.
    //
    // BOTH directions use a NARROWED preflight that only checks the single gate
    // an operator can satisfy BEFORE the deploy step:
    //   - kubernetes: kubectl present + cluster API reachable (runK8sPreflight, Issue #713)
    //   - compose:    docker daemon reachable                  (runComposePreflight)
    //
    // The full `runDoctor` probe suite (host-cp health, auth vault, KG storage,
    // engine header) requires host-cp to already be deployed on the target engine.
    // Running it pre-deploy causes false failures — the symmetric bug to Issue #713
    // observed in the compose direction when an operator switches FROM kubernetes
    // TO compose on a host that has never run compose host-cp.
    //
    // Operators run the full `olam doctor` AFTER `olam upgrade` / `olam bootstrap`
    // to confirm post-deploy health.
    stdout.write(`Running pre-flight for ${pc.bold(targetSubstrate)} substrate…\n`);
    let doctorResult;
    try {
        if (targetSubstrate === 'kubernetes') {
            doctorResult = await k8sPreflightFn({ json: true });
        }
        else {
            doctorResult = await composePreflightFn({ json: true });
        }
    }
    catch (err) {
        stderr.write(`${pc.red('error:')} pre-flight crashed: ${err instanceof Error ? err.message : String(err)}\n`);
        return 1;
    }
    if (doctorResult.exitCode !== 0) {
        stderr.write(`${pc.red('error:')} pre-flight failed for ${pc.bold(targetSubstrate)} substrate.\n`);
        stderr.write(`       Run ${pc.bold(`olam doctor${targetSubstrate === 'kubernetes' ? ' --substrate=kubernetes' : ''}`)} to see what's missing, fix the named prereqs, then re-run.\n`);
        return 1;
    }
    // Persist + emit only after the gates pass.
    const next = write({ host: { substrate: targetSubstrate } });
    emit({
        substrate: targetSubstrate,
        flavor: targetSubstrate === 'kubernetes' ? 'k3s' : undefined,
    });
    stdout.write(formatBetaLine(next) + '\n');
    if (targetSubstrate === 'kubernetes') {
        stdout.write(`\nKubernetes substrate is in beta until ${BETA_EXPIRY_DATE}.\n` +
            `Rollback: ${pc.bold('olam substrate set compose')} (preserves compose worlds you create after this).\n` +
            `Next: ${pc.bold('olam upgrade')} to deploy host-cp to your kubectl context.\n`);
    }
    else {
        stdout.write(`\nNext: ${pc.bold('olam upgrade')} to redeploy host-cp on the compose substrate.\n`);
    }
    return 0;
}
export function registerSubstrate(program) {
    const sub = program.command('substrate').description('Manage deployment substrate (beta)');
    sub
        .command('get')
        .description('Print the active substrate')
        .action(async () => {
        try {
            const code = await handleGet();
            if (code !== 0)
                process.exitCode = code;
        }
        catch (err) {
            printError(`olam substrate get failed: ${err instanceof Error ? err.message : String(err)}`);
            process.exitCode = 1;
        }
    });
    sub
        .command('set <substrate>')
        .description('Switch substrate (compose | kubernetes)')
        .action(async (target) => {
        try {
            const code = await handleSet(target);
            if (code !== 0)
                process.exitCode = code;
        }
        catch (err) {
            printError(`olam substrate set failed: ${err instanceof Error ? err.message : String(err)}`);
            process.exitCode = 1;
        }
    });
    // Phase 1b C3 — D19: manifest-refresh audit log inspection.
    registerSubstrateAuditLog(sub);
}
// Silence unused-import lints when these helpers are referenced only via
// indirect calls (printInfo/printWarning/printSuccess are imported for parity
// with sibling commands' patterns even though substrate uses stdout/stderr
// directly for clean test injection).
void printInfo;
void printWarning;
void printSuccess;
//# sourceMappingURL=substrate.js.map