"use strict";
/**
 * Runtime transform utility for applying strategy transforms per-turn.
 *
 * This module enables multi-turn attack providers (Hydra, Crescendo, etc.)
 * to apply layer transforms (audio, base64, etc.) to each turn's prompt
 * before sending to the target.
 *
 * It reuses existing strategy implementations, avoiding code duplication.
 */
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.applyRuntimeTransforms = applyRuntimeTransforms;
const logger_1 = __importDefault(require("../../logger"));
/**
 * Applies strategy transforms to a prompt at runtime (per-turn).
 * This is used by multi-turn attack providers to transform each turn's
 * output before sending to the target.
 *
 * @param prompt - The text prompt to transform
 * @param injectVar - The variable name used for injection (e.g., 'query')
 * @param layerConfigs - Array of layer configurations to apply in order
 * @param strategies - The loaded strategies array (to avoid circular imports)
 * @returns TransformResult with transformed prompt and audio metadata
 *
 * @example
 * ```typescript
 * // In Hydra provider:
 * const result = await applyRuntimeTransforms(
 *   attackPrompt,
 *   'query',
 *   ['audio', 'base64'],
 *   Strategies
 * );
 * // result.prompt = transformed, result.audio = { data, format } if audio
 * ```
 */
async function applyRuntimeTransforms(prompt, injectVar, layerConfigs, strategies) {
    const originalPrompt = prompt;
    if (!layerConfigs?.length) {
        return { prompt, originalPrompt };
    }
    logger_1.default.debug(`[RuntimeTransform] Applying ${layerConfigs.length} transforms to prompt`);
    // Create a pseudo test case to run through existing strategy actions
    // This reuses the exact same code path as pre-eval transforms
    let testCase = {
        vars: { [injectVar]: prompt },
        assert: [],
        metadata: { pluginId: 'runtime-transform' },
    };
    let audioApplied = false;
    let imageApplied = false;
    for (const layer of layerConfigs) {
        const layerId = typeof layer === 'string' ? layer : layer.id;
        const layerConfig = typeof layer === 'string' ? {} : layer.config || {};
        // Find the strategy by ID
        const strategy = strategies.find((s) => s.id === layerId);
        if (!strategy) {
            logger_1.default.warn(`[RuntimeTransform] Unknown layer strategy: ${layerId}, skipping`);
            continue;
        }
        logger_1.default.debug(`[RuntimeTransform] Applying layer: ${layerId}`);
        // Track which media layers were applied
        if (layerId === 'audio') {
            audioApplied = true;
        }
        else if (layerId === 'image') {
            imageApplied = true;
        }
        try {
            // Call existing strategy action - this REUSES all existing implementation
            // The strategy expects an array of test cases and returns transformed test cases
            const result = await strategy.action([testCase], injectVar, layerConfig);
            const transformed = result[0];
            if (transformed) {
                // Preserve the pluginId while updating the test case
                testCase = {
                    ...transformed,
                    metadata: {
                        ...transformed.metadata,
                        pluginId: testCase.metadata.pluginId,
                    },
                };
            }
            else {
                logger_1.default.warn(`[RuntimeTransform] Layer ${layerId} returned no test cases`);
            }
        }
        catch (error) {
            const errorMsg = `Transform ${layerId} failed: ${error.message || 'Unknown error'}`;
            logger_1.default.error(`[RuntimeTransform] ${errorMsg}: ${JSON.stringify(error)}`);
            // Return error so caller can skip the turn
            return {
                prompt: originalPrompt,
                originalPrompt,
                error: errorMsg,
            };
        }
    }
    // Extract the transformed prompt
    const transformedPrompt = String(testCase.vars?.[injectVar] ?? prompt);
    logger_1.default.debug(`[RuntimeTransform] Transform complete: ${JSON.stringify({
        originalLength: prompt.length,
        resultLength: transformedPrompt.length,
        layersApplied: layerConfigs.length,
        audioApplied,
        imageApplied,
    })}`);
    // Build result with media metadata
    const result = {
        prompt: transformedPrompt,
        originalPrompt,
    };
    // If audio layer was applied, extract audio data
    if (audioApplied && transformedPrompt !== originalPrompt) {
        // Check if it's a data URL or raw base64
        const dataUrlMatch = transformedPrompt.match(/^data:audio\/([^;]+);base64,(.+)$/);
        if (dataUrlMatch) {
            // Already a data URL - extract format and raw data
            result.audio = {
                data: dataUrlMatch[2], // Just the base64 part
                format: dataUrlMatch[1],
            };
        }
        else {
            // Raw base64 - assume MP3 format (Google Cloud TTS default)
            result.audio = {
                data: transformedPrompt,
                format: 'mp3',
            };
        }
    }
    // If image layer was applied, extract image data
    if (imageApplied && transformedPrompt !== originalPrompt) {
        // Check if it's a data URL or raw base64
        const dataUrlMatch = transformedPrompt.match(/^data:image\/([^;]+);base64,(.+)$/);
        if (dataUrlMatch) {
            // Already a data URL - extract format and raw data
            result.image = {
                data: dataUrlMatch[2], // Just the base64 part
                format: dataUrlMatch[1],
            };
        }
        else {
            // Raw base64 - assume PNG format
            result.image = {
                data: transformedPrompt,
                format: 'png',
            };
        }
    }
    return result;
}
//# sourceMappingURL=runtimeTransform.js.map