/**
 * Retry actions for failed tasks.
 *
 * Uses the existing worktree (clone) for conversation and direct re-execution.
 * The worktree is preserved after initial execution, so no clone creation is needed.
 */
import * as fs from 'node:fs';
import { TaskRunner, resolveTaskWorkflowValue } from '../../../infra/task/index.js';
import { loadWorkflowByIdentifier, resolveWorkflowConfigValue, getWorkflowDescription } from '../../../infra/config/index.js';
import { selectOptionWithDefault } from '../../../shared/prompt/index.js';
import { info, header, blankLine, status, warn } from '../../../shared/ui/index.js';
import { createLogger } from '../../../shared/utils/index.js';
import { readRunMetaBySlug } from '../../../core/workflow/run/run-meta.js';
import { findRunForTask, loadRunSessionContext, getRunPaths, formatRunSessionForPrompt, runTaskRetryMode, findPreviousOrderContent, } from '../../interactive/index.js';
import { executeAndCompleteTask } from '../execute/taskExecution.js';
import { appendRetryNote, buildAutoRequeueNote, DEPRECATED_PROVIDER_CONFIG_WARNING, hasDeprecatedProviderConfig, selectWorkflowWithOptionalReuse, } from './requeueHelpers.js';
import { prepareTaskForExecution } from './prepareTaskForExecution.js';
import { cleanupPreparedRetryTaskSpec, prepareRetryTaskSpecWithAttachments, } from './retryTaskSpecAttachments.js';
import { sanitizeTerminalText } from '../../../shared/utils/text.js';
import { workflowEntryMatchesWorkflow } from '../../../core/workflow/workflow-reference.js';
const log = createLogger('list-tasks');
function displayFailureInfo(task, failure) {
    header(`Failed Task: ${sanitizeTerminalText(task.name)}`);
    info(`  Failed at: ${task.createdAt}`);
    blankLine();
    if (failure.step) {
        status('Failed at', sanitizeTerminalText(failure.step), 'red');
    }
    status('Error', sanitizeTerminalText(failure.error), 'red');
    if (failure.last_message) {
        status('Last message', sanitizeTerminalText(failure.last_message));
    }
    blankLine();
}
async function selectStartStep(workflowConfig, defaultStep) {
    const steps = workflowConfig.steps.map((step) => step.name);
    const defaultIdx = defaultStep
        ? steps.indexOf(defaultStep)
        : 0;
    const effectiveDefault = defaultIdx >= 0 ? steps[defaultIdx] : steps[0];
    const options = steps.map((name) => ({
        label: sanitizeTerminalText(name),
        value: name,
        description: name === workflowConfig.initialStep ? 'Initial step' : undefined,
    }));
    return await selectOptionWithDefault('Start from step:', options, effectiveDefault ?? steps[0]);
}
function buildRetryFailureInfo(task, failure) {
    return {
        taskName: task.name,
        taskContent: task.content,
        createdAt: task.createdAt,
        failedStep: failure.step ?? '',
        error: failure.error,
        lastMessage: failure.last_message ?? '',
        retryNote: task.data?.retry_note ?? '',
    };
}
function buildRetryRunInfo(runsBaseDir, slug) {
    const paths = getRunPaths(runsBaseDir, slug);
    const sessionContext = loadRunSessionContext(runsBaseDir, slug);
    const formatted = formatRunSessionForPrompt(sessionContext);
    return {
        logsDir: paths.logsDir,
        reportsDir: paths.reportsDir,
        task: formatted.runTask,
        workflow: formatted.runWorkflow,
        status: formatted.runStatus,
        stepLogs: formatted.runStepLogs,
        reports: formatted.runReports,
    };
}
function resolveRetryRunSlug(task, worktreePath) {
    return task.runSlug ?? findRunForTask(worktreePath, task.content);
}
function readRetryRunMeta(worktreePath, runSlug) {
    if (!runSlug) {
        return null;
    }
    return readRunMetaBySlug(worktreePath, runSlug, (warningMessage) => {
        warn(warningMessage);
    });
}
function resolveRetryResumePoint(task, runMeta) {
    const metaResumePoint = runMeta?.resumePoint;
    if (metaResumePoint) {
        return metaResumePoint;
    }
    return task.data?.resume_point;
}
function resolveRetryDefaultStep(workflowConfig, failure, resumePoint) {
    const rootEntry = resumePoint?.stack[0];
    if (rootEntry
        && workflowEntryMatchesWorkflow(rootEntry, workflowConfig)
        && workflowConfig.steps.some((step) => step.name === rootEntry.step)) {
        return rootEntry.step;
    }
    return failure.step ?? null;
}
function resolveFailureStepForRequeueNote(failure, runMeta, resumePoint) {
    const failureStep = failure.step?.trim();
    if (failureStep) {
        return failureStep;
    }
    const currentStep = runMeta?.currentStep?.trim();
    if (currentStep) {
        return currentStep;
    }
    const resumeStep = resumePoint?.stack[0]?.step.trim();
    if (resumeStep) {
        return resumeStep;
    }
    return undefined;
}
function requireFailedStepForRequeueNote(failedStep) {
    if (!failedStep) {
        throw new Error('Failed task step name could not be resolved for auto requeue note.');
    }
    return failedStep;
}
function shouldResumeFromSelectedStep(workflowConfig, selectedStep, resumePoint) {
    const rootEntry = resumePoint?.stack[0];
    if (!rootEntry) {
        return false;
    }
    return workflowEntryMatchesWorkflow(rootEntry, workflowConfig)
        && rootEntry.step === selectedStep
        && workflowConfig.steps.some((step) => step.name === rootEntry.step);
}
function resolveWorktreePath(task) {
    if (!task.worktreePath) {
        throw new Error(`Worktree path is not set for task: ${task.name}`);
    }
    if (!fs.existsSync(task.worktreePath)) {
        throw new Error(`Worktree directory does not exist: ${task.worktreePath}`);
    }
    return task.worktreePath;
}
function resolveSelectedWorkflowOverride(previousWorkflow, selectedWorkflow) {
    return previousWorkflow === selectedWorkflow ? undefined : selectedWorkflow;
}
function requireFailedTaskFailure(task) {
    if (!task.failure) {
        throw new Error(`Failed task "${sanitizeTerminalText(task.name)}" is missing failure details.`);
    }
    if (task.failure.error.trim() === '') {
        throw new Error(`Failed task "${sanitizeTerminalText(task.name)}" has empty failure.error.`);
    }
    return task.failure;
}
async function prepareFailedTaskRetrySelection(task, projectDir) {
    if (task.kind !== 'failed') {
        throw new Error(`Failed task retry action requires failed task. received: ${task.kind}`);
    }
    const failure = requireFailedTaskFailure(task);
    const worktreePath = resolveWorktreePath(task);
    displayFailureInfo(task, failure);
    const matchedSlug = resolveRetryRunSlug(task, worktreePath);
    const runMeta = readRetryRunMeta(worktreePath, matchedSlug);
    const previousWorkflow = task.data
        ? resolveTaskWorkflowValue(task.data)
        : undefined;
    const selectedWorkflow = await selectWorkflowWithOptionalReuse(projectDir, previousWorkflow, worktreePath);
    if (!selectedWorkflow) {
        info('Cancelled');
        return null;
    }
    const workflowConfig = loadWorkflowByIdentifier(selectedWorkflow, projectDir, { lookupCwd: worktreePath });
    if (!workflowConfig) {
        throw new Error(`Workflow "${sanitizeTerminalText(selectedWorkflow)}" not found after selection.`);
    }
    const resumePoint = resolveRetryResumePoint(task, runMeta);
    const failedStep = resolveFailureStepForRequeueNote(failure, runMeta, resumePoint);
    const selectedStep = await selectStartStep(workflowConfig, resolveRetryDefaultStep(workflowConfig, failure, resumePoint));
    if (selectedStep === null) {
        return null;
    }
    const previousOrderContent = findPreviousOrderContent(worktreePath, matchedSlug);
    if (hasDeprecatedProviderConfig(previousOrderContent)) {
        warn(DEPRECATED_PROVIDER_CONFIG_WARNING);
    }
    const startStep = selectedStep !== workflowConfig.initialStep
        ? selectedStep
        : undefined;
    const selectedResumePoint = shouldResumeFromSelectedStep(workflowConfig, selectedStep, resumePoint)
        ? resumePoint
        : undefined;
    const selectedWorkflowOverride = resolveSelectedWorkflowOverride(previousWorkflow, selectedWorkflow);
    return {
        worktreePath,
        failure,
        failedStep,
        matchedSlug,
        runMeta,
        selectedWorkflow,
        previousOrderContent,
        startStep,
        selectedResumePoint,
        selectedWorkflowOverride,
    };
}
export async function requeueFailedTask(task, projectDir) {
    const selection = await prepareFailedTaskRetrySelection(task, projectDir);
    if (!selection) {
        return false;
    }
    const retryNote = appendRetryNote(task.data?.retry_note, buildAutoRequeueNote({
        ...selection.failure,
        step: requireFailedStepForRequeueNote(selection.failedStep),
    }));
    const runner = new TaskRunner(projectDir);
    runner.requeueTask(task.name, ['failed'], selection.startStep, retryNote, selection.selectedResumePoint, selection.selectedWorkflowOverride);
    info(`Task "${sanitizeTerminalText(task.name)}" has been requeued.`);
    return true;
}
/**
 * Retry a failed task.
 *
 * Runs the retry conversation in the existing worktree, then directly
 * re-executes the task there (auto-commit + push + status update).
 *
 * @returns true if task was re-executed successfully, false if cancelled or failed
 */
