/*
 * Copyright 2026, Jaime Terrats.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
// ─── Public API ─────────────────────────────────────────────────────────────
export function getDefaultConfig() {
    return {
        enabled: true,
        maxLinesPerFile: 150,
        maxTotalContextLines: 500,
        strategies: ['schema-extract', 'section-extract', 'truncate'],
        headLines: 60,
        tailLines: 30,
    };
}
export function preprocessContext(files, config) {
    if (!config.enabled) {
        return files.map((f) => {
            const lines = countLines(f.content);
            return {
                path: f.path,
                content: f.content,
                originalLines: lines,
                finalLines: lines,
                strategy: 'passthrough',
                annotation: null,
            };
        });
    }
    // Separate small vs large files based on per-file threshold
    const classified = files.map((f) => ({
        file: f,
        lineCount: countLines(f.content),
    }));
    const smallFiles = classified.filter((c) => c.lineCount <= config.maxLinesPerFile);
    const largeFiles = classified.filter((c) => c.lineCount > config.maxLinesPerFile);
    // Budget distribution
    const usedBySmall = smallFiles.reduce((sum, c) => sum + c.lineCount, 0);
    const remainingBudget = Math.max(0, config.maxTotalContextLines - usedBySmall);
    const perLargeFile = largeFiles.length > 0 ? Math.max(Math.floor(remainingBudget / largeFiles.length), 40) : 0;
    const results = [];
    // Process small files — passthrough
    for (const { file, lineCount } of smallFiles) {
        const content = prependVaultMetadata(file);
        results.push({
            path: file.path,
            content,
            originalLines: lineCount,
            finalLines: countLines(content),
            strategy: 'passthrough',
            annotation: null,
        });
    }
    // Process large files — apply strategies
    for (const { file, lineCount } of largeFiles) {
        const baseContent = prependVaultMetadata(file);
        const budget = perLargeFile;
        let reduced = null;
        let strategy = 'truncate';
        // Try strategies in order
        for (const strat of config.strategies) {
            if (strat === 'schema-extract') {
                reduced = applySchemaExtraction(baseContent, budget);
                if (reduced !== null) {
                    strategy = 'schema-extract';
                    break;
                }
            }
            else if (strat === 'section-extract') {
                reduced = applySectionExtraction(baseContent, budget);
                if (reduced !== null) {
                    strategy = 'section-extract';
                    break;
                }
            }
            else if (strat === 'truncate') {
                reduced = applyTruncation(baseContent, config.headLines, config.tailLines, file.path);
                strategy = 'truncate';
                break;
            }
        }
        // Fallback to truncation if no strategy succeeded
        if (reduced === null) {
            reduced = applyTruncation(baseContent, config.headLines, config.tailLines, file.path);
            strategy = 'truncate';
        }
        const finalLines = countLines(reduced);
        const annotation = `[REDUCED: original ${lineCount} lines → ${finalLines} lines | strategy: ${strategy} | Full file: ${file.path}]`;
        results.push({
            path: file.path,
            content: reduced,
            originalLines: lineCount,
            finalLines,
            strategy,
            annotation,
        });
    }
    return results;
}
// ─── Internal strategy functions ────────────────────────────────────────────
function applySchemaExtraction(content, maxLines) {
    const lines = content.split('\n');
    // Detect structured patterns
    const hasYamlFrontmatter = lines[0] === '---' && lines.slice(1).some((l) => l === '---');
    const hasObjectTable = lines.some((l) => /\|\s*Field\s*\|\s*Type\s*\|/i.test(l));
    const hasErdMarkers = lines.some((l) => /[→←]|1:N|M:N|1:1/i.test(l));
    const hasJsonSchema = content.includes('"$schema"') || content.includes('"properties"');
    if (!hasYamlFrontmatter && !hasObjectTable && !hasErdMarkers && !hasJsonSchema) {
        return null;
    }
    const extracted = [];
    if (hasYamlFrontmatter)
        extractYamlFrontmatter(lines, extracted);
    if (hasObjectTable)
        extractTableRows(lines, extracted);
    if (hasErdMarkers)
        extractErdLines(lines, extracted);
    if (hasJsonSchema && !hasObjectTable && !hasErdMarkers)
        extractJsonSchemaKeys(lines, extracted);
    if (extracted.length === 0)
        return null;
    return extracted.slice(0, maxLines).join('\n');
}
function extractYamlFrontmatter(lines, out) {
    const endIdx = lines.indexOf('---', 1);
    if (endIdx > 0) {
        out.push(...lines.slice(0, endIdx + 1));
    }
}
function extractTableRows(lines, out) {
    for (const line of lines) {
        if (line.startsWith('|') && line.endsWith('|')) {
            out.push(line);
        }
    }
}
function extractErdLines(lines, out) {
    for (const line of lines) {
        if (/[→←]|1:N|M:N|1:1/i.test(line)) {
            out.push(line);
        }
    }
}
function extractJsonSchemaKeys(lines, out) {
    for (const line of lines) {
        if (/"(properties|type|required|items|\$schema)"/.test(line)) {
            out.push(line);
        }
    }
}
function applySectionExtraction(content, maxLines) {
    const lines = content.split('\n');
    // Find all markdown headings
    const headings = [];
    for (let i = 0; i < lines.length; i++) {
        const match = /^(#{1,6})\s+(.*)$/.exec(lines[i]);
        if (match) {
            headings.push({ index: i, level: match[1].length, text: lines[i] });
        }
    }
    if (headings.length < 3)
        return null;
    // Extract headings + first non-empty line under each
    const extracted = [];
    for (const heading of headings) {
        extracted.push(heading.text);
        // Find first non-empty line after heading
        for (let j = heading.index + 1; j < lines.length; j++) {
            const line = lines[j].trim();
            if (line === '')
                continue;
            if (line.startsWith('#'))
                break; // next heading
            extracted.push(lines[j]);
            break;
        }
    }
    // If over budget, drop deeper headings (### before ##)
    if (extracted.length > maxLines) {
        // Sort headings by level descending — remove deeper ones first
        const sortedHeadings = [...headings].sort((a, b) => b.level - a.level);
        const dropSet = new Set();
        let remaining = extracted.length;
        for (const h of sortedHeadings) {
            if (remaining <= maxLines)
                break;
            dropSet.add(h.text);
            remaining -= 2; // heading + its first line
        }
        const filtered = extracted.filter((line) => !dropSet.has(line));
        return filtered.slice(0, maxLines).join('\n');
    }
    return extracted.slice(0, maxLines).join('\n');
}
function applyTruncation(content, headLines, tailLines, path) {
    const lines = content.split('\n');
    const totalLines = lines.length;
    if (totalLines <= headLines + tailLines) {
        return content;
    }
    const head = lines.slice(0, headLines);
    const tail = lines.slice(-tailLines);
    const omitted = totalLines - headLines - tailLines;
    return [...head, `\n... [${omitted} lines omitted — full file: ${path}] ...\n`, ...tail].join('\n');
}
// ─── Helpers ────────────────────────────────────────────────────────────────
function countLines(content) {
    if (content === '')
        return 0;
    return content.split('\n').length;
}
function prependVaultMetadata(file) {
    if (file.vaultEntry?.description) {
        const tags = file.vaultEntry.tags.length > 0 ? `[${file.vaultEntry.tags.join(', ')}] ` : '';
        const vaultLine = `<!-- vault: ${tags}${file.vaultEntry.description} -->`;
        return `${vaultLine}\n${file.content}`;
    }
    return file.content;
}
//# sourceMappingURL=context-preprocessor.js.map