"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.materializeMarkdownLinks = materializeMarkdownLinks;
const node_path_1 = __importDefault(require("node:path"));
function materializeMarkdownLinks(options) {
    const replacements = [];
    const skippedCandidates = [];
    const occupiedRanges = [];
    const fencedCodeRanges = collectFencedCodeRanges(options.markdownText);
    const frontMatterRange = collectFrontMatterRange(options.markdownText);
    for (const candidate of [...options.candidates].sort(compareCandidates)) {
        const occurrence = candidate.occurrence;
        const startOffset = occurrence.startOffset;
        const endOffset = occurrence.endOffset;
        const rawText = options.markdownText.slice(startOffset, endOffset);
        if (isPlainPathOnlyField(candidate.field)) {
            skippedCandidates.push(createSkippedCandidate(candidate, "non-plain-reference", "Field requires plain repository-relative path bullets and is not materialized to Markdown links."));
            continue;
        }
        if (!isMaterializableReferenceFormat(candidate)) {
            skippedCandidates.push(createSkippedCandidate(candidate, "non-plain-reference", "Reference is not a plain body reference."));
            continue;
        }
        if (!sourceTextMatchesCandidate({ candidate, rawText })) {
            skippedCandidates.push(createSkippedCandidate(candidate, "source-mismatch", "Source text no longer matches the candidate evidence."));
            continue;
        }
        if (frontMatterRange !== null && rangesOverlap(occurrence, frontMatterRange)) {
            skippedCandidates.push(createSkippedCandidate(candidate, "front-matter", "Reference is inside YAML front matter."));
            continue;
        }
        if (fencedCodeRanges.some((range) => rangesOverlap(occurrence, range))) {
            skippedCandidates.push(createSkippedCandidate(candidate, "fenced-code", "Reference is inside a fenced code block."));
            continue;
        }
        if (occurrence.format === "plain" && isInsideInlineCode(options.markdownText, startOffset)) {
            skippedCandidates.push(createSkippedCandidate(candidate, "inline-code", "Reference is inside inline code."));
            continue;
        }
        if (isExistingMarkdownLinkDestination(options.markdownText, startOffset)) {
            skippedCandidates.push(createSkippedCandidate(candidate, "existing-link", "Reference is already part of a Markdown link."));
            continue;
        }
        if (occupiedRanges.some((range) => rangesOverlap(occurrence, range))) {
            skippedCandidates.push(createSkippedCandidate(candidate, "overlapping-candidate", "Candidate overlaps another accepted replacement."));
            continue;
        }
        const linkDestination = toMarkdownLinkDestination(candidate.sourceArtifactPath, candidate.normalizedPath);
        const linkText = toMarkdownLinkText(candidate);
        const replacementText = `[${linkText}](${linkDestination})`;
        replacements.push({
            sourceArtifactPath: candidate.sourceArtifactPath,
            field: candidate.field,
            value: candidate.value,
            normalizedPath: candidate.normalizedPath,
            linkDestination,
            startOffset,
            endOffset,
            replacementText
        });
        occupiedRanges.push({ startOffset, endOffset });
    }
    const markdownText = applyReplacements(options.markdownText, replacements);
    return {
        changed: replacements.length > 0,
        markdownText,
        replacements,
        skippedCandidates
    };
}
function toMarkdownLinkDestination(sourceArtifactPath, normalizedPath) {
    const sourceDirectory = node_path_1.default.posix.dirname(sourceArtifactPath);
    const relativePath = node_path_1.default.posix.relative(sourceDirectory, normalizedPath);
    return relativePath === "" ? node_path_1.default.posix.basename(normalizedPath) : relativePath;
}
function toMarkdownLinkText(candidate) {
    if (isDeclaredChildLinkField(candidate.field)) {
        return candidate.value;
    }
    if ((candidate.field === "body.Completed Task Summaries" ||
        candidate.field === "body.Acceptance Criteria Review" ||
        candidate.field === "body.Validation Review") &&
        candidate.normalizedPath.endsWith("/implementation-summary.md")) {
        const taskSlug = node_path_1.default.posix.basename(node_path_1.default.posix.dirname(candidate.normalizedPath));
        const readableTaskName = taskSlug
            .replace(/^\d+-/, "")
            .split("-")
            .filter((part) => part.length > 0)
            .map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`)
            .join(" ");
        return readableTaskName === "" ? "Implementation Summary" : `${readableTaskName} Summary`;
    }
    return candidate.normalizedPath;
}
function isMaterializableReferenceFormat(candidate) {
    return candidate.occurrence.format === "plain" ||
        (candidate.occurrence.format === "backticked" &&
            isDeclaredChildLinkField(candidate.field));
}
function isPlainPathOnlyField(field) {
    return field === "body.Dependencies" ||
        field === "body.Implementation Target Paths" ||
        field === "body.Changed Paths";
}
function isDeclaredChildLinkField(field) {
    return field === "body.Phases" ||
        field === "body.Milestones" ||
        field === "body.Tasks";
}
function sourceTextMatchesCandidate(options) {
    const occurrence = options.candidate.occurrence;
    if (options.rawText !== occurrence.rawText) {
        return false;
    }
    if (occurrence.format === "plain") {
        return options.rawText === options.candidate.value;
    }
    return occurrence.value === options.candidate.value;
}
function compareCandidates(left, right) {
    return left.occurrence.startOffset - right.occurrence.startOffset ||
        left.occurrence.endOffset - right.occurrence.endOffset;
}
function createSkippedCandidate(candidate, reason, message) {
    return {
        sourceArtifactPath: candidate.sourceArtifactPath,
        field: candidate.field,
        value: candidate.value,
        normalizedPath: candidate.normalizedPath,
        startOffset: candidate.occurrence.startOffset,
        endOffset: candidate.occurrence.endOffset,
        reason,
        message
    };
}
function applyReplacements(markdownText, replacements) {
    let nextText = markdownText;
    for (const replacement of [...replacements].sort((left, right) => right.startOffset - left.startOffset)) {
        nextText = `${nextText.slice(0, replacement.startOffset)}${replacement.replacementText}${nextText.slice(replacement.endOffset)}`;
    }
    return nextText;
}
function collectFrontMatterRange(markdownText) {
    if (!markdownText.startsWith("---")) {
        return null;
    }
    const closingMatch = /\r?\n---\r?\n/.exec(markdownText.slice(3));
    if (closingMatch === null) {
        return null;
    }
    return {
        startOffset: 0,
        endOffset: 3 + closingMatch.index + closingMatch[0].length
    };
}
function collectFencedCodeRanges(markdownText) {
    const ranges = [];
    const lines = markdownText.split(/\r?\n/);
    const newlineMatches = markdownText.match(/\r?\n/g) ?? [];
    let offset = 0;
    let openFenceStart = null;
    for (let index = 0; index < lines.length; index += 1) {
        if (/^\s*```/.test(lines[index])) {
            if (openFenceStart === null) {
                openFenceStart = offset;
            }
            else {
                ranges.push({
                    startOffset: openFenceStart,
                    endOffset: offset + lines[index].length
                });
                openFenceStart = null;
            }
        }
        offset += lines[index].length + (newlineMatches[index]?.length ?? 0);
    }
    if (openFenceStart !== null) {
        ranges.push({
            startOffset: openFenceStart,
            endOffset: markdownText.length
        });
    }
    return ranges;
}
function isInsideInlineCode(markdownText, offset) {
    const lineStart = markdownText.lastIndexOf("\n", offset - 1) + 1;
    const lineEndIndex = markdownText.indexOf("\n", offset);
    const lineEnd = lineEndIndex === -1 ? markdownText.length : lineEndIndex;
    const before = markdownText.slice(lineStart, offset);
    const line = markdownText.slice(lineStart, lineEnd);
    const backticksBefore = [...before].filter((character) => character === "`").length;
    const backticksInLine = [...line].filter((character) => character === "`").length;
    return backticksInLine >= 2 && backticksBefore % 2 === 1;
}
function isExistingMarkdownLinkDestination(markdownText, offset) {
    if (markdownText.slice(Math.max(0, offset - 2), offset) !== "](") {
        return false;
    }
    const closeParen = markdownText.indexOf(")", offset);
    return closeParen !== -1;
}
function rangesOverlap(left, right) {
    return left.startOffset < right.endOffset && right.startOffset < left.endOffset;
}
