/**
 * auth-refresh-kubernetes.ts — D4 kubernetes Secret rotation for `olam auth refresh`.
 *
 * Extracted from auth.ts to keep the testable logic free of @olam/core dependencies.
 * No @olam/core imports here — tested directly by auth.test.ts without needing
 * to mock the entire @olam/core workspace package.
 *
 * D20 security invariant: secret value NEVER in subprocess argv (–from-literal forbidden).
 * SEC-NEW-003: refuses when kubectl_context_pinned absent (no ambient KUBECONFIG fallthrough).
 * ADV-N-003: kubectl rollout restart after apply (K8s does NOT hot-update mounted Secrets).
 */
import * as fs from 'node:fs';
import * as path from 'node:path';
import pc from 'picocolors';
import { stringify as yamlStringify } from 'yaml';
import { OLAM_HOME } from './config.js';
import { kubectlWrap } from './kubectl-wrap.js';
import { HOST_CP_SECRET_NAME, HOST_CP_DEPLOYMENT_NAME } from './upgrade-kubernetes.js';
import { resolveKubectlContext as resolveKubectlContextShared } from './kubectl-context.js';
const K8S_NAMESPACE = 'olam';
/**
 * Construct the olam-host-cp-secret YAML in-process (D20 stdin-safe design).
 * Values are base64-encoded via Buffer. NEVER uses --from-literal in subprocess argv.
 */
export function buildSecretYaml(secretName, data) {
    const base64Data = {};
    for (const [key, value] of Object.entries(data)) {
        base64Data[key] = Buffer.from(value).toString('base64');
    }
    return yamlStringify({
        apiVersion: 'v1',
        kind: 'Secret',
        metadata: { name: secretName, namespace: K8S_NAMESPACE },
        type: 'Opaque',
        data: base64Data,
    });
}
/**
 * Resolve the pinned kubectl context from config or OLAM_K8S_CONTEXT_ACK fallback.
 *
 * Returns { context } on success.
 * Returns { error } when neither source has a context (SEC-NEW-003 enforcement).
 * Returns { context, deprecationWarning } when OLAM_K8S_CONTEXT_ACK fallback is used.
 *
 * Re-exports the canonical implementation from `kubectl-context.ts` so all
 * call sites share one policy.
 */
export function resolveKubectlContext(configPath) {
    return resolveKubectlContextShared(configPath);
}
/**
 * Run the kubernetes-substrate portion of auth refresh (D4).
 *
 * Called by auth.ts after a successful `client.refreshAccount()` when
 * the substrate is kubernetes. Performs:
 *   1. Reads ~/.olam/auth-secret for the bearer secret value.
 *   2. Writes ~/.olam/auth-secret back (idempotent, sets mode 0o600).
 *   3. Constructs olam-host-cp-secret YAML in-process (D20: value in stdin, not argv).
 *   4. kubectl apply -f - via stdin pipe.
 *   5. kubectl rollout restart deployment/olam-host-cp -n olam (ADV-N-003).
 *
 * Returns exit code: 0 = success, 1 = error.
 */
export async function applyK8sAuthRefresh(pinnedContext, deps = {}) {
    const stdout = deps.stdout ?? process.stdout;
    const stderr = deps.stderr ?? process.stderr;
    const olamHome = deps.olamHomeOverride ?? OLAM_HOME;
    const authSecretFile = path.join(olamHome, 'auth-secret');
    const readFn = deps.readFileSyncImpl ?? ((p, enc) => fs.readFileSync(p, enc));
    const writeFn = deps.writeFileSyncImpl ?? ((p, data, opts) => {
        fs.mkdirSync(path.dirname(p), { recursive: true });
        fs.writeFileSync(p, data, { encoding: 'utf8', mode: opts.mode });
    });
    const wrap = deps.kubectlWrapImpl ?? kubectlWrap;
    // Read current ~/.olam/auth-secret.
    let authSecretValue = null;
    try {
        const raw = readFn(authSecretFile, 'utf8').trim();
        if (raw.length > 0)
            authSecretValue = raw;
    }
    catch {
        // File absent — warn and skip Secret update.
    }
    // Write ~/.olam/auth-secret (both substrates; idempotent).
    if (authSecretValue !== null) {
        writeFn(authSecretFile, authSecretValue + '\n', { mode: 0o600 });
    }
    else {
        stderr.write(`${pc.yellow('[warn]')} ~/.olam/auth-secret not found — skipping kubernetes Secret update. ` +
            `Run \`olam upgrade\` to populate the Secret.\n`);
        return 0;
    }
    // D20: construct Secret YAML in-process; secret value NEVER in subprocess argv.
    const secretYaml = buildSecretYaml(HOST_CP_SECRET_NAME, {
        OLAM_AUTH_SECRET: authSecretValue,
    });
    // Apply the Secret via stdin pipe.
    const applyResult = await wrap(['--context', pinnedContext, 'apply', '-f', '-'], { timeout: 30_000, stdin: secretYaml });
    if (!applyResult.ok) {
        stderr.write(`${pc.red('error:')} kubectl apply (Secret update) failed: ${applyResult.stderr.split('\n')[0] ?? ''}\n`);
        return 1;
    }
    // ADV-N-003: rollout restart to flush the mounted Secret into host-cp.
    const rolloutResult = await wrap(['--context', pinnedContext, 'rollout', 'restart', `deployment/${HOST_CP_DEPLOYMENT_NAME}`, '-n', K8S_NAMESPACE], { timeout: 60_000 });
    if (!rolloutResult.ok) {
        stderr.write(`${pc.yellow('[warn]')} rollout restart failed: ${rolloutResult.stderr.split('\n')[0] ?? ''}. ` +
            `host-cp may not pick up the new token until the next pod restart.\n`);
        // Non-fatal: apply succeeded; warn but return 0.
    }
    else {
        stdout.write(`${pc.green('✓')} Restarted deployment/${HOST_CP_DEPLOYMENT_NAME} (new token mounted)\n`);
    }
    return 0;
}
//# sourceMappingURL=auth-refresh-kubernetes.js.map