// engine/batch-script-generator/index.ts — Fan-out batch script generator (SPEC-253)
import { writeFile, chmod } from 'node:fs/promises';
import { join } from 'node:path';
const DEFAULT_PILOT_COUNT = 3;
const DEFAULT_OUTPUT_FORMAT = 'text';
const DEFAULT_PERMISSION_MODE = 'auto';
/**
 * Convert a glob pattern to a find-compatible path expression.
 * e.g. "src/**\/*.ts" -> "-path './src/**\/*.ts'"
 */
function globToFind(pattern) {
    // Normalize: ensure pattern starts with ./
    const normalized = pattern.startsWith('./') ? pattern : `./${pattern}`;
    return normalized;
}
/**
 * Estimate number of files from a glob pattern (heuristic based on specificity).
 * Returns a rough estimate without filesystem access.
 */
function estimateFileCount(fileGlob) {
    if (fileGlob.includes('**')) {
        return 50;
    }
    if (fileGlob.includes('*')) {
        return 10;
    }
    return 1;
}
/**
 * Build the main loop body used in both full and pilot scripts.
 */
function buildLoopBody(taskDescription, allowedTools, outputFormat, permissionMode) {
    const toolsArg = allowedTools.length > 0 ? `    --allowedTools "${allowedTools.join(',')}"` : '';
    const lines = [
        'for file in "${FILES[@]}"; do',
        '  echo -n "[$(($PASS + $FAIL + 1))/$TOTAL] $file ... "',
        `  if claude -p "${taskDescription} for file: $file" \\`,
    ];
    if (toolsArg) {
        lines.push(`${toolsArg} \\`);
    }
    lines.push(`    --permission-mode ${permissionMode} \\`, `    --output-format ${outputFormat}; then`, '    echo "OK" && ((PASS++))', '    echo "OK: $file" >> "$LOG_FILE"', '  else', '    echo "FAIL" && ((FAIL++))', '    echo "FAIL: $file" >> "$LOG_FILE"', '  fi', 'done');
    return lines.join('\n');
}
/**
 * Build the bash script header (shebang, set, variables, file discovery).
 */
function buildScriptHeader(taskDescription, specId, fileGlob, timestamp) {
    const specLabel = specId ? ` — ${specId}` : '';
    const findPattern = globToFind(fileGlob);
    return [
        '#!/usr/bin/env bash',
        `# Planu batch script${specLabel} — Generated ${timestamp}`,
        `# Task: ${taskDescription}`,
        'set -euo pipefail',
        '',
        'SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"',
        `LOG_FILE="$SCRIPT_DIR/batch-results-${timestamp.replace(/[:.]/g, '-')}.log"`,
        'FILES=()',
        `while IFS= read -r -d '' f; do FILES+=("$f"); done < <(find . -path "${findPattern}" -print0 2>/dev/null)`,
        'TOTAL=${#FILES[@]}',
        'PASS=0; FAIL=0',
        '',
        `echo "[$(date -u +%Y-%m-%dT%H:%M:%S)] Starting batch: $TOTAL files" | tee "$LOG_FILE"`,
        '',
    ].join('\n');
}
/**
 * Build the script footer with summary output.
 */
function buildScriptFooter() {
    return [
        '',
        'echo ""',
        'echo "============================="',
        'echo "Done: $PASS ok / $FAIL failed / $TOTAL total"',
        'echo "Log: $LOG_FILE"',
    ].join('\n');
}
/**
 * Build a pilot script with only the first `pilotCount` files hardcoded.
 */
function buildPilotScript(fullScript, pilotFiles) {
    // Replace the FILES=() array population block with hardcoded files
    const hardcoded = pilotFiles.map((f) => `  "${f}"`).join('\n');
    const filesBlock = `FILES=(\n${hardcoded}\n)`;
    return fullScript.replace(/FILES=\(\)\nwhile IFS=.*?done < <\(find[^\n]+\)\n/, `${filesBlock}\n`);
}
/**
 * Generate full and pilot bash scripts for fan-out batch processing with claude -p.
 */
export async function generateBatchScript(input) {
    const { projectPath, taskDescription, fileGlob, specId, allowedTools = [], outputFormat = DEFAULT_OUTPUT_FORMAT, permissionMode = DEFAULT_PERMISSION_MODE, pilotCount = DEFAULT_PILOT_COUNT, dryRun = false, } = input;
    const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, '');
    const safeTimestamp = timestamp.replace(/[:.]/g, '-');
    const specSlug = specId ? `${specId}-` : '';
    const fullScriptName = `batch-${specSlug}${safeTimestamp}.sh`;
    const pilotScriptName = `batch-pilot-${safeTimestamp}.sh`;
    const fullScriptPath = join(projectPath, fullScriptName);
    const pilotScriptPath = join(projectPath, pilotScriptName);
    const header = buildScriptHeader(taskDescription, specId, fileGlob, timestamp);
    const loopBody = buildLoopBody(taskDescription, allowedTools, outputFormat, permissionMode);
    const footer = buildScriptFooter();
    const fullScript = `${header}${loopBody}${footer}\n`;
    // Build pilot script with placeholder files (actual files resolved at runtime)
    // We use placeholders since we don't do filesystem access during generation
    const pilotPlaceholders = Array.from({ length: pilotCount }, (_, i) => `./file-${String(i + 1)}-replace-me`);
    const pilotScript = buildPilotScript(fullScript, pilotPlaceholders);
    const estimatedFiles = estimateFileCount(fileGlob);
    if (!dryRun) {
        await writeFile(fullScriptPath, fullScript, { encoding: 'utf8' });
        await chmod(fullScriptPath, 0o755);
        await writeFile(pilotScriptPath, pilotScript, { encoding: 'utf8' });
        await chmod(pilotScriptPath, 0o755);
    }
    return {
        fullScript,
        pilotScript,
        fullScriptPath,
        pilotScriptPath,
        suggestedPilotCommand: `bash ${pilotScriptName}`,
        scaleUpCommand: `bash ${fullScriptName}`,
        estimatedFiles,
    };
}
//# sourceMappingURL=index.js.map