import { filteredMarker } from "./transcript-serialization.js";
function coerceEndpoint(value) {
    if (value === undefined)
        return undefined;
    const number = Number(value);
    return Number.isFinite(number) ? Math.trunc(number) : undefined;
}
function mergeRanges(ranges) {
    const merged = [];
    for (const range of ranges) {
        const current = merged[merged.length - 1];
        if (current && range.start <= current.end + 1)
            current.end = Math.max(current.end, range.end);
        else
            merged.push({ ...range });
    }
    return merged;
}
function splitAroundProtected(range, protectedLines) {
    const fragments = [];
    let start = range.start;
    for (let line = range.start; line <= range.end; line++) {
        if (!protectedLines.has(line))
            continue;
        if (start < line)
            fragments.push({ start, end: line - 1 });
        start = line + 1;
    }
    if (start <= range.end)
        fragments.push({ start, end: range.end });
    return fragments;
}
/** Normalize untrusted model ranges into the sole range type accepted by reconstruction. */
export function validateDeletedRanges(raw, region) {
    const totalLines = region.lines.length;
    const normalized = [];
    for (const candidate of raw) {
        let start = coerceEndpoint(candidate.start);
        let end = coerceEndpoint(candidate.end);
        if (start === undefined || end === undefined)
            continue;
        if (start > end)
            [start, end] = [end, start];
        start = Math.max(1, start);
        end = Math.min(totalLines, end);
        if (start <= end)
            normalized.push({ start, end });
    }
    normalized.sort((left, right) => left.start - right.start || left.end - right.end);
    const protectedLines = new Set(region.protectedLineNumbers ?? []);
    const split = mergeRanges(normalized).flatMap((range) => splitAroundProtected(range, protectedLines));
    return Object.assign(split, { __brand: "ValidatedRanges" });
}
function rangesToDeletedLines(ranges, lineCount) {
    const deleted = Array.from({ length: lineCount }, () => false);
    for (const range of ranges) {
        for (let line = range.start; line <= range.end; line++)
            deleted[line - 1] = true;
    }
    return deleted;
}
function foldAdjacentMarkers(region, ranges) {
    const deleted = rangesToDeletedLines(ranges, region.lines.length);
    let changed = true;
    while (changed) {
        changed = false;
        for (const lineNumber of region.priorMarkerNs.keys()) {
            const index = lineNumber - 1;
            if (deleted[index])
                continue;
            const previousDeleted = index > 0 ? (deleted[index - 1] ?? false) : false;
            const nextDeleted = index + 1 < deleted.length ? (deleted[index + 1] ?? false) : false;
            if (previousDeleted || nextDeleted) {
                deleted[index] = true;
                changed = true;
            }
        }
    }
    const folded = [];
    let start;
    for (let index = 0; index <= deleted.length; index++) {
        // Sentinel iteration at index === deleted.length: treat as not deleted so a
        // trailing open range closes without reading past the array bounds.
        const isDeleted = index < deleted.length ? (deleted[index] ?? false) : false;
        if (isDeleted && start === undefined)
            start = index + 1;
        if (!isDeleted && start !== undefined) {
            folded.push({ start, end: index });
            start = undefined;
        }
    }
    return folded;
}
function cumulativeDeletedCount(region, range) {
    let count = range.end - range.start + 1;
    for (let line = range.start; line <= range.end; line++) {
        const priorCount = region.priorMarkerNs.get(line);
        if (priorCount !== undefined)
            count += priorCount - 1;
    }
    return count;
}
/** Rebuild a compacted transcript mechanically from validated ranges. */
export function reconstructCompactedTranscript(region, ranges) {
    const finalRanges = foldAdjacentMarkers(region, ranges);
    const output = [];
    let line = 1;
    for (const range of finalRanges) {
        while (line < range.start)
            output.push(region.lines[line++ - 1]);
        output.push(filteredMarker(cumulativeDeletedCount(region, range)));
        line = range.end + 1;
    }
    while (line <= region.lines.length)
        output.push(region.lines[line++ - 1]);
    const text = output.join("\n");
    const linesDeleted = finalRanges.reduce((total, range) => total + range.end - range.start + 1, 0);
    const tokensAfter = Math.ceil(text.length / 4);
    const percentReduction = region.tokenEstimate === 0 ? 0 : Math.round((1 - tokensAfter / region.tokenEstimate) * 1000) / 10;
    return {
        text,
        ranges: finalRanges,
        stats: {
            linesBefore: region.lines.length,
            linesDeleted,
            linesKept: region.lines.length - linesDeleted,
            rangeCount: finalRanges.length,
            tokensBefore: region.tokenEstimate,
            tokensAfter,
            percentReduction,
        },
    };
}
//# sourceMappingURL=deleted-ranges.js.map