/**
 * `runCreate()` — high-level orchestrator for creating a SpecVerse spec
 * from a natural-language request.
 *
 * Public API for `spv ai create` (TODO #21 phase 4) and the eval harness.
 * One call, full pipeline:
 *
 *   1. write input/requirements.md (the NL request preserved alongside the run)
 *   2. assemble + call the create prompt → specs/draft.specly + main.specly
 *   3. (optional) verify-create pass → specs/verified.specly + main.specly
 *   4. spv validate; record validate.txt
 *   5. (optional) materialize project/ via spv init + spv realize
 *
 * Caller responsibilities:
 *   - Decide where outputs go via `outputDir`
 *   - Score against expected_* fields if running eval
 *   - Compose final README + manifest with caseMeta + score
 */
import { writeFileSync, existsSync, rmSync, cpSync } from 'fs';
import { join } from 'path';
import { execSync, spawnSync } from 'child_process';
import { inputDir, promptsDir, llmOutputDir, specsDir, ensureSubdirs, writeStageSpec, gateVerifyAgainstDraft, writeProjectReadme, writeManifest, writeReadme, } from './run-paths.js';
import { runPrompt, extractSpec, promptTag } from './prompt-runner.js';
import { resolveModel, resolveProviderId } from './model-resolver.js';
function runValidate(specPath) {
    const result = spawnSync('spv', ['validate', specPath], {
        encoding: 'utf8',
        timeout: 30_000,
    });
    if (result.status === 0)
        return { ok: true, errors: '' };
    const errors = (result.stderr || '') + (result.stdout || '');
    return { ok: false, errors: errors.trim() };
}
async function realizeProject(outputDir) {
    const projectPath = join(outputDir, 'project');
    const mainSpec = join(specsDir(outputDir), 'main.specly');
    if (!existsSync(mainSpec))
        return { ok: false, error: 'specs/main.specly missing' };
    if (existsSync(projectPath))
        rmSync(projectPath, { recursive: true, force: true });
    // See analyse-runner.ts realizeProject — SPECVERSE_USER_CWD must be
    // overridden to match each execSync's cwd, otherwise generated CLI handlers
    // resolve paths against the inherited (parent) value and ./project/ lands
    // outside the run dir.
    try {
        execSync(`spv init project --template default`, {
            cwd: outputDir, stdio: 'pipe', timeout: 60_000,
            env: { ...process.env, SPECVERSE_USER_CWD: outputDir },
        });
    }
    catch (err) {
        return { ok: false, error: `spv init failed: ${err.message}` };
    }
    cpSync(mainSpec, join(projectPath, 'specs', 'main.specly'));
    try {
        execSync(`spv realize all specs/main.specly`, {
            cwd: projectPath, stdio: 'pipe', timeout: 15 * 60_000,
            env: { ...process.env, SPECVERSE_USER_CWD: projectPath },
        });
    }
    catch (err) {
        const e = err;
        const stderr = e.stderr ? String(e.stderr).slice(0, 500) : '';
        return { ok: false, error: `spv realize failed: ${stderr || e.message}` };
    }
    return { ok: true };
}
/**
 * Execute the full create pipeline: NL request → draft spec → optional verify
 * → validate → optional realize. Writes the structured run output. Returns
 * paths + status; caller composes scoring / README finalization separately.
 */
