"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.TestingEngine = void 0;
const interpolation_1 = require("./interpolation");
const normalize_steps_1 = require("./normalize-steps");
const timeout_1 = require("./timeout");
class TestingEngine {
    constructor(registry, defaults) {
        this.registry = registry;
        this.defaults = defaults;
    }
    async runScenario(scenario, ctxBase) {
        const retries = scenario.retries ?? this.defaults?.retries ?? 0;
        const maxAttempts = Math.max(0, retries) + 1;
        const scenarioTimeoutMs = scenario.timeoutMs ?? this.defaults?.timeoutMs;
        for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
            const ctx = {
                ...ctxBase,
                scenarioId: scenario.id,
                scenarioType: scenario.type,
                params: scenario.params ?? {},
            };
            const reporter = ctx.reporter;
            const scenarioStart = Date.now();
            let scenarioError;
            reporter.onScenarioStart(scenario);
            try {
                const execute = async () => {
                    for (const block of scenario.steps) {
                        const normalized = (0, normalize_steps_1.normalizeBlock)(block);
                        await this.runBlock(normalized.phase, normalized.steps, ctx);
                    }
                };
                if (scenarioTimeoutMs !== undefined) {
                    await (0, timeout_1.withTimeout)(execute(), scenarioTimeoutMs, `Scenario timeout after ${scenarioTimeoutMs}ms: "${scenario.id}"`);
                }
                else {
                    await execute();
                }
            }
            catch (err) {
                scenarioError = err;
            }
            finally {
                const durationMs = Date.now() - scenarioStart;
                reporter.onScenarioEnd(scenario, {
                    ok: !scenarioError,
                    error: scenarioError,
                    durationMs,
                });
            }
            if (!scenarioError) {
                return;
            }
            if (attempt >= maxAttempts) {
                throw scenarioError;
            }
        }
    }
    async runBlock(phase, steps, ctx) {
        for (const step of steps) {
            const reporter = ctx.reporter;
            const stepStart = Date.now();
            let stepError;
            let resolvedStep;
            reporter.onStepStart({ scenarioId: ctx.scenarioId, phase, step });
            try {
                resolvedStep = (0, interpolation_1.interpolateDeep)(step, ctx);
                const handler = this.registry.get(resolvedStep.op);
                const run = handler(ctx, resolvedStep);
                const result = resolvedStep.timeoutMs !== undefined
                    ? await (0, timeout_1.withTimeout)(run, resolvedStep.timeoutMs, `Step timeout after ${resolvedStep.timeoutMs}ms: "${resolvedStep.op}"`)
                    : await run;
                if (resolvedStep.saveAs) {
                    ctx.resources.set(resolvedStep.saveAs, result);
                }
            }
            catch (err) {
                stepError = err;
                throw err;
            }
            finally {
                const durationMs = Date.now() - stepStart;
                reporter.onStepEnd({
                    scenarioId: ctx.scenarioId,
                    phase,
                    step: resolvedStep ?? step,
                    ok: !stepError,
                    error: stepError,
                    durationMs,
                });
            }
        }
    }
}
exports.TestingEngine = TestingEngine;
//# sourceMappingURL=testing-engine.js.map