/**
 * Variable resolver — handles dynamic variable interpolation.
 *
 * Resolves dynamic variables ($latest, $env, $now, etc.) in task
 * configurations. All resolved values are recorded in the
 * VariableEnvelope with provenance metadata for audit and cache
 * invalidation.
 *
 * Design decision: dynamic variables are opt-in and logged.
 * A task must explicitly use `$latest` or `$env(VAR)` syntax —
 * there's no implicit resolution. Every dynamic resolution is
 * recorded in provenance for reproducibility tracking.
 *
 * @see docs/design-docs/architecture-overhaul/domain-model.md (VariableEnvelope)
 * @see docs/archive/exec-plans/architecture-overhaul/phase-2-config-compiler.md
 */
import { simpleHash } from "./hash.js";
// ---------------------------------------------------------------------------
// Dynamic variable patterns
// ---------------------------------------------------------------------------
/** Pattern for $env(VAR_NAME) references */
const ENV_PATTERN = /\$env\(([A-Z_][A-Z0-9_]*)\)/g;
/** Pattern for $latest references (resolved to current timestamp) */
const LATEST_PATTERN = /\$latest/g;
/** Pattern for $now references (resolved to ISO timestamp) */
const NOW_PATTERN = /\$now/g;
/**
 * Resolve dynamic variables in a VariableEnvelope.
 *
 * Scans all string values for dynamic patterns and replaces them
 * with resolved values. Non-string values are passed through unchanged.
 */
export function resolveVariables(envelope, options) {
    const resolutions = [];
    const warnings = [];
    const resolvedValues = {};
    const resolvedProvenance = {
        ...envelope.provenance,
    };
    const envVars = options?.env ?? process.env;
    const log = options?.log ?? (() => { });
    const now = new Date().toISOString();
    for (const [key, value] of Object.entries(envelope.values)) {
        if (typeof value !== "string") {
            resolvedValues[key] = value;
            continue;
        }
        let resolved = value;
        let wasResolved = false;
        // Resolve $env(VAR_NAME)
        resolved = resolved.replace(ENV_PATTERN, (_match, varName) => {
            const envValue = envVars[varName];
            if (envValue === undefined) {
                warnings.push(`Variable "${key}": $env(${varName}) could not be resolved — ` +
                    `environment variable "${varName}" is not set`);
                return _match; // Leave unresolved
            }
            wasResolved = true;
            const msg = `Resolved $env(${varName}) in "${key}"`;
            resolutions.push(msg);
            log(msg);
            return envValue;
        });
        // Resolve $latest — alias for $now (same ISO timestamp).
        // Both exist for readability: $now in "run at $now" vs $latest in
        // "compare against $latest baseline". Semantically identical.
        resolved = resolved.replace(LATEST_PATTERN, () => {
            wasResolved = true;
            const msg = `Resolved $latest in "${key}" to ${now} (alias for $now)`;
            resolutions.push(msg);
            log(msg);
            return now;
        });
        // Resolve $now — current ISO timestamp
        resolved = resolved.replace(NOW_PATTERN, () => {
            wasResolved = true;
            const msg = `Resolved $now in "${key}" to ${now}`;
            resolutions.push(msg);
            log(msg);
            return now;
        });
        resolvedValues[key] = resolved;
        if (wasResolved) {
            resolvedProvenance[key] = {
                hash: simpleHash(resolved),
                resolvedAt: now,
                source: { expression: value, inputs: [key], type: "derived" },
            };
        }
    }
    return {
        envelope: {
            declarations: envelope.declarations,
            provenance: resolvedProvenance,
            values: resolvedValues,
        },
        resolutions,
        warnings,
    };
}
/**
 * Create a VariableEnvelope from a plain key-value map.
 *
 * Convenience function for wrapping raw variables with empty provenance.
 */
export function createEnvelope(values, declarations) {
    return {
        declarations: declarations ?? [],
        provenance: {},
        values,
    };
}
// ---------------------------------------------------------------------------
// Hash helper (shared with fixture-resolver)
// ---------------------------------------------------------------------------
