"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    var desc = Object.getOwnPropertyDescriptor(m, k);
    if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
      desc = { enumerable: true, get: function() { return m[k]; } };
    }
    Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
    if (k2 === undefined) k2 = k;
    o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
    Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
    o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
    var ownKeys = function(o) {
        ownKeys = Object.getOwnPropertyNames || function (o) {
            var ar = [];
            for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
            return ar;
        };
        return ownKeys(o);
    };
    return function (mod) {
        if (mod && mod.__esModule) return mod;
        var result = {};
        if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
        __setModuleDefault(result, mod);
        return result;
    };
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.listStepsCommand = void 0;
exports.listSteps = listSteps;
const fs = __importStar(require("fs"));
const debugFailWrapper_1 = require("../util/debugFailWrapper");
const constants_1 = require("../../../constants");
const constants_2 = require("@mablhq-internal/runtime/core/constants");
const DebugSession_1 = require("@mablhq-internal/runtime/debug/DebugSession");
const expandSteps_1 = require("@mablhq-internal/runtime/debug/expandSteps");
const CliTestGenerationState_1 = require("@mablhq-internal/runtime/trainer/cli/CliTestGenerationState");
const buildDebugSessionState_1 = require("@mablhq-internal/runtime/debug/buildDebugSessionState");
const stepIndexUtils_1 = require("@mablhq-internal/runtime/trainer/sharedUtils/stepIndexUtils");
const describe_1 = require("../../../commandUtil/describe");
const debugCommands_1 = require("../../debugCommands");
const DEFAULT_AROUND_CONTEXT = 5;
exports.listStepsCommand = (0, debugCommands_1.defineLeaf)({
    command: `list-steps <${constants_1.CommandArgSessionId}>`,
    describe: 'List the steps in the debug session with their execution status. Output is YAML by default; pass --output json for the machine-readable form. EvaluateFlow / StepGroup children are expanded inline. Each entry carries a 1-based dot-notation `position` (e.g. "3.2") that `set-current-step` accepts.',
    builder: (yargs) => {
        yargs
            .positional(constants_1.CommandArgSessionId, {
            type: 'string',
            describe: 'Debug session ID',
            demandOption: true,
        })
            .option(constants_1.CommandArgFilter, {
            describe: 'Case-insensitive substring match against the step description. Use to grep for "Application", "login", etc. without piping through grep.',
            type: 'string',
        })
            .option(constants_1.CommandArgAround, {
            describe: 'Show only the window of steps around the given step. Accepts a step id OR a 1-based position ("3", "4.1"). Use --context N to widen (default 5 before / 5 after).',
            type: 'string',
        })
            .option(constants_1.CommandArgFrom, {
            describe: 'Show steps starting at the given step. Accepts a step id OR a 1-based position ("3", "4.1"). Combine with --limit to bound the window.',
            type: 'string',
        })
            .option(constants_1.CommandArgLimitOutput, {
            describe: 'Cap the number of steps in the output. Combine with --from for a forward window.',
            type: 'number',
        })
            .option(constants_1.CommandArgContext, {
            describe: `Window size for --${constants_1.CommandArgAround}. Default ${DEFAULT_AROUND_CONTEXT} steps before and ${DEFAULT_AROUND_CONTEXT} after the target.`,
            type: 'number',
            default: DEFAULT_AROUND_CONTEXT,
        })
            .option(constants_1.CommandArgOutput, {
            alias: constants_1.CommandArgAliases.OutputType,
            describe: 'Output format (json | yaml).',
            choices: constants_1.DefaultOutputFormatChoices,
            default: constants_2.OutputFormats.Yaml,
            nargs: 1,
        })
            .conflicts(constants_1.CommandArgAround, constants_1.CommandArgFrom);
    },
    handler: (0, debugFailWrapper_1.debugFailWrapper)(listSteps),
});
async function listSteps(parsed) {
    const sessionId = parsed[constants_1.CommandArgSessionId];
    const session = (0, DebugSession_1.readSession)(sessionId);
    const positionLookup = await loadPositionLookup(sessionId, session.snapshot);
    const resultsByStepId = new Map(session.stepResults.map((result) => [result.stepId, result]));
    const expanded = (0, expandSteps_1.expandSessionSteps)(session.snapshot);
    const allSteps = expanded.map((entry, idx) => {
        const { step, parentStepId } = entry;
        const stepId = (0, expandSteps_1.addressableStepId)(entry, idx);
        const result = resultsByStepId.get(stepId);
        const resultStatus = result?.status === 'passed' || result?.status === 'failed'
            ? result.status
            : undefined;
        const status = resultStatus ??
            (idx === session.currentStepIndex ? 'current' : 'pending');
        const positionEntry = positionLookup?.byStepId.get(stepId);
        const position = positionEntry?.position ?? String(idx + 1);
        const path = positionEntry?.path ?? [idx];
        const isCursor = idx === session.currentStepIndex;
        return {
            index: idx,
            id: stepId,
            type: step.getStepName(),
            description: step.getResolvedDescription(),
            status,
            position,
            path,
            isCursor,
            parentStepId,
        };
    });
    const steps = applyListFilters(allSteps, parsed);
    (0, describe_1.outputEntity)(steps, parsed[constants_1.CommandArgOutput]);
}
async function loadPositionLookup(sessionId, snapshot) {
    const stateFile = (0, buildDebugSessionState_1.debugStateFilePath)(sessionId);
    if (!fs.existsSync(stateFile)) {
        return undefined;
    }
    const state = await CliTestGenerationState_1.CliTestGenerationState.load(stateFile);
    const getNestedSteps = (0, stepIndexUtils_1.createNestedStepResolver)((0, buildDebugSessionState_1.buildFlowStepsResolverFromSnapshot)(snapshot, state));
    const byStepId = new Map();
    for (const entry of (0, stepIndexUtils_1.flattenStepsWithPaths)(state.getSteps(), getNestedSteps)) {
        if (entry.step.id) {
            byStepId.set(entry.step.id, { position: entry.index, path: entry.path });
        }
    }
    const cursorStep = (0, stepIndexUtils_1.getStepAtPath)(state.getSteps(), state.getNextStepIndex(), getNestedSteps);
    return { byStepId, cursorStepId: cursorStep?.id };
}
function findStepIndexByReference(steps, reference) {
    return steps.findIndex((s) => s.id === reference || s.position === reference);
}
function applyListFilters(allSteps, parsed) {
    let steps = allSteps;
    const aroundRef = parsed[constants_1.CommandArgAround];
    const fromRef = parsed[constants_1.CommandArgFrom];
    if (aroundRef) {
        const idx = findStepIndexByReference(steps, aroundRef);
        if (idx === -1) {
            throw new Error(`Step not found: ${aroundRef}`);
        }
        const ctx = Math.max(0, parsed[constants_1.CommandArgContext]);
        steps = steps.slice(Math.max(0, idx - ctx), idx + ctx + 1);
    }
    else if (fromRef) {
        const idx = findStepIndexByReference(steps, fromRef);
        if (idx === -1) {
            throw new Error(`Step not found: ${fromRef}`);
        }
        steps = steps.slice(idx);
    }
    const filter = parsed[constants_1.CommandArgFilter]?.toLowerCase();
    if (filter) {
        steps = steps.filter((s) => s.description.toLowerCase().includes(filter));
    }
    const limit = parsed[constants_1.CommandArgLimitOutput];
    if (limit !== undefined && limit >= 0) {
        steps = steps.slice(0, limit);
    }
    return steps;
}
