"use strict";
var __importDefault = (this && this.__importDefault) || function (mod) {
    return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.resolveReleaseNotesPreviousMarker = resolveReleaseNotesPreviousMarker;
exports.selectReleaseNotesPlanningEvidence = selectReleaseNotesPlanningEvidence;
exports.createReleaseNotesSourceSummary = createReleaseNotesSourceSummary;
exports.createSkippedReleaseNotesDraft = createSkippedReleaseNotesDraft;
exports.createNotApplicableReleaseNotesDraft = createNotApplicableReleaseNotesDraft;
exports.writeReleaseNotesDraft = writeReleaseNotesDraft;
exports.renderReleaseNotesMarkdown = renderReleaseNotesMarkdown;
const node_child_process_1 = require("node:child_process");
const node_fs_1 = __importDefault(require("node:fs"));
const node_path_1 = __importDefault(require("node:path"));
const errors_1 = require("../../../lib/errors");
const version_1 = require("./version");
const RELEASE_DOWNLOAD_BUNDLE_DIRECTORY = "download-bundle";
const PRIVATE_LICENSE_OPERATION_RELEASE_NOTE_REDACTION = "Private beta/VIP license-operation details are intentionally omitted from public release notes; recipient-facing release guidance covers signed license installation and local status checks.";
const PRIVATE_LICENSE_OPERATION_RELEASE_NOTE_TITLE = "Private beta/VIP license-operation boundary";
const PRIVATE_LICENSE_OPERATION_SOURCE_PATH_PATTERN = /(?:license-issuer|issue-local-license|local-license-ops|recipients\.(?:csv|json)|licenses-out|issuance-report|support-records?)/i;
const PRIVATE_LICENSE_OPERATION_TEXT_PATTERNS = [
    /\bissue-local-license\.(?:js|cjs|mjs)\b/i,
    /\blicense-issuer\b/i,
    /\blocal-license-ops\b/i,
    /\brecipients\.(?:csv|json)\b/i,
    /\blicenses-out\b/i,
    /\bissuance-report\.json\b/i,
    /\bprivate (?:signing )?key\b/i,
    /\bsupport-records?\b/i,
    /\bday-shift\s+license\s+issue\b/i
];
function resolveReleaseNotesPreviousMarker(options) {
    const currentVersion = requireStableVersion(options.version, "current release version");
    if (currentVersion.version === "0.1.0") {
        return {
            kind: "first-public-release",
            marker: "planning-history-start",
            version: null,
            sources: [
                {
                    source: "first-public-release",
                    version: "0.1.0",
                    path: ".day-shift/planning"
                }
            ]
        };
    }
    const sources = [
        ...collectManifestMarkerEvidence(options, currentVersion),
        ...collectLocalReleaseMetadataMarkerEvidence(options, currentVersion),
        ...collectReleaseTagMarkerEvidence(options, currentVersion)
    ];
    const versions = Array.from(new Set(sources.map((source) => source.version))).sort(compareStableVersions);
    if (versions.length === 0) {
        throw (0, errors_1.createUsageError)("Release notes require a previous release marker for versions after 0.1.0.", [
            "Reason: missing-previous-release-marker",
            `Current version: ${currentVersion.version}`,
            `Release output root: ${options.releaseRootRelative}`,
            "Checked sources: release manifests, local release metadata, release tags",
            "Recovery: restore at least one previous release marker before generating release notes source metadata.",
            "Exit behavior: exits before generated release notes or manifest source metadata are emitted."
        ]);
    }
    const selectedVersion = versions[versions.length - 1];
    return {
        kind: "previous-release",
        marker: `release:${selectedVersion}`,
        version: selectedVersion,
        sources: sources
            .filter((source) => source.version === selectedVersion)
            .sort(compareMarkerEvidence)
    };
}
function selectReleaseNotesPlanningEvidence(options) {
    const planningRootRelative = normalizeRelativePath(options.planningRoot ?? ".day-shift/planning");
    const planningRoot = node_path_1.default.resolve(options.repositoryRoot, planningRootRelative);
    const selectedSources = [];
    const excludedSources = [];
    const explicitIncludes = new Set((options.explicitlyIncludedPlanningArtifacts ?? [])
        .map((artifactPath) => normalizeRelativePath(artifactPath)));
    const previouslyReleased = new Set((options.previouslyReleasedPlanningArtifacts ?? [])
        .map((artifactPath) => normalizeRelativePath(artifactPath)));
    if (node_fs_1.default.existsSync(planningRoot) && node_fs_1.default.statSync(planningRoot).isDirectory()) {
        for (const filePath of collectMarkdownFiles(planningRoot)) {
            const artifactPath = toRepositoryRelativePath(options.repositoryRoot, filePath);
            const artifact = readPlanningArtifact(filePath);
            const explicitlyIncluded = explicitIncludes.has(artifactPath);
            if (isPrivateLicenseOperationReleaseNoteSource(artifactPath)) {
                explicitIncludes.delete(artifactPath);
                continue;
            }
            if (explicitlyIncluded) {
                selectedSources.push({
                    kind: "explicit-release-metadata-include",
                    path: artifactPath,
                    title: artifact.title
                });
                explicitIncludes.delete(artifactPath);
                continue;
            }
            if (previouslyReleased.has(artifactPath)) {
                excludedSources.push({ path: artifactPath, reason: "already-included-in-previous-release" });
                continue;
            }
            if (artifactPath.endsWith("/implementation-summary.md")) {
                if (artifact.metadata.status === "completed" && artifact.metadata.implementation_status === "completed") {
                    selectedSources.push({
                        kind: "implementation-summary",
                        path: artifactPath,
                        title: artifact.title
                    });
                }
                else {
                    excludedSources.push({ path: artifactPath, reason: "draft-or-pending" });
                }
                continue;
            }
            if (artifactPath.endsWith("/reconciliation.md")) {
                if (artifact.metadata.status === "completed" && artifact.metadata.reconciliation_status === "completed") {
                    selectedSources.push({
                        kind: "milestone-reconciliation",
                        path: artifactPath,
                        title: artifact.title
                    });
                }
                else {
                    excludedSources.push({ path: artifactPath, reason: "draft-or-pending" });
                }
                continue;
            }
            if (artifactPath.endsWith("/task-definition.md") || artifactPath.endsWith("/overview.md")) {
                excludedSources.push({ path: artifactPath, reason: "proposal-only" });
            }
            else {
                excludedSources.push({ path: artifactPath, reason: "unsupported-planning-artifact" });
            }
        }
    }
    for (const missingPath of Array.from(explicitIncludes).sort()) {
        excludedSources.push({
            path: missingPath,
            reason: "explicit-include-missing"
        });
    }
    const sortedSelectedSources = selectedSources.sort(comparePlanningSources);
    const sortedExcludedSources = excludedSources.sort(comparePlanningExclusions);
    return {
        planningRoot: planningRootRelative,
        selectedSources: sortedSelectedSources,
        excludedSources: sortedExcludedSources,
        counts: {
            selectedImplementationSummaries: sortedSelectedSources.filter((source) => source.kind === "implementation-summary").length,
            selectedMilestoneReconciliations: sortedSelectedSources.filter((source) => source.kind === "milestone-reconciliation").length,
            explicitIncludes: sortedSelectedSources.filter((source) => source.kind === "explicit-release-metadata-include").length,
            excludedDraftOrPending: sortedExcludedSources.filter((source) => source.reason === "draft-or-pending").length,
            excludedProposalOnly: sortedExcludedSources.filter((source) => source.reason === "proposal-only").length,
            excludedUnsupported: sortedExcludedSources.filter((source) => source.reason === "unsupported-planning-artifact" || source.reason === "explicit-include-missing").length
        }
    };
}
function createReleaseNotesSourceSummary(options) {
    const marker = resolveReleaseNotesPreviousMarker(options);
    const previouslyReleasedPlanningArtifacts = collectPreviouslyReleasedPlanningArtifacts(options.repositoryRoot, marker);
    const selection = selectReleaseNotesPlanningEvidence({
        repositoryRoot: options.repositoryRoot,
        planningRoot: options.planningRoot,
        explicitlyIncludedPlanningArtifacts: options.explicitlyIncludedPlanningArtifacts,
        previouslyReleasedPlanningArtifacts
    });
    return {
        status: options.notesGenerated ? "generated" : "not-generated",
        previousReleaseMarker: marker.marker,
        planningArtifactRange: `${marker.marker}..${selection.planningRoot}`,
        marker,
        counts: {
            ...selection.counts,
            selectedSources: selection.selectedSources.length,
            excludedSources: selection.excludedSources.length
        },
        selectedSources: selection.selectedSources,
        excludedSources: selection.excludedSources
    };
}
function collectPreviouslyReleasedPlanningArtifacts(repositoryRoot, marker) {
    if (marker.kind === "first-public-release") {
        return [];
    }
    const paths = new Set();
    for (const source of marker.sources) {
        const sourceSummary = readPreviousReleaseNotesSourceSummary(repositoryRoot, source.path);
        for (const selectedSource of sourceSummary?.selectedSources ?? []) {
            if (typeof selectedSource.path === "string" && selectedSource.path.length > 0) {
                paths.add(normalizeRelativePath(selectedSource.path));
            }
        }
    }
    return Array.from(paths).sort();
}
function readPreviousReleaseNotesSourceSummary(repositoryRoot, markerPath) {
    if (markerPath === null || markerPath.startsWith("tag:")) {
        return null;
    }
    const resolvedPath = node_path_1.default.resolve(repositoryRoot, normalizeRelativePath(markerPath));
    if (!node_fs_1.default.existsSync(resolvedPath) || !node_fs_1.default.statSync(resolvedPath).isFile()) {
        return null;
    }
    const record = readJsonRecord(resolvedPath);
    const directSummary = record.releaseNotesSourceSummary;
    const manifestReleaseNotes = isRecord(record.releaseNotes) ? record.releaseNotes : null;
    const manifestSummary = manifestReleaseNotes?.sourceSummary;
    const summary = isReleaseNotesSourceSummaryLike(directSummary)
        ? directSummary
        : isReleaseNotesSourceSummaryLike(manifestSummary)
            ? manifestSummary
            : null;
    return summary === null ? null : { selectedSources: summary.selectedSources };
}
function createSkippedReleaseNotesDraft(options) {
    return {
        status: "skipped-dry-run",
        path: options.versionDirectoryRelative === null
            ? null
            : `${options.versionDirectoryRelative}/${RELEASE_DOWNLOAD_BUNDLE_DIRECTORY}/release-notes.md`,
        sourceSummary: options.sourceSummary
    };
}
function createNotApplicableReleaseNotesDraft() {
    return {
        status: "not-applicable",
        path: null,
        sourceSummary: null
    };
}
function writeReleaseNotesDraft(options) {
    const bundleDirectory = node_path_1.default.join(options.versionDirectory, RELEASE_DOWNLOAD_BUNDLE_DIRECTORY);
    const releaseNotesPath = node_path_1.default.join(bundleDirectory, "release-notes.md");
    const releaseNotesPathRelative = `${options.versionDirectoryRelative}/${RELEASE_DOWNLOAD_BUNDLE_DIRECTORY}/release-notes.md`;
    const sourceSummary = {
        ...options.sourceSummary,
        status: "generated"
    };
    node_fs_1.default.mkdirSync(bundleDirectory, { recursive: true });
    node_fs_1.default.writeFileSync(releaseNotesPath, renderReleaseNotesMarkdown({
        repositoryRoot: options.repositoryRoot,
        version: options.version,
        generatedPath: releaseNotesPathRelative,
        sourceSummary
    }));
    return {
        status: "created",
        path: releaseNotesPathRelative,
        sourceSummary
    };
}
function renderReleaseNotesMarkdown(options) {
    const sources = options.sourceSummary.selectedSources
        .map((source) => readRenderedSource(options.repositoryRoot, source))
        .sort(compareRenderedSources);
    const lines = [
        `# Day Shift ${options.version} Release Notes`,
        "",
        renderHighLevelReleaseSummary(sources),
        "",
        "## Completed Work",
        ""
    ];
    appendCompletedWorkSection(lines, sources);
    appendUserVisibleBehaviorSection(lines, sources);
    appendKnownFollowUpWorkSection(lines, sources);
    appendSourceTraceSection(lines, sources);
    return lines.join("\n");
}
function renderHighLevelReleaseSummary(sources) {
    const milestoneLabels = uniqueStrings(sources
        .map((source) => createPlanningGroupLabel(source.metadata))
        .filter((label) => label !== "Planning Evidence")
        .map((label) => humanizePlanningGroupLabel(label)));
    if (milestoneLabels.length === 0) {
        return "This release contains the completed planning work selected for this version.";
    }
    if (milestoneLabels.length === 1) {
        return `This release completes ${milestoneLabels[0]}.`;
    }
    return `This release completes ${formatHumanList(milestoneLabels)}.`;
}
function appendCompletedWorkSection(lines, sources) {
    const reconciliationSources = sources.filter((source) => source.kind === "milestone-reconciliation");
    if (reconciliationSources.length === 0) {
        lines.push("- No completed milestone reconciliation evidence was selected for this release.", "");
        return;
    }
    for (const source of reconciliationSources) {
        const label = source.title ?? humanizePlanningGroupLabel(createPlanningGroupLabel(source.metadata));
        lines.push(`- ${label}`);
        if (source.summary.length === 0) {
            lines.push("  - Completed milestone reconciliation evidence was selected for this release.");
        }
        else {
            for (const item of source.summary) {
                lines.push(`  - ${item}`);
            }
        }
    }
    lines.push("");
}
function appendUserVisibleBehaviorSection(lines, sources) {
    const items = uniqueStrings(sources
        .filter((source) => source.kind === "implementation-summary")
        .flatMap((source) => source.summary.length > 0 ? source.summary : source.behavior)
        .map(shortenReleaseNoteLine)
        .filter((item) => item.length > 0));
    lines.push("## User-Visible Behavior");
    lines.push("");
    if (items.length === 0) {
        lines.push("- No user-visible implementation summary was selected for this release.");
    }
    else {
        for (const item of items) {
            lines.push(`- ${item}`);
        }
    }
    lines.push("");
}
function appendKnownFollowUpWorkSection(lines, sources) {
    const followUps = uniqueStrings(sources
        .flatMap((source) => source.followUps)
        .map(normalizeFollowUpLine)
        .filter((item) => item.length > 0));
    lines.push("## Known Follow-Up Work");
    lines.push("");
    if (followUps.length === 0) {
        lines.push("- None.");
    }
    else {
        for (const followUp of followUps) {
            lines.push(`- ${followUp}`);
        }
    }
    lines.push("");
}
function appendSourceTraceSection(lines, sources) {
    const milestonePaths = uniqueStrings(sources.map((source) => createMilestoneTracePath(source.path)));
    lines.push("## Source Trace");
    lines.push("");
    if (milestonePaths.length === 0) {
        lines.push("- No selected milestone source trace.");
    }
    else {
        for (const milestonePath of milestonePaths) {
            lines.push(`- ${milestonePath}`);
        }
    }
    lines.push("");
}
function createMilestoneTracePath(sourcePath) {
    const normalizedPath = normalizeRelativePath(sourcePath);
    const milestoneMatch = /^(.*\/milestones\/[^/]+)/.exec(normalizedPath);
    if (milestoneMatch !== null) {
        return milestoneMatch[1];
    }
    const taskMatch = /^(.*)\/tasks\/[^/]+\/implementation-summary\.md$/.exec(normalizedPath);
    if (taskMatch !== null) {
        return taskMatch[1];
    }
    return normalizedPath
        .replace(/\/reconciliation\.md$/, "")
        .replace(/\/implementation-summary\.md$/, "");
}
function humanizePlanningGroupLabel(label) {
    return label
        .split("/")
        .map((part) => part.trim())
        .filter((part) => part.length > 0)
        .map(humanizeSlug)
        .join(" / ");
}
function humanizeSlug(value) {
    return value
        .replace(/^\d+-/, "")
        .replace(/-/g, " ")
        .replace(/\s+/g, " ")
        .trim();
}
function formatHumanList(values) {
    if (values.length <= 2) {
        return values.join(" and ");
    }
    return `${values.slice(0, -1).join(", ")}, and ${values[values.length - 1]}`;
}
function shortenReleaseNoteLine(value) {
    const normalized = value
        .replace(/^(?:Completed|Passed|Met):\s*/i, "")
        .replace(/\s+/g, " ")
        .trim();
    const sentenceMatch = /^(.+?[.!?])(?:\s|$)/.exec(normalized);
    const sentence = sentenceMatch?.[1] ?? normalized;
    const maxLength = 220;
    if (sentence.length <= maxLength) {
        return sentence;
    }
    const truncated = sentence.slice(0, maxLength - 1);
    const lastSpace = truncated.lastIndexOf(" ");
    return `${truncated.slice(0, lastSpace > 120 ? lastSpace : maxLength - 1).trimEnd()}.`;
}
function normalizeFollowUpLine(value) {
    const normalized = shortenReleaseNoteLine(value)
        .replace(/^(?:Candidate follow-up|Follow-up candidate|Follow-up(?: in task \d+)?|Discovered work|Residual risk):\s*/i, "")
        .trim();
    if (/^(?:none\b.*|not applicable|no additional .*|no new .*|no task-local .*|no .* follow-up.*)$/i.test(normalized)) {
        return "";
    }
    return normalized.length === 0 ? "" : `${normalized[0].toUpperCase()}${normalized.slice(1)}`;
}
function uniqueStrings(values) {
    return Array.from(new Set(values)).sort();
}
function collectManifestMarkerEvidence(options, currentVersion) {
    if (!node_fs_1.default.existsSync(options.releaseRoot) || !node_fs_1.default.statSync(options.releaseRoot).isDirectory()) {
        return [];
    }
    return node_fs_1.default.readdirSync(options.releaseRoot, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .flatMap((entry) => {
        const bundledManifestPath = node_path_1.default.join(options.releaseRoot, entry.name, RELEASE_DOWNLOAD_BUNDLE_DIRECTORY, "release-manifest.json");
        const bundledManifestPathRelative = `${options.releaseRootRelative}/${entry.name}/${RELEASE_DOWNLOAD_BUNDLE_DIRECTORY}/release-manifest.json`;
        const rootManifestPath = node_path_1.default.join(options.releaseRoot, entry.name, "release-manifest.json");
        const rootManifestPathRelative = `${options.releaseRootRelative}/${entry.name}/release-manifest.json`;
        const manifestPath = node_fs_1.default.existsSync(bundledManifestPath) ? bundledManifestPath : rootManifestPath;
        const manifestPathRelative = node_fs_1.default.existsSync(bundledManifestPath) ? bundledManifestPathRelative : rootManifestPathRelative;
        if (!node_fs_1.default.existsSync(manifestPath) || !node_fs_1.default.statSync(manifestPath).isFile()) {
            return [];
        }
        const manifest = readJsonRecord(manifestPath);
        const version = typeof manifest.version === "string" ? manifest.version : entry.name;
        const parsed = (0, version_1.parseReleaseVersion)(version);
        if (!parsed.ok || compareParsedVersions(parsed, currentVersion) >= 0) {
            return [];
        }
        return [{
                source: "published-manifest",
                version: parsed.version,
                path: manifestPathRelative
            }];
    })
        .sort(compareMarkerEvidence);
}
function collectLocalReleaseMetadataMarkerEvidence(options, currentVersion) {
    if (!node_fs_1.default.existsSync(options.releaseRoot) || !node_fs_1.default.statSync(options.releaseRoot).isDirectory()) {
        return [];
    }
    return node_fs_1.default.readdirSync(options.releaseRoot, { withFileTypes: true })
        .filter((entry) => entry.isDirectory())
        .flatMap((entry) => {
        const metadataPath = node_path_1.default.join(options.releaseRoot, entry.name, "staging", ".day-shift-release.json");
        const metadataPathRelative = `${options.releaseRootRelative}/${entry.name}/staging/.day-shift-release.json`;
        if (!node_fs_1.default.existsSync(metadataPath) || !node_fs_1.default.statSync(metadataPath).isFile()) {
            return [];
        }
        const metadata = readJsonRecord(metadataPath);
        const parsed = (0, version_1.parseReleaseVersion)(metadata.version);
        if (!parsed.ok || compareParsedVersions(parsed, currentVersion) >= 0) {
            return [];
        }
        return [{
                source: "local-release-metadata",
                version: parsed.version,
                path: metadataPathRelative
            }];
    })
        .sort(compareMarkerEvidence);
}
function collectReleaseTagMarkerEvidence(options, currentVersion) {
    let tags;
    try {
        tags = (0, node_child_process_1.execFileSync)("git", ["tag", "--list"], {
            cwd: options.repositoryRoot,
            encoding: "utf8",
            stdio: ["ignore", "pipe", "ignore"]
        })
            .split(/\r?\n/)
            .map((tag) => tag.trim())
            .filter((tag) => tag.length > 0);
    }
    catch {
        return [];
    }
    return tags
        .flatMap((tag) => {
        const version = normalizeReleaseTagVersion(tag);
        if (version === null) {
            return [];
        }
        const parsed = (0, version_1.parseReleaseVersion)(version);
        if (!parsed.ok || compareParsedVersions(parsed, currentVersion) >= 0) {
            return [];
        }
        return [{
                source: "release-tag",
                version: parsed.version,
                path: `tag:${tag}`
            }];
    })
        .sort(compareMarkerEvidence);
}
function normalizeReleaseTagVersion(tag) {
    for (const pattern of [/^v(\d+\.\d+\.\d+)$/, /^release-(\d+\.\d+\.\d+)$/, /^day-shift-v(\d+\.\d+\.\d+)$/]) {
        const match = pattern.exec(tag);
        if (match !== null) {
            return match[1];
        }
    }
    return null;
}
function readJsonRecord(filePath) {
    try {
        const parsed = JSON.parse(node_fs_1.default.readFileSync(filePath, "utf8"));
        return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)
            ? parsed
            : {};
    }
    catch {
        return {};
    }
}
function isRecord(value) {
    return typeof value === "object" && value !== null && !Array.isArray(value);
}
function isReleaseNotesSourceSummaryLike(value) {
    if (!isRecord(value) || !Array.isArray(value.selectedSources)) {
        return false;
    }
    return value.selectedSources.every((source) => isRecord(source) && typeof source.path === "string");
}
function collectMarkdownFiles(directory) {
    if (!node_fs_1.default.existsSync(directory) || !node_fs_1.default.statSync(directory).isDirectory()) {
        return [];
    }
    return node_fs_1.default.readdirSync(directory, { withFileTypes: true })
        .flatMap((entry) => {
        const entryPath = node_path_1.default.join(directory, entry.name);
        if (entry.isDirectory()) {
            return collectMarkdownFiles(entryPath);
        }
        return entry.isFile() && entry.name.endsWith(".md") ? [entryPath] : [];
    })
        .sort();
}
function readPlanningArtifact(filePath) {
    const content = node_fs_1.default.readFileSync(filePath, "utf8");
    const metadata = {};
    const frontMatter = /^---\r?\n([\s\S]*?)\r?\n---/.exec(content);
    if (frontMatter !== null) {
        for (const line of frontMatter[1].split(/\r?\n/)) {
            const match = /^([A-Za-z0-9_-]+):\s*"?([^"#]+?)"?\s*$/.exec(line);
            if (match !== null) {
                metadata[match[1]] = match[2].trim();
            }
        }
    }
    return {
        metadata,
        title: /^#\s+(.+)$/m.exec(content)?.[1].trim() ?? null
    };
}
function readRenderedSource(repositoryRoot, source) {
    const filePath = node_path_1.default.join(repositoryRoot, source.path);
    const artifact = readPlanningArtifact(filePath);
    const content = node_fs_1.default.readFileSync(filePath, "utf8");
    return {
        ...source,
        title: redactPrivateLicenseOperationReleaseNoteTitle(source.title ?? artifact.title),
        metadata: artifact.metadata,
        summary: redactPrivateLicenseOperationReleaseNoteLines(extractEvidenceLines(content, ["Summary", "Review Status", "Milestone Intent"])),
        behavior: redactPrivateLicenseOperationReleaseNoteLines(extractEvidenceLines(content, ["Acceptance Criteria Results", "Acceptance Criteria Review"])),
        validation: redactPrivateLicenseOperationReleaseNoteLines(extractEvidenceLines(content, ["Validation Performed", "Validation Review"])),
        followUps: redactPrivateLicenseOperationReleaseNoteLines(extractEvidenceLines(content, ["Discovered Work", "Risks and Follow-Ups", "Follow-Up Tasks", "Backlog Work", "Accepted Residual Work"])),
        compatibility: redactPrivateLicenseOperationReleaseNoteLines(extractCompatibilityEvidenceLines(content))
    };
}
function groupRenderedSources(sources) {
    const groups = new Map();
    for (const source of sources) {
        const label = createPlanningGroupLabel(source.metadata);
        groups.set(label, [...(groups.get(label) ?? []), source]);
    }
    return Array.from(groups.entries())
        .map(([label, groupSources]) => ({
        label,
        sources: groupSources.sort(compareRenderedSources)
    }))
        .sort((left, right) => left.label.localeCompare(right.label));
}
function createPlanningGroupLabel(metadata) {
    const parts = [metadata.slice, metadata.phase, metadata.milestone]
        .filter((value) => typeof value === "string" && value.length > 0);
    return parts.length === 0 ? "Planning Evidence" : parts.join(" / ");
}
function appendEvidenceSection(lines, title, sources, readItems) {
    const rows = sources
        .flatMap((source) => readItems(source).map((item) => ({
        source,
        item
    })))
        .filter((row) => row.item.length > 0);
    if (rows.length === 0) {
        return;
    }
    lines.push(`## ${title}`);
    lines.push("");
    for (const row of rows) {
        lines.push(`- ${row.item}`);
        lines.push(`  - Source: ${row.source.path}`);
    }
    lines.push("");
}
function extractEvidenceLines(content, headings) {
    return headings.flatMap((heading) => extractMarkdownSectionLines(content, heading));
}
function extractCompatibilityEvidenceLines(content) {
    const sections = collectMarkdownSections(content)
        .filter((section) => /compatibility|migration|schema/i.test(section.heading));
    return normalizeEvidenceLines(sections.flatMap((section) => section.lines));
}
function extractMarkdownSectionLines(content, heading) {
    const section = collectMarkdownSections(content)
        .find((candidate) => candidate.heading.toLowerCase() === heading.toLowerCase());
    return section === undefined ? [] : normalizeEvidenceLines(section.lines);
}
function collectMarkdownSections(content) {
    const sections = [];
    let current = null;
    for (const line of content.split(/\r?\n/)) {
        const headingMatch = /^##\s+(.+?)\s*$/.exec(line);
        if (headingMatch !== null) {
            current = {
                heading: headingMatch[1].trim(),
                lines: []
            };
            sections.push(current);
            continue;
        }
        if (current !== null) {
            current.lines.push(line);
        }
    }
    return sections;
}
function normalizeEvidenceLines(lines) {
    return lines
        .map((line) => line.trim())
        .filter((line) => line.length > 0)
        .filter((line) => !/^[-*]\s*(none|none currently|not applicable)\.?$/i.test(line))
        .map((line) => line.replace(/^[-*]\s+/, "").trim())
        .filter((line) => line.length > 0);
}
function isPrivateLicenseOperationReleaseNoteSource(artifactPath) {
    return PRIVATE_LICENSE_OPERATION_SOURCE_PATH_PATTERN.test(artifactPath);
}
function redactPrivateLicenseOperationReleaseNoteTitle(value) {
    if (value === null) {
        return null;
    }
    return containsPrivateLicenseOperationText(value)
        ? PRIVATE_LICENSE_OPERATION_RELEASE_NOTE_TITLE
        : value;
}
function redactPrivateLicenseOperationReleaseNoteLines(lines) {
    const redactedLines = [];
    let redactionAdded = false;
    for (const line of lines) {
        if (containsPrivateLicenseOperationText(line)) {
            if (!redactionAdded) {
                redactedLines.push(PRIVATE_LICENSE_OPERATION_RELEASE_NOTE_REDACTION);
                redactionAdded = true;
            }
            continue;
        }
        redactedLines.push(line);
    }
    return redactedLines;
}
function containsPrivateLicenseOperationText(value) {
    return PRIVATE_LICENSE_OPERATION_TEXT_PATTERNS.some((pattern) => pattern.test(value));
}
function normalizeRelativePath(value) {
    return value.trim().replace(/\\/g, "/").replace(/^\.?\//, "");
}
function toRepositoryRelativePath(repositoryRoot, filePath) {
    return node_path_1.default.relative(repositoryRoot, filePath).split(node_path_1.default.sep).join("/");
}
function requireStableVersion(version, label) {
    const parsed = (0, version_1.parseReleaseVersion)(version);
    if (!parsed.ok) {
        throw (0, errors_1.createUsageError)(`Release notes marker resolution requires a stable ${label}.`, [
            `Reason: ${parsed.reason}`,
            `Version: ${version}`,
            `Recovery: ${parsed.correctiveAction}`
        ]);
    }
    return parsed;
}
function compareStableVersions(left, right) {
    return compareParsedVersions(requireStableVersion(left, "version"), requireStableVersion(right, "version"));
}
function compareParsedVersions(left, right) {
    return left.major - right.major ||
        left.minor - right.minor ||
        left.patch - right.patch;
}
function compareMarkerEvidence(left, right) {
    return compareStableVersions(left.version, right.version) ||
        left.source.localeCompare(right.source) ||
        String(left.path ?? "").localeCompare(String(right.path ?? ""));
}
function comparePlanningSources(left, right) {
    return left.path.localeCompare(right.path) || left.kind.localeCompare(right.kind);
}
function comparePlanningExclusions(left, right) {
    return left.path.localeCompare(right.path) || left.reason.localeCompare(right.reason);
}
function compareRenderedSources(left, right) {
    return createPlanningGroupLabel(left.metadata).localeCompare(createPlanningGroupLabel(right.metadata)) ||
        left.path.localeCompare(right.path) ||
        left.kind.localeCompare(right.kind);
}
