"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.buildStepTimingMap = buildStepTimingMap;
exports.getAvailableArtifacts = getAvailableArtifacts;
exports.handleSteps = handleSteps;
const mablApiClientFactory_1 = require("@mablhq-internal/runtime/api/mablApiClientFactory");
const mablApi_1 = require("@mablhq-internal/runtime/mablApi");
const describe_1 = require("../../../commandUtil/describe");
const debugUtils_1 = require("./debugUtils");
const DEFAULT_VISIBLE_STATUSES = new Set([
    'failed',
    'recovered',
]);
function buildStepTimingMap(userLogs) {
    const map = new Map();
    for (const entry of userLogs) {
        if (entry.event_type !== 'START_ACTION') {
            continue;
        }
        if (entry.event_time === undefined) {
            continue;
        }
        const stepRunId = (0, debugUtils_1.getStepRunId)(entry);
        if (!stepRunId) {
            continue;
        }
        if (!map.has(stepRunId)) {
            map.set(stepRunId, entry.event_time);
        }
    }
    return map;
}
function getAvailableArtifacts(entry) {
    const artifacts = [];
    if (entry.image_url) {
        artifacts.push('screenshot');
    }
    if (entry.console_log_url) {
        artifacts.push('console_log');
    }
    if (entry.dom_snapshot_url) {
        artifacts.push('dom_snapshot');
    }
    if (entry.har_url) {
        artifacts.push('har');
    }
    if (entry.chrome_trace_url) {
        artifacts.push('chrome_trace');
    }
    return artifacts;
}
async function resolveFlowNames(apiClient, testRun) {
    const nameMap = new Map();
    const flowIds = testRun.journey?.flows;
    if (!flowIds) {
        return nameMap;
    }
    const promises = flowIds.map(async (flowId, index) => {
        try {
            const flow = await apiClient.getFlow(flowId, 'none');
            const name = flow.description || `Flow ${index + 1}`;
            nameMap.set(index, name);
        }
        catch {
            const failFlowId = testRun.failure_summary?.flow_id;
            if (failFlowId === flowId && testRun.failure_summary?.flow_name) {
                nameMap.set(index, testRun.failure_summary.flow_name);
            }
            else {
                nameMap.set(index, `Flow ${index + 1}`);
            }
        }
    });
    await Promise.all(promises);
    return nameMap;
}
async function handleSteps(options) {
    const { testRunId, showAll, outputFormat, apiKey } = options;
    const apiClient = await mablApiClientFactory_1.MablApiClientFactory.createApiClientFromOptionalApiKey(apiKey);
    const testRun = await (0, debugUtils_1.getTestRunOrThrow)(apiClient, testRunId);
    if (testRun.test_type === mablApi_1.TestTypeEnum.Performance ||
        testRun.test_type === mablApi_1.TestTypeEnum.Api ||
        testRun.test_type === mablApi_1.TestTypeEnum.Mobile) {
        const label = testRun.test_type === mablApi_1.TestTypeEnum.Performance
            ? 'performance'
            : testRun.test_type === mablApi_1.TestTypeEnum.Api
                ? 'API'
                : 'mobile';
        throw new Error(`Test run ${testRunId} is a ${label} test run; mabl agent debug steps only supports browser test runs. ` +
            (label === 'performance'
                ? "Performance tests don't expose per-step artifacts — read the run report in the mabl UI."
                : `${label[0].toUpperCase()}${label.slice(1)} test runs aren't debuggable from this surface.`));
    }
    const userLogs = await (0, debugUtils_1.fetchUserLog)(apiClient, testRun);
    const collapsed = (0, debugUtils_1.collapseStepEntries)(userLogs);
    const timingMap = buildStepTimingMap(userLogs);
    const urlMap = (0, debugUtils_1.buildStepUrlMap)(userLogs);
    const flowNames = await resolveFlowNames(apiClient, testRun);
    const isAssertion = !!testRun.failure_summary?.assert_failure;
    const failureSummaryError = testRun.failure_summary?.error ||
        testRun.failure_summary?.assert_failure?.reason;
    const steps = collapsed.map((collapsedStep, idx) => {
        const { canonical, artifactSource, status, recoverySessionId } = collapsedStep;
        const index = idx + 1;
        const stepRunId = collapsedStep.step_run_id;
        const startTime = timingMap.get(stepRunId);
        const endTime = artifactSource.event_time;
        const durationMs = startTime !== undefined && endTime !== undefined
            ? endTime - startTime
            : 0;
        const sourceIndex = canonical.step_source_index_in_flow ?? canonical.step_index;
        const stepEntry = {
            index,
            step_run_id: stepRunId,
            flow: flowNames.get(canonical.flow_index) ||
                `Flow ${canonical.flow_index + 1}`,
            action: canonical.action_code,
            description: canonical.message,
            status,
            duration_ms: durationMs,
            step_number_in_flow: sourceIndex + 1,
            artifacts: getAvailableArtifacts(canonical),
        };
        const perFlowStepId = canonical.step_id_in_flow ?? canonical.step_id;
        if (perFlowStepId) {
            stepEntry.step_id = perFlowStepId;
        }
        if (canonical.step_id_in_journey) {
            stepEntry.step_id_in_test = canonical.step_id_in_journey;
        }
        const urls = urlMap.get(stepRunId);
        if (urls?.url_before) {
            stepEntry.url_before = urls.url_before;
        }
        if (urls?.url_after) {
            stepEntry.url_after = urls.url_after;
        }
        if (status === 'failed' || status === 'recovered') {
            stepEntry.error = {
                type: isAssertion ? 'assertion' : 'exception',
                message: failureSummaryError ?? canonical.message.replace(/^\[ERROR]\s*/, ''),
            };
        }
        if (status === 'recovered' && recoverySessionId) {
            stepEntry.recovery = { session_id: recoverySessionId };
        }
        return stepEntry;
    });
    const totalDuration = testRun.started_time && testRun.completed_time
        ? testRun.completed_time - testRun.started_time
        : steps.reduce((sum, s) => sum + s.duration_ms, 0);
    const visibleSteps = showAll
        ? steps
        : steps.filter((s) => DEFAULT_VISIBLE_STATUSES.has(s.status));
    const hiddenCount = steps.length - visibleSteps.length;
    const trace = {
        test_run_id: testRunId,
        total_steps: steps.length,
        displayed_steps: visibleSteps.length,
        total_duration_ms: totalDuration,
        steps: visibleSteps,
    };
    const hasRecoveredStep = steps.some((s) => s.status === 'recovered');
    if (testRun.failure_summary || hasRecoveredStep) {
        const summary = {};
        const failureFlowId = testRun.failure_summary?.flow_id;
        if (testRun.failure_summary?.flow_name) {
            summary.flow = testRun.failure_summary.flow_name;
        }
        else if (failureFlowId) {
            const flowIds = testRun.journey?.flows ?? [];
            const idx = flowIds.indexOf(failureFlowId);
            const resolved = idx >= 0 ? flowNames.get(idx) : undefined;
            if (resolved) {
                summary.flow = resolved;
            }
        }
        if (testRun.failure_summary?.step_number !== undefined) {
            summary.step_number_in_flow = testRun.failure_summary.step_number;
        }
        if (testRun.failure_summary?.step_id_in_test) {
            summary.step_id_in_test = testRun.failure_summary.step_id_in_test;
        }
        if (failureSummaryError) {
            summary.error = failureSummaryError;
        }
        if (!steps.some((s) => s.status === 'failed')) {
            summary.recovered = true;
        }
        const failingEntry = (summary.step_id_in_test &&
            steps.find((s) => s.step_id_in_test === summary.step_id_in_test)) ||
            steps.find((s) => s.status === 'failed' || s.status === 'recovered');
        if (failingEntry?.step_id) {
            summary.step_id = failingEntry.step_id;
        }
        if (!testRun.failure_summary && failingEntry) {
            if (summary.flow === undefined) {
                summary.flow = failingEntry.flow;
            }
            if (summary.step_number_in_flow === undefined) {
                summary.step_number_in_flow = failingEntry.step_number_in_flow;
            }
            if (summary.step_id_in_test === undefined &&
                failingEntry.step_id_in_test) {
                summary.step_id_in_test = failingEntry.step_id_in_test;
            }
            if (summary.error === undefined && failingEntry.error?.message) {
                summary.error = failingEntry.error.message;
            }
        }
        trace.summary = summary;
    }
    if (steps.length === 0) {
        trace.note = 'No step artifacts available for this test run';
    }
    else if (!showAll && hiddenCount > 0) {
        const visibleStatusList = [...DEFAULT_VISIBLE_STATUSES].join('/');
        trace.note = `Filtered to ${visibleStatusList} steps (${hiddenCount} hidden). Pass --all to see the full trace.`;
    }
    (0, describe_1.outputEntity)(trace, outputFormat);
}
