"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseStructuredTaskPlan = parseStructuredTaskPlan;
const ordered_root_identity_1 = require("./ordered-root-identity");
const planning_artifact_document_1 = require("./planning-artifact-document");
const TASK_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/u;
const ORDERED_ROOT_PATTERN = /^(\d{2})-([a-z0-9]+(?:-[a-z0-9]+)*)$/u;
const BULLET_PATTERN = /^(\s*-\s*)(?:`([^`\n]+)`|\[([^\]\n]+)\]\(([^)\n]+)\)|([a-z0-9]+(?:-[a-z0-9]+)*))(.*)$/u;
const CANONICAL_MAPPING_PATTERN = /^\s*\(\s*(?:`task_id`|task_id)\s*:\s*(?:`([^`]+)`|([^`;\s)]+))\s*;\s*sequence\s*:\s*(?:`([^`]+)`|([^`;)\s]+))\s*\)\s*:/u;
const SEMANTIC_SEQUENCE_PATTERN = /^\s*\(\s*sequence\s*:?\s*(?:`([^`]+)`|([^`)\s]+))\s*\)\s*:/u;
/**
 * Parses the Structured overview Task Plan mapping without treating its ordered
 * directory name as the task's semantic identity.
 */
function parseStructuredTaskPlan(markdownText) {
    const document = (0, planning_artifact_document_1.parsePlanningArtifactDocument)(markdownText);
    const section = document.sections.find((candidate) => candidate.heading === 'Task Plan');
    if (section === undefined) {
        return { present: false, declarations: [], issues: [] };
    }
    const lines = scanContentLines(section.contentText);
    const declarations = [];
    for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
        const line = lines[lineIndex];
        const match = BULLET_PATTERN.exec(line.text);
        if (match === null)
            continue;
        const backtickedValue = match[2];
        const linkLabel = match[3];
        const linkDestination = match[4];
        const plainValue = match[5];
        const referenceValue = (linkDestination ?? backtickedValue ?? plainValue ?? '').trim();
        const referenceLabel = (linkLabel ?? backtickedValue ?? plainValue ?? '').trim();
        const rawText = linkDestination !== undefined
            ? `[${linkLabel ?? ''}](${linkDestination})`
            : backtickedValue !== undefined
                ? `\`${backtickedValue}\``
                : (plainValue ?? '');
        const referenceKind = linkDestination !== undefined
            ? 'markdown-link'
            : backtickedValue !== undefined
                ? 'backticked'
                : 'plain';
        const referenceColumnOffset = line.text.indexOf(rawText, match[1].length);
        const startOffset = section.contentRange.start.offset + line.startOffset + referenceColumnOffset;
        const sourcePosition = offsetToLineColumn(markdownText, startOffset);
        const occurrence = {
            value: referenceValue,
            rawText,
            sectionName: 'Task Plan',
            format: referenceKind === 'backticked' ? 'backticked' : 'plain',
            startOffset,
            endOffset: startOffset + rawText.length,
            line: sourcePosition.line,
            column: sourcePosition.column,
            occurrenceIndex: declarations.length,
        };
        const metadataText = collectMetadataText(lines, lineIndex, match[6] ?? '');
        declarations.push(parseDeclaration({
            referenceValue,
            referenceLabel,
            referenceKind,
            metadataText,
            occurrence,
        }));
    }
    addDuplicateIssues(declarations);
    return {
        present: true,
        declarations,
        issues: declarations.flatMap((declaration) => declaration.issues),
    };
}
function parseDeclaration(input) {
    const canonicalMatch = CANONICAL_MAPPING_PATTERN.exec(input.metadataText);
    if (canonicalMatch !== null) {
        return validateMappedDeclaration({
            ...input,
            syntax: 'canonical-mapping',
            taskId: (canonicalMatch[1] ?? canonicalMatch[2] ?? '').trim(),
            sequenceText: (canonicalMatch[3] ?? canonicalMatch[4] ?? '').trim(),
            declaredOrderedRootName: input.referenceLabel,
        });
    }
    const semanticMatch = SEMANTIC_SEQUENCE_PATTERN.exec(input.metadataText);
    if (semanticMatch !== null) {
        const taskId = input.referenceLabel;
        return validateMappedDeclaration({
            ...input,
            syntax: 'semantic-id-sequence',
            taskId,
            sequenceText: (semanticMatch[1] ?? semanticMatch[2] ?? '').trim(),
            declaredOrderedRootName: null,
        });
    }
    if (/^\s*\([^\n)]*(?:task_id|sequence)/u.test(input.metadataText)) {
        const issue = {
            code: 'malformed-mapping',
            message: `Structured Task Plan entry '${input.referenceLabel}' has malformed identity mapping syntax. Use \`<NN>-<task-id>\` (\`task_id\`: \`<task-id>\`; sequence: <N>): description.`,
        };
        return {
            referenceValue: input.referenceValue,
            referenceKind: input.referenceKind,
            referenceLabel: input.referenceLabel,
            syntax: 'malformed-mapping',
            mappingStatus: 'invalid',
            taskId: null,
            sequence: null,
            sequencePrefix: null,
            orderedTaskRootName: null,
            issues: [issue],
            occurrence: input.occurrence,
        };
    }
    return {
        referenceValue: input.referenceValue,
        referenceKind: input.referenceKind,
        referenceLabel: input.referenceLabel,
        syntax: 'legacy-reference',
        mappingStatus: 'absent',
        taskId: null,
        sequence: null,
        sequencePrefix: null,
        orderedTaskRootName: null,
        issues: [],
        occurrence: input.occurrence,
    };
}
function validateMappedDeclaration(input) {
    const issues = [];
    let sequence = null;
    let sequencePrefix = null;
    let orderedTaskRootName = input.declaredOrderedRootName;
    if (ORDERED_ROOT_PATTERN.test(input.taskId)) {
        let doublePrefixedName = `<NN>-${input.taskId}`;
        try {
            const doublePrefixSequence = /^\d{1,2}$/u.test(input.sequenceText)
                ? Number.parseInt(input.sequenceText, 10)
                : input.sequenceText;
            doublePrefixedName = (0, ordered_root_identity_1.composeOrderedDirectoryName)('structured-task', input.taskId, doublePrefixSequence);
        }
        catch {
            // The sequence-specific diagnostic below owns malformed sequence input.
        }
        issues.push({
            code: 'prefixed-semantic-task-id',
            message: `Structured Task Plan task_id '${input.taskId}' must be an unprefixed semantic identity; work task create applies the sequence prefix separately and would produce double-prefixed directory '${doublePrefixedName}'.`,
        });
    }
    else if (!TASK_ID_PATTERN.test(input.taskId)) {
        issues.push({
            code: 'invalid-task-id',
            message: `Structured Task Plan task_id '${input.taskId}' must be an unprefixed lowercase kebab-case identity.`,
        });
    }
    try {
        const sequenceInput = /^\d{1,2}$/u.test(input.sequenceText)
            ? Number.parseInt(input.sequenceText, 10)
            : input.sequenceText;
        sequencePrefix = (0, ordered_root_identity_1.normalizeImplementationOrder)(sequenceInput, 'Structured Task Plan sequence');
        sequence = Number.parseInt(sequencePrefix, 10);
    }
    catch {
        issues.push({
            code: 'invalid-sequence',
            message: `Structured Task Plan sequence '${input.sequenceText}' must be an integer from 1 through 99.`,
        });
    }
    if (sequence !== null && TASK_ID_PATTERN.test(input.taskId) && !ORDERED_ROOT_PATTERN.test(input.taskId)) {
        const expectedOrderedRootName = (0, ordered_root_identity_1.composeOrderedDirectoryName)('structured-task', input.taskId, sequence);
        if (input.declaredOrderedRootName === null) {
            orderedTaskRootName = expectedOrderedRootName;
        }
        else if (input.declaredOrderedRootName !== expectedOrderedRootName) {
            issues.push({
                code: 'ordered-root-mismatch',
                message: `Structured Task Plan ordered root '${input.declaredOrderedRootName}' does not match task_id '${input.taskId}' and sequence ${sequence}; expected '${expectedOrderedRootName}'.`,
            });
        }
        if (input.referenceKind === 'markdown-link') {
            const normalizedReference = input.referenceValue.replace(/\\/gu, '/');
            const expectedSuffix = `tasks/${expectedOrderedRootName}/task-definition.md`;
            if (normalizedReference !== expectedSuffix && !normalizedReference.endsWith(`/${expectedSuffix}`)) {
                issues.push({
                    code: 'ordered-root-mismatch',
                    message: `Structured Task Plan link '${input.referenceValue}' does not target ordered root '${expectedOrderedRootName}'.`,
                });
            }
        }
    }
    return {
        referenceValue: input.referenceValue,
        referenceKind: input.referenceKind,
        referenceLabel: input.referenceLabel,
        syntax: input.syntax,
        mappingStatus: issues.length === 0 ? 'valid' : 'invalid',
        taskId: input.taskId,
        sequence,
        sequencePrefix,
        orderedTaskRootName,
        issues,
        occurrence: input.occurrence,
    };
}
function addDuplicateIssues(declarations) {
    addDuplicateIssue(declarations, 'taskId', 'duplicate-task-id', 'task_id');
    addDuplicateIssue(declarations, 'sequence', 'duplicate-sequence', 'sequence');
    addDuplicateIssue(declarations, 'orderedTaskRootName', 'duplicate-ordered-root', 'ordered task root');
    for (const declaration of declarations) {
        if (declaration.mappingStatus !== 'absent') {
            declaration.mappingStatus = declaration.issues.length === 0 ? 'valid' : 'invalid';
        }
    }
}
function addDuplicateIssue(declarations, field, code, label) {
    const observed = new Map();
    for (const declaration of declarations) {
        const value = declaration[field];
        if (value === null)
            continue;
        const comparable = String(value);
        const previous = observed.get(comparable);
        if (previous !== undefined) {
            const issue = {
                code,
                message: `Structured Task Plan ${label} '${comparable}' is declared more than once.`,
            };
            if (!previous.issues.some((candidate) => candidate.code === code)) {
                previous.issues.push(issue);
            }
            declaration.issues.push(issue);
        }
        else {
            observed.set(comparable, declaration);
        }
    }
}
function collectMetadataText(lines, lineIndex, remainder) {
    const parts = [remainder];
    for (let index = lineIndex + 1; index < lines.length; index += 1) {
        const next = lines[index].text;
        if (/^\s*-\s+/u.test(next) || /^##\s+/u.test(next))
            break;
        if (next.trim() !== '' && !/^\s+/u.test(next))
            break;
        parts.push(next);
        if (/\)\s*:/u.test(next))
            break;
    }
    return parts.join('\n');
}
function scanContentLines(text) {
    const lines = [];
    const pattern = /.*?(?:\r\n|\n|$)/gu;
    let match;
    while ((match = pattern.exec(text)) !== null) {
        if (match[0] === '' && pattern.lastIndex === text.length)
            break;
        lines.push({
            text: match[0].replace(/\r?\n$/u, ''),
            startOffset: match.index,
        });
    }
    return lines;
}
function offsetToLineColumn(text, offset) {
    const preceding = text.slice(0, offset);
    const lines = preceding.split(/\r?\n/u);
    return {
        line: lines.length,
        column: (lines.at(-1)?.length ?? 0) + 1,
    };
}