export async function retryFailedTask(task, projectDir) {
    const selection = await prepareFailedTaskRetrySelection(task, projectDir);
    if (!selection) {
        return false;
    }
    const runInfo = selection.matchedSlug && selection.runMeta
        ? buildRetryRunInfo(selection.worktreePath, selection.matchedSlug)
        : null;
    const previewCount = resolveWorkflowConfigValue(projectDir, 'interactivePreviewSteps');
    const workflowDesc = getWorkflowDescription(selection.selectedWorkflow, projectDir, previewCount, selection.worktreePath);
    const workflowContext = {
        name: workflowDesc.name,
        description: workflowDesc.description,
        workflowStructure: workflowDesc.workflowStructure,
        stepPreviews: workflowDesc.stepPreviews,
    };
    blankLine();
    const retryContext = {
        failure: buildRetryFailureInfo(task, selection.failure),
        subject: {
            kind: 'branch',
            value: task.branch ?? task.name,
        },
        workflowContext,
        run: runInfo,
        previousOrderContent: selection.previousOrderContent,
    };
    const retryResult = await runTaskRetryMode(selection.worktreePath, retryContext);
    if (retryResult.action === 'cancel') {
        return false;
    }
    const retryNote = appendRetryNote(task.data?.retry_note, retryResult.task);
    const preparedSpec = prepareRetryTaskSpecWithAttachments(projectDir, task.content, retryNote, retryResult.attachments, task.taskDir);
    const taskDir = preparedSpec?.taskDirRelative;
    const runner = new TaskRunner(projectDir);
    if (retryResult.action === 'save_task') {
        try {
            runner.requeueTask(task.name, ['failed'], selection.startStep, retryNote, selection.selectedResumePoint, selection.selectedWorkflowOverride, taskDir);
        }
        catch (error) {
            cleanupPreparedRetryTaskSpec(preparedSpec);
            throw error;
        }
        info(`Task "${sanitizeTerminalText(task.name)}" has been requeued.`);
        return true;
    }
    let taskInfo;
    try {
        taskInfo = runner.startReExecution(task.name, ['failed'], selection.startStep, retryNote, selection.selectedResumePoint, selection.selectedWorkflowOverride, taskDir);
    }
    catch (error) {
        cleanupPreparedRetryTaskSpec(preparedSpec);
        throw error;
    }
    const taskForExecution = prepareTaskForExecution(taskInfo, selection.selectedWorkflow);
    log.info('Starting re-execution of failed task', {
        name: task.name,
        worktreePath: selection.worktreePath,
        startStep: selection.startStep,
    });
    return executeAndCompleteTask(taskForExecution, runner, projectDir);
}
//# sourceMappingURL=taskRetryActions.js.map