import { getTranscriptTextForSlide, parseTranscriptTimedText, resolveSlideTextBudget, resolveSlideWindowSeconds, } from "./text-transcript.js";
const SLIDE_TAG_PATTERN = /^\[[^\]]*slide[^\d\]]*(\d+)[^\]]*\]\s*(.*)$/i;
const SLIDE_LABEL_PATTERN = /^(?:\[)?slide\s+(\d+)(?:\s*(?:\/|of)\s*\d+)?(?:\])?(?:\s*[\u00b7:-]\s*.*)?$/i;
const TITLE_ONLY_MAX_CHARS = 80;
const collapseLineWhitespace = (value) => value.replace(/\s+/g, " ").trim();
const deriveHeadlineFromBody = (body) => {
    const cleaned = collapseLineWhitespace(body);
    if (!cleaned)
        return null;
    const firstSentence = cleaned.split(/[.!?]/)[0] ?? "";
    const clause = firstSentence.split(/[,;:\u2013\u2014-]/)[0] ?? firstSentence;
    const words = clause.trim().split(/\s+/).filter(Boolean);
    if (words.length < 2)
        return null;
    const title = words.slice(0, Math.min(6, words.length)).join(" ");
    return title.replace(/[,:;-]+$/g, "").trim() || null;
};
const isTitleOnlySlideText = (value) => {
    const trimmed = value.trim();
    if (!trimmed)
        return true;
    const lines = trimmed
        .split("\n")
        .map((line) => line.trim())
        .filter(Boolean);
    if (lines.length !== 1)
        return false;
    if (trimmed.length > TITLE_ONLY_MAX_CHARS)
        return false;
    if (/[.!?]/.test(trimmed))
        return false;
    return true;
};
const isInterludeSlideText = (value) => {
    const normalized = value
        .trim()
        .split("\n")
        .map((line) => line.trim().replace(/^#{1,6}\s+/, ""))
        .filter(Boolean)
        .join(" ");
    return normalized.toLowerCase() === "interlude";
};
const stripSlideTitleList = (markdown) => {
    if (!markdown.trim())
        return markdown;
    const lines = markdown.split("\n");
    const out = [];
    let skipNextTitle = false;
    for (const line of lines) {
        const trimmed = line.trim();
        if (SLIDE_TAG_PATTERN.test(trimmed) || SLIDE_LABEL_PATTERN.test(trimmed)) {
            skipNextTitle = true;
            continue;
        }
        if (skipNextTitle) {
            if (!trimmed)
                continue;
            if (isTitleOnlySlideText(trimmed)) {
                skipNextTitle = false;
                continue;
            }
            skipNextTitle = false;
        }
        out.push(line);
    }
    return out.join("\n");
};
export const splitSlideTitleFromText = ({ text, }) => {
    const trimmed = text.trim();
    if (!trimmed)
        return { title: null, body: "" };
    const lines = trimmed
        .split("\n")
        .map((line) => line.trim())
        .filter(Boolean);
    if (lines.length === 0)
        return { title: null, body: "" };
    const cleaned = lines.slice();
    while (cleaned.length > 0) {
        const first = cleaned[0] ?? "";
        if (SLIDE_LABEL_PATTERN.test(first) || SLIDE_TAG_PATTERN.test(first)) {
            cleaned.shift();
            continue;
        }
        break;
    }
    if (cleaned.length === 0)
        return { title: null, body: "" };
    const filtered = cleaned.filter((line) => !SLIDE_LABEL_PATTERN.test(line) && !SLIDE_TAG_PATTERN.test(line));
    if (filtered.length === 0)
        return { title: null, body: "" };
    const labelPattern = /^(?:title|headline)\s*:\s*(.*)$/i;
    let title = null;
    let bodyLines = filtered.slice();
    for (let i = 0; i < filtered.length; i += 1) {
        const line = filtered[i] ?? "";
        const labelMatch = line.match(labelPattern);
        if (!labelMatch)
            continue;
        const labelText = collapseLineWhitespace(labelMatch[1] ?? "").trim();
        if (labelText) {
            title = labelText;
            bodyLines = filtered.filter((_, idx) => idx !== i);
        }
        else {
            const fallbackTitle = collapseLineWhitespace(filtered[i + 1] ?? "").trim();
            if (fallbackTitle)
                title = fallbackTitle;
            bodyLines = filtered.filter((_, idx) => idx !== i && idx !== i + 1);
        }
        break;
    }
    if (!title) {
        for (let i = 0; i < filtered.length; i += 1) {
            const line = filtered[i] ?? "";
            const headingMatch = line.match(/^#{1,6}\s+(.+)/);
            if (!headingMatch)
                continue;
            const headingText = collapseLineWhitespace(headingMatch[1] ?? "").trim();
            const headingLabelMatch = headingText.match(labelPattern);
            if (headingLabelMatch) {
                const headingLabel = collapseLineWhitespace(headingLabelMatch[1] ?? "").trim();
                if (headingLabel) {
                    title = headingLabel;
                    bodyLines = filtered.filter((_, idx) => idx !== i);
                }
                else {
                    const fallbackTitle = collapseLineWhitespace(filtered[i + 1] ?? "").trim();
                    if (fallbackTitle)
                        title = fallbackTitle;
                    bodyLines = filtered.filter((_, idx) => idx !== i && idx !== i + 1);
                }
            }
            else {
                title = headingText || null;
                bodyLines = filtered.filter((_, idx) => idx !== i);
            }
            break;
        }
    }
    if (!title && filtered.length > 1) {
        const candidates = filtered
            .map((line, idx) => ({ line, idx }))
            .filter(({ line }) => isTitleOnlySlideText(line));
        if (candidates.length === 1) {
            const pick = candidates[0];
            title = collapseLineWhitespace(pick?.line ?? "").trim() || null;
            bodyLines = filtered.filter((_, idx) => idx !== pick?.idx);
        }
        else if (isTitleOnlySlideText(filtered[0] ?? "")) {
            title = collapseLineWhitespace(filtered[0] ?? "").trim() || null;
            bodyLines = filtered.slice(1);
        }
    }
    const body = bodyLines
        .map((line) => line.replace(/^#{1,6}\s+/, ""))
        .join("\n")
        .trim();
    if (!title && body) {
        title = deriveHeadlineFromBody(body);
    }
    return { title, body };
};
export const ensureSlideTitleLine = ({ text, slide, total, }) => {
    void slide;
    void total;
    return text
        .trim()
        .split("\n")
        .map((line) => line.replace(/^(#{1,6})([^#\s])/, "$1 $2"))
        .join("\n");
};
export function findSlidesSectionStart(markdown) {
    if (!markdown)
        return null;
    const heading = markdown.match(/^#{1,3}\s+Slides\b.*$/im);
    const tag = markdown.match(/^\[slide:\d+\]/im);
    const label = markdown.match(/^\s*slide\s+\d+(?:\s*(?:\/|of)\s*\d+)?(?:\s*[\u00b7:-].*)?$/im);
    const indexes = [heading?.index, tag?.index, label?.index].filter((idx) => idx != null);
    if (indexes.length === 0)
        return null;
    return Math.min(...indexes);
}
export function splitSummaryFromSlides(markdown) {
    const start = findSlidesSectionStart(markdown);
    if (start == null)
        return { summary: markdown.trim(), slidesSection: null };
    const summary = markdown.slice(0, start).trim();
    const slidesSection = markdown.slice(start);
    return { summary, slidesSection };
}
export function parseSlideSummariesFromMarkdown(markdown) {
    const result = new Map();
    if (!markdown.trim())
        return result;
    const start = findSlidesSectionStart(markdown);
    if (start == null) {
        const inline = parseInlineSlideSummaries(markdown);
        return inline.size > 0 ? inline : result;
    }
    const slice = markdown.slice(start);
    const lines = slice.split("\n");
    let currentIndex = null;
    let buffer = [];
    let sawBlankAfterMarker = false;
    let sawBlankAfterTitle = false;
    const hasFutureMarker = (start) => lines.slice(start).some((line) => {
        const trimmed = line.trim();
        return SLIDE_TAG_PATTERN.test(trimmed) || SLIDE_LABEL_PATTERN.test(trimmed);
    });
    const flush = () => {
        if (currentIndex == null)
            return;
        const text = buffer
            .map((line) => collapseLineWhitespace(line))
            .join("\n")
            .trim();
        result.set(currentIndex, text);
        currentIndex = null;
        buffer = [];
        sawBlankAfterMarker = false;
        sawBlankAfterTitle = false;
    };
    for (let i = 0; i < lines.length; i += 1) {
        const line = lines[i] ?? "";
        const trimmed = line.trim();
        const heading = trimmed.match(/^#{1,3}\s+\S/);
        if (heading &&
            (currentIndex == null ||
                buffer.length > 0 ||
                sawBlankAfterMarker ||
                /^#{3}\s+\S/.test(trimmed)) &&
            !trimmed.toLowerCase().startsWith("### slides")) {
            flush();
            break;
        }
        const match = trimmed.match(SLIDE_TAG_PATTERN);
        if (match) {
            flush();
            const index = Number.parseInt(match[1] ?? "", 10);
            if (!Number.isFinite(index) || index <= 0)
                continue;
            currentIndex = index;
            sawBlankAfterMarker = false;
            sawBlankAfterTitle = false;
            const rest = (match[2] ?? "").trim();
            if (rest)
                buffer.push(rest);
            continue;
        }
        const label = trimmed.match(SLIDE_LABEL_PATTERN);
        if (label) {
            flush();
            const index = Number.parseInt(label[1] ?? "", 10);
            if (!Number.isFinite(index) || index <= 0)
                continue;
            currentIndex = index;
            sawBlankAfterMarker = false;
            sawBlankAfterTitle = false;
            continue;
        }
        if (currentIndex == null)
            continue;
        if (!trimmed) {
            if (buffer.length === 0) {
                sawBlankAfterMarker = true;
            }
            if (buffer.length === 1 && isTitleOnlySlideText(buffer[0] ?? "")) {
                sawBlankAfterTitle = true;
            }
            continue;
        }
        if (sawBlankAfterTitle &&
            buffer.length === 1 &&
            isTitleOnlySlideText(buffer[0] ?? "") &&
            (!/^#{1,6}\s+\S/.test(buffer[0]?.trim() ?? "") || isInterludeSlideText(buffer[0] ?? "")) &&
            !isTitleOnlySlideText(trimmed) &&
            !hasFutureMarker(i)) {
            flush();
            break;
        }
        sawBlankAfterMarker = false;
        sawBlankAfterTitle = false;
        buffer.push(trimmed);
    }
    flush();
    return result;
}
function parseInlineSlideSummaries(markdown) {
    const result = new Map();
    const matches = Array.from(markdown.matchAll(/\[slide:(\d+)\]/gi));
    if (matches.length === 0)
        return result;
    for (let i = 0; i < matches.length; i += 1) {
        const match = matches[i];
        const index = Number.parseInt(match?.[1] ?? "", 10);
        if (!Number.isFinite(index) || index <= 0)
            continue;
        const start = (match.index ?? 0) + match[0].length;
        const next = i + 1 < matches.length ? matches[i + 1] : null;
        const end = next?.index ?? markdown.length;
        if (end <= start) {
            result.set(index, "");
            continue;
        }
        const segment = markdown
            .slice(start, end)
            .replace(/^\s*[:\-\u2013\u2014]?\s*/, "")
            .trim();
        result.set(index, segment);
    }
    return result;
}
export function extractSlideMarkers(markdown) {
    if (!markdown.trim())
        return [];
    const indexes = [];
    const regex = /\[[^\]]*slide[^\d\]]*(\d+)[^\]]*\]/gi;
    let match = regex.exec(markdown);
    while (match) {
        const index = Number.parseInt(match[1] ?? "", 10);
        match = regex.exec(markdown);
        if (Number.isFinite(index) && index > 0)
            indexes.push(index);
    }
    return indexes;
}
export function normalizeSummarySlideHeadings(markdown) {
    if (!markdown.trim())
        return markdown;
    if (!/\[slide:\d+\]/i.test(markdown))
        return markdown;
    const deleteMarker = "__SUMMARIZE_DELETE__";
    const lines = [];
    for (const line of markdown.split("\n")) {
        const trimmed = line.trim();
        const headingSlideMatch = trimmed.match(/^#{1,6}\s*(\[[^\]]*slide[^\d\]]*\d+[^\]]*\])\s*(.*)$/i);
        if (!headingSlideMatch) {
            lines.push(line);
            continue;
        }
        lines.push(headingSlideMatch[1] ?? "");
        const rest = (headingSlideMatch[2] ?? "").replace(/^\[[\d:\s.\-\u2013\u2014]+\]\s*/, "").trim();
        if (rest) {
            lines.push(`## ${rest.replace(/^#{1,6}\s*/, "")}`);
        }
    }
    for (let i = 0; i < lines.length; i += 1) {
        const line = lines[i] ?? "";
        if (!SLIDE_TAG_PATTERN.test(line.trim()))
            continue;
        for (let k = i + 1; k < lines.length; k += 1) {
            const candidate = lines[k] ?? "";
            const trimmed = candidate.trim();
            if (!trimmed)
                continue;
            if (SLIDE_LABEL_PATTERN.test(trimmed)) {
                lines[k] = deleteMarker;
                continue;
            }
            const labelMatch = trimmed.match(/^(?:title|headline)\s*:\s*(.*)$/i);
            if (labelMatch) {
                const labelText = collapseLineWhitespace(labelMatch[1] ?? "").trim();
                lines[k] = labelText ? `## ${labelText}` : deleteMarker;
            }
            break;
        }
    }
    return lines.filter((line) => line !== deleteMarker).join("\n");
}
function splitMarkdownParagraphs(markdown) {
    return markdown
        .split(/\n\s*\n+/)
        .map((part) => part.trim())
        .filter(Boolean);
}
function pickIntroParagraph(markdown) {
    const paragraphs = splitMarkdownParagraphs(markdown);
    if (paragraphs.length === 0)
        return "";
    const firstNonHeading = paragraphs.find((paragraph) => !/^#{1,6}\s+\S/.test(paragraph.trim())) ?? paragraphs[0];
    if (!firstNonHeading)
        return "";
    const sentences = firstNonHeading.match(/[^.!?]+[.!?]+|[^.!?]+$/g) ?? [firstNonHeading];
    if (sentences.length <= 3)
        return firstNonHeading.trim();
    return sentences.slice(0, 3).join(" ").trim();
}
export function buildSlideTextFallback({ slides, transcriptTimedText, lengthArg, }) {
    const map = new Map();
    if (!transcriptTimedText || !transcriptTimedText.trim())
        return map;
    if (slides.length === 0)
        return map;
    const segments = parseTranscriptTimedText(transcriptTimedText);
    if (segments.length === 0)
        return map;
    const ordered = slides.slice().sort((a, b) => a.index - b.index);
    const budget = resolveSlideTextBudget({ lengthArg, slideCount: ordered.length });
    const windowSeconds = resolveSlideWindowSeconds({ lengthArg });
    for (let i = 0; i < ordered.length; i += 1) {
        const slide = ordered[i];
        if (!slide)
            continue;
        const nextSlide = i + 1 < ordered.length ? (ordered[i + 1] ?? null) : null;
        const text = getTranscriptTextForSlide({
            slide,
            nextSlide,
            segments,
            budget,
            windowSeconds,
        });
        if (text)
            map.set(slide.index, text);
    }
    return map;
}
export function coerceSummaryWithSlides({ markdown, slides, transcriptTimedText, lengthArg, reserveIntro = true, }) {
    if (!markdown.trim() || slides.length === 0)
        return markdown;
    const ordered = slides.slice().sort((a, b) => a.index - b.index);
    const { summary, slidesSection } = splitSummaryFromSlides(markdown);
    const intro = reserveIntro ? pickIntroParagraph(summary) : "";
    const slideSummaries = slidesSection ? parseSlideSummariesFromMarkdown(markdown) : new Map();
    const interludeSlideIndexes = new Set(Array.from(slideSummaries.entries())
        .filter(([, text]) => isInterludeSlideText(text))
        .map(([index]) => index));
    const titleOnlySlideSummaries = slideSummaries.size > 0 &&
        Array.from(slideSummaries.values()).every((text) => isTitleOnlySlideText(text));
    const distributionMarkdown = titleOnlySlideSummaries ? stripSlideTitleList(markdown) : markdown;
    const fallbackSummaries = buildSlideTextFallback({
        slides: ordered,
        transcriptTimedText,
        lengthArg,
    });
    if (slideSummaries.size > 0 && !titleOnlySlideSummaries) {
        const parts = [];
        if (intro)
            parts.push(intro);
        const paragraphs = splitMarkdownParagraphs(summary);
        const introParagraph = reserveIntro ? intro || paragraphs[0] || "" : "";
        const introIndex = introParagraph ? paragraphs.indexOf(introParagraph) : -1;
        const remaining = reserveIntro
            ? introIndex >= 0
                ? paragraphs.filter((_, index) => index !== introIndex)
                : paragraphs.slice(1)
            : paragraphs;
        const distributedSummaries = new Map();
        if (remaining.length > 0) {
            const distributableSlides = ordered.filter((slide) => !interludeSlideIndexes.has(slide.index));
            const distributionSlides = distributableSlides.length > 0 ? distributableSlides : ordered;
            const total = distributionSlides.length;
            let distributionIndex = 0;
            for (const slide of ordered) {
                if (interludeSlideIndexes.has(slide.index) && distributableSlides.length > 0)
                    continue;
                const segmentIndex = distributionIndex;
                const start = Math.round((segmentIndex * remaining.length) / total);
                const end = Math.round(((segmentIndex + 1) * remaining.length) / total);
                distributionIndex += 1;
                const segment = remaining.slice(start, end).join("\n\n").trim() ||
                    remaining[Math.min(remaining.length - 1, Math.floor((segmentIndex * remaining.length) / total))]?.trim() ||
                    "";
                if (segment)
                    distributedSummaries.set(slide.index, segment);
            }
        }
        for (const slide of ordered) {
            const directText = slideSummaries.get(slide.index);
            const directBody = directText
                ? splitSlideTitleFromText({
                    text: directText,
                    slideIndex: slide.index,
                    total: ordered.length,
                }).body.trim()
                : "";
            const distributedText = distributedSummaries.get(slide.index) ?? "";
            const fallbackText = slideSummaries.has(slide.index)
                ? ""
                : (fallbackSummaries.get(slide.index) ?? "");
            const directOutput = directBody || isInterludeSlideText(directText ?? "") ? directText : "";
            const text = directOutput || distributedText || fallbackText;
            const withTitle = text ? ensureSlideTitleLine({ text, slide, total: ordered.length }) : "";
            parts.push(withTitle ? `[slide:${slide.index}]\n${withTitle}` : `[slide:${slide.index}]`);
        }
        return parts.join("\n\n");
    }
    const paragraphs = splitMarkdownParagraphs(distributionMarkdown);
    if (paragraphs.length === 0)
        return markdown;
    const parts = [];
    const allOrderedSlidesAreInterludes = ordered.length > 0 && ordered.every((slide) => interludeSlideIndexes.has(slide.index));
    if (allOrderedSlidesAreInterludes) {
        if (intro)
            parts.push(intro.trim());
        for (const slide of ordered) {
            parts.push(`[slide:${slide.index}]\n## Interlude`);
        }
        return parts.join("\n\n");
    }
    const introParagraph = reserveIntro ? intro || paragraphs[0] || "" : "";
    const introIndex = introParagraph ? paragraphs.indexOf(introParagraph) : -1;
    const remaining = reserveIntro
        ? introIndex >= 0
            ? paragraphs.filter((_, index) => index !== introIndex)
            : paragraphs.slice(1)
        : paragraphs;
    if (introParagraph)
        parts.push(introParagraph.trim());
    if (remaining.length === 0) {
        for (const slide of ordered) {
            if (interludeSlideIndexes.has(slide.index)) {
                parts.push(`[slide:${slide.index}]\n## Interlude`);
                continue;
            }
            const fallback = fallbackSummaries.get(slide.index) ?? "";
            const withTitle = fallback
                ? ensureSlideTitleLine({ text: fallback, slide, total: ordered.length })
                : "";
            parts.push(withTitle ? `[slide:${slide.index}]\n${withTitle}` : `[slide:${slide.index}]`);
        }
        return parts.join("\n\n");
    }
    const distributableSlides = ordered.filter((slide) => !interludeSlideIndexes.has(slide.index));
    const distributionSlides = distributableSlides.length > 0 ? distributableSlides : ordered;
    const total = distributionSlides.length;
    const slideTotal = ordered.length;
    let distributionIndex = 0;
    for (const slide of ordered) {
        const slideIndex = slide.index;
        if (interludeSlideIndexes.has(slideIndex) && distributableSlides.length > 0) {
            parts.push(`[slide:${slideIndex}]\n## Interlude`);
            continue;
        }
        const segmentIndex = distributionIndex;
        const start = Math.round((segmentIndex * remaining.length) / total);
        const end = Math.round(((segmentIndex + 1) * remaining.length) / total);
        distributionIndex += 1;
        const segment = remaining.slice(start, end).join("\n\n").trim() ||
            remaining[Math.min(remaining.length - 1, Math.floor((segmentIndex * remaining.length) / total))]?.trim() ||
            "";
        const fallback = fallbackSummaries.get(slideIndex) ?? "";
        const text = segment || fallback;
        const withTitle = text ? ensureSlideTitleLine({ text, slide, total: slideTotal }) : "";
        parts.push(withTitle ? `[slide:${slideIndex}]\n${withTitle}` : `[slide:${slideIndex}]`);
    }
    return parts.join("\n\n");
}
//# sourceMappingURL=text-markdown.js.map