export async function runCreate(opts) {
    const startedAt = new Date().toISOString();
    const t0 = Date.now();
    ensureSubdirs(opts.outputDir);
    // 0. Persist the NL request as input/requirements.md so the run dir is
    // self-contained and the request is reviewable later.
    const reqLines = [`# ${opts.caseName ?? 'spec'} — natural-language request`, ''];
    if (opts.scale)
        reqLines.push(`**Scale:** ${opts.scale}`);
    if (opts.preferredTech)
        reqLines.push(`**Preferred tech:** ${opts.preferredTech}`);
    reqLines.push('', '---', '', opts.requirements);
    writeFileSync(join(inputDir(opts.outputDir), 'requirements.md'), reqLines.join('\n'));
    // 1. Resolve model (caller may have pre-built one for session caching).
    const model = opts.model ?? resolveModel({
        timeout: opts.timeoutMs ?? 10 * 60_000,
        sessionId: opts.sessionId,
    });
    // 2. Create pass.
    const draftResult = await runPrompt({
        operation: 'create',
        values: {
            requirements: opts.requirements,
            scale: opts.scale,
            preferredTech: opts.preferredTech,
        },
        model,
        timeoutMs: opts.timeoutMs ?? 10 * 60_000,
        onAssembled: ({ system, user }) => {
            writeFileSync(join(promptsDir(opts.outputDir), 'create-system.txt'), system);
            writeFileSync(join(promptsDir(opts.outputDir), 'create-user.txt'), user);
        },
    });
    const draftSpec = extractSpec(draftResult.text);
    const draftSpecPath = writeStageSpec(opts.outputDir, 'draft', draftSpec);
    writeFileSync(join(llmOutputDir(opts.outputDir), 'create.txt'), draftResult.text);
    // 3. Verify pass (optional).
    let finalSpec = draftSpec;
    let finalStage = 'draft';
    let verifyDurationMs = 0;
    let gatedValidation = null;
    if (opts.verify) {
        try {
            const verifyResult = await runPrompt({
                operation: 'verify-create',
                values: {
                    requirements: opts.requirements,
                    draft: draftSpec,
                },
                model,
                timeoutMs: opts.timeoutMs ?? 10 * 60_000,
                onAssembled: ({ system, user }) => {
                    writeFileSync(join(promptsDir(opts.outputDir), 'verify-system.txt'), system);
                    writeFileSync(join(promptsDir(opts.outputDir), 'verify-user.txt'), user);
                },
            });
            const verifiedSpec = extractSpec(verifyResult.text);
            writeFileSync(join(llmOutputDir(opts.outputDir), 'verify.txt'), verifyResult.text);
            verifyDurationMs = verifyResult.duration_ms;
            writeStageSpec(opts.outputDir, 'verified', verifiedSpec);
            // Gate: keep the verify output only if it validates no worse than the draft.
            const gate = gateVerifyAgainstDraft(opts.outputDir, draftSpec, verifiedSpec, runValidate);
            finalSpec = gate.finalSpec;
            finalStage = gate.finalStage;
            gatedValidation = gate.validation;
            if (gate.reverted) {
                console.warn('[verify-create] verify output failed validation; kept the valid draft.');
            }
        }
        catch { /* fallback to draft */ }
    }
    // 4. Final validation (reuse the gate's result when verify ran — no re-spawn).
    const finalSpecPath = join(specsDir(opts.outputDir), 'main.specly');
    const validateFinal = gatedValidation ?? runValidate(finalSpecPath);
    writeFileSync(join(opts.outputDir, 'validate.txt'), validateFinal.ok ? 'OK' : validateFinal.errors);
    // 5. Optional realize.
    let realize = null;
    if (opts.realize && validateFinal.ok) {
        realize = await realizeProject(opts.outputDir);
    }
    writeProjectReadme(opts.outputDir, {
        caseName: opts.caseName ?? 'spec',
        realized: realize,
    });
    // 6. Manifest.
    const wallTimeMs = Date.now() - t0;
    const manifest = {
        kind: 'create',
        case: opts.caseName ?? 'spec',
        runFolder: opts.outputDir.split('/').pop() ?? '',
        startedAt,
        endedAt: new Date().toISOString(),
        wallTimeMs,
        provider: opts.providerId ?? resolveProviderId(),
        model: opts.modelId ?? 'default',
        prompt: opts.prompt ?? 'create-v9',
        promptTag: promptTag(),
        workflow: opts.verify ? 'create → verify-create' : 'create',
        verify: !!opts.verify,
        realize: opts.realize ? (realize?.ok ? 'OK' : realize?.error || 'skipped') : false,
        validateFinal: validateFinal.ok ? 'OK' : validateFinal.errors.slice(0, 200),
        experimentRunId: opts.experimentRunId ?? null,
    };
    writeManifest(opts.outputDir, manifest);
    // Default README — caller (harness) may overwrite after scoring.
    writeReadme(opts.outputDir, {
        caseEntry: opts.caseMeta ?? { name: opts.caseName ?? 'spec', nl_request: opts.requirements },
        manifest,
        runFolder: manifest.runFolder,
        kind: 'create',
    });
    return {
        outputDir: opts.outputDir,
        draftSpecPath,
        finalSpecPath,
        finalStage,
        validateFinal,
        realize,
        draftDurationMs: draftResult.duration_ms,
        verifyDurationMs,
        wallTimeMs,
        manifest,
    };
}
//# sourceMappingURL=create-runner.js.map