/**
 * GC-PERM-011 — Policy loader with signature validation.
 *
 * Wraps the raw policy bundle loading path with a signature validation
 * step. In managed mode, bundles with invalid or missing signatures are
 * rejected. In non-managed mode, unsigned bundles are allowed through
 * with a `SignatureStatus` of `'unsigned'` for UI display.
 *
 * This module does NOT perform I/O itself — it accepts a pre-parsed
 * `SignedPolicyBundle` so callers control the loading strategy.
 */
import { GoodVibesSdkError } from '@pellux/goodvibes-errors';
import { verifyBundle } from './policy-signer.js';
import { isFeatureGateEnabled } from '../feature-flags/index.js';
// ── Error class ───────────────────────────────────────────────────────────────
/**
 * PolicySignatureError — Thrown when a managed-mode bundle fails signature
 * validation and the caller opts into strict error throwing.
 *
 * Managed mode MUST reject bundles with invalid or missing signatures.
 */
export class PolicySignatureError extends GoodVibesSdkError {
    bundleId;
    signatureStatus;
    constructor(
    /** The bundle ID that failed validation. */
    bundleId, 
    /** The validation outcome. */
    signatureStatus, message) {
        super(message, { code: 'POLICY_SIGNATURE_INVALID', category: 'permission', source: 'runtime', recoverable: false });
        this.bundleId = bundleId;
        this.signatureStatus = signatureStatus;
        this.name = 'PolicySignatureError';
    }
}
// ── Core loader ───────────────────────────────────────────────────────────────
/**
 * loadPolicyBundle — Validates and loads a signed policy bundle.
 *
 * Performs signature verification and returns the extracted rules with
 * a full provenance record. In managed mode, bundles that are unsigned,
 * missing a signature, or carry an invalid signature are rejected.
 *
 * In non-managed mode, unsigned bundles are accepted with status `'unsigned'`
 * so the UI can display a warning.
 *
 * @param bundle  — The pre-parsed signed bundle object.
 * @param options — Loader behaviour options.
 *
 * @example
 * ```ts
 * const result = loadPolicyBundle(bundle, {
 *   signingKey: process.env['POLICY_SIGNING_KEY'],
 *   managed: true,
 *   provenanceSource: 'local-file',
 * });
 * if (!result.ok) {
 *   throw new Error(`Policy load failed: ${result.error}`);
 * }
 * ```
 */
export function loadPolicyBundle(bundle, options = {}) {
    const { signingKey, managed = false, provenanceSource = 'inline', throwOnRejection = false, } = options;
    const signingEnabled = isFeatureGateEnabled(options.featureFlags, 'policy-signing');
    const baseProv = {
        policyBundleId: bundle.bundleId,
        signatureStatus: 'skipped',
        provenanceSource,
        issuedAt: bundle.issuedAt,
        issuer: bundle.issuer,
    };
    // ── Signature validation ────────────────────────────────────────────────────
    let signatureStatus;
    if (!signingEnabled) {
        signatureStatus = 'skipped';
    }
    else if (!signingKey) {
        // No key supplied — skip verification (permitted in non-managed mode)
        signatureStatus = 'skipped';
    }
    else if (!bundle.signature) {
        // Key supplied but bundle has no signature — unsigned in non-managed, rejected in managed
        signatureStatus = 'unsigned';
    }
    else {
        const result = verifyBundle(bundle, signingKey);
        signatureStatus = result.status;
    }
    const provenance = { ...baseProv, signatureStatus };
    // ── Managed-mode enforcement ────────────────────────────────────────────────
    if (managed && signingEnabled) {
        const rejected = signatureStatus === 'invalid'
            || signatureStatus === 'missing'
            || signatureStatus === 'unsigned'
            || signatureStatus === 'skipped'; // managed mode requires a key + valid sig
        if (rejected) {
            const msg = `Managed-mode policy rejection: bundle "${bundle.bundleId}" ` +
                `has signatureStatus="${signatureStatus}". ` +
                `Only bundles with a valid HMAC-SHA256 signature are accepted in managed mode.`;
            if (throwOnRejection) {
                throw new PolicySignatureError(bundle.bundleId, signatureStatus, msg);
            }
            return { ok: false, provenance, error: msg };
        }
    }
    // ── Payload extraction ───────────────────────────────────────────────────────
    const payload = bundle.payload;
    return {
        ok: true,
        rules: payload.rules,
        provenance,
    };
}
/**
 * createUnsignedBundle — Convenience helper for non-managed/test usage.
 *
 * Creates a bare `SignedPolicyBundle` without a signature. The resulting
 * bundle will be accepted in non-managed mode with status `'unsigned'`.
 *
 * @param bundleId — Unique identifier for this bundle.
 * @param payload  — The policy payload.
 */
export function createUnsignedBundle(bundleId, payload) {
    return {
        bundleId,
        issuedAt: new Date().toISOString(),
        payload,
        // No signature field
    };
}
