// tools/convert-to-bdd.ts — Converts checkbox-format spec.md to BDD/Gherkin format (SPEC-481)
import { readFile } from 'node:fs/promises';
import { atomicWriteFile } from '../engine/safety/atomic-write-file.js';
import { glob } from 'glob';
import { extractCriteria } from '../engine/spec-format/lean-spec-generator.js';
import { convertCheckboxToBdd, renderBddScenariosYaml } from '../engine/spec-format/bdd-parser.js';
/**
 * Reads a spec.md file, extracts checkbox criteria, converts them to BDD scenarios,
 * and writes the updated spec.md back to disk.
 */
export async function handleConvertToBdd(input) {
    const { projectPath, specId } = input;
    // Find spec.md by scanning planu/specs/
    const specPath = await findSpecPath(projectPath, specId);
    if (!specPath) {
        return {
            content: [
                {
                    type: 'text',
                    text: `Spec ${specId} not found in ${projectPath}/planu/specs/. Verify the specId and projectPath.`,
                },
            ],
            isError: true,
        };
    }
    const content = await readFile(specPath, 'utf-8');
    // Check if already in BDD format
    if (/^scenarios:/m.test(content)) {
        return {
            content: [
                {
                    type: 'text',
                    text: `**${specId}** is already in BDD format (scenarios: block detected). No changes made.`,
                },
            ],
        };
    }
    // Check that criteria: block exists
    if (!/^criteria:/m.test(content)) {
        return {
            content: [
                {
                    type: 'text',
                    text: `**${specId}** has no criteria: block. Cannot convert. Add checkbox criteria first.`,
                },
            ],
            isError: true,
        };
    }
    // Extract criteria from the description body (after the closing ---)
    const afterFrontmatter = extractBodyAfterFrontmatter(content);
    const criteria = extractCriteria(afterFrontmatter);
    // Also parse inline criteria from the frontmatter YAML directly
    const frontmatterCriteria = parseFrontmatterCriteria(content);
    const allCriteria = frontmatterCriteria.length > 0 ? frontmatterCriteria : criteria;
    const scenarios = convertCheckboxToBdd(allCriteria);
    const scenarioLines = renderBddScenariosYaml(scenarios);
    // Replace criteria: block with scenarios: block in the frontmatter
    const updated = content.replace(/^criteria:[\s\S]*?(?=^[a-z])/m, scenarioLines.join('\n') + '\n');
    await atomicWriteFile(specPath, updated);
    return {
        content: [
            {
                type: 'text',
                text: [
                    `**${specId}** converted to BDD format.`,
                    `- Scenarios created: ${String(scenarios.length)}`,
                    `- File: ${specPath}`,
                ].join('\n'),
            },
        ],
    };
}
/** Find spec.md path for a given specId under planu/specs/. */
async function findSpecPath(projectPath, specId) {
    const patterns = [
        `planu/specs/${specId}-*/spec.md`,
        `planu/specs/${specId}/spec.md`,
        `planu/specs/*/${specId}-*/spec.md`,
    ];
    for (const pattern of patterns) {
        const matches = await glob(pattern, { cwd: projectPath, absolute: true });
        if (matches[0]) {
            return matches[0];
        }
    }
    // Fallback: scan all spec.md files for matching id in frontmatter
    const allSpecs = await glob('planu/specs/**/spec.md', { cwd: projectPath, absolute: true });
    for (const specFile of allSpecs) {
        try {
            const text = await readFile(specFile, 'utf-8');
            if (new RegExp(`^id:\\s*${specId}\\s*$`, 'm').test(text)) {
                return specFile;
            }
        }
        catch {
            // skip unreadable files
        }
    }
    return null;
}
/** Extract the description body after the closing --- of frontmatter. */
function extractBodyAfterFrontmatter(content) {
    const parts = content.split(/^---\s*$/m);
    // parts[0] = before first ---, parts[1] = frontmatter, parts[2] = body
    return parts[2] ?? '';
}
/** Parse criteria items directly from frontmatter YAML text. */
function parseFrontmatterCriteria(content) {
    const result = [];
    // Match criteria block in frontmatter
    const criteriaBlockMatch = /^criteria:\n((?:[ \t].*\n?)*)/m.exec(content);
    if (!criteriaBlockMatch) {
        return result;
    }
    const block = criteriaBlockMatch[1] ?? '';
    const textRegex = /text:\s*"([^"]+)"/g;
    const doneRegex = /done:\s*(true|false)/g;
    let textMatch;
    let doneMatch;
    const texts = [];
    const dones = [];
    textMatch = textRegex.exec(block);
    while (textMatch) {
        texts.push(textMatch[1] ?? '');
        textMatch = textRegex.exec(block);
    }
    doneMatch = doneRegex.exec(block);
    while (doneMatch) {
        dones.push(doneMatch[1] === 'true');
        doneMatch = doneRegex.exec(block);
    }
    for (let i = 0; i < texts.length; i++) {
        const text = texts[i];
        if (text) {
            result.push({ text, done: dones[i] ?? false });
        }
    }
    return result;
}
// Used only for testing — expose the path so tests can resolve the spec location
export { findSpecPath as _findSpecPath };
//# sourceMappingURL=convert-to-bdd.js.map