/**
 * File Sampler
 *
 * Intelligently selects representative files from a repo for LLM analysis.
 * Prioritizes high-value files while respecting size limits.
 */
import * as fs from 'fs';
import * as path from 'path';
import { glob } from 'glob';
const DEFAULT_CONFIG = {
    maxFiles: 20,
    maxLinesPerFile: 500,
    maxTotalChars: 100000, // ~100KB total
};
/**
 * Priority file patterns - checked in order
 */
const PRIORITY_PATTERNS = [
    // Tier 1: Always include if exists
    { pattern: 'README.md', type: 'readme', priority: 1 },
    { pattern: 'readme.md', type: 'readme', priority: 1 },
    { pattern: 'package.json', type: 'config', priority: 1 },
    { pattern: 'tsconfig.json', type: 'config', priority: 1 },
    { pattern: 'pyproject.toml', type: 'config', priority: 1 },
    { pattern: 'go.mod', type: 'config', priority: 1 },
    { pattern: 'Cargo.toml', type: 'config', priority: 1 },
    { pattern: 'pom.xml', type: 'config', priority: 1 },
    // Tier 2: Entry points
    { pattern: 'src/index.ts', type: 'entry', priority: 2 },
    { pattern: 'src/main.ts', type: 'entry', priority: 2 },
    { pattern: 'src/app.ts', type: 'entry', priority: 2 },
    { pattern: 'src/server.ts', type: 'entry', priority: 2 },
    { pattern: 'index.ts', type: 'entry', priority: 2 },
    { pattern: 'main.ts', type: 'entry', priority: 2 },
    { pattern: 'app.ts', type: 'entry', priority: 2 },
    { pattern: 'main.py', type: 'entry', priority: 2 },
    { pattern: 'app.py', type: 'entry', priority: 2 },
    { pattern: 'main.go', type: 'entry', priority: 2 },
    { pattern: 'cmd/main.go', type: 'entry', priority: 2 },
    // Tier 3: API surfaces (glob patterns)
    { pattern: 'src/routes/**/*.ts', type: 'api', priority: 3 },
    { pattern: 'src/api/**/*.ts', type: 'api', priority: 3 },
    { pattern: 'src/controllers/**/*.ts', type: 'api', priority: 3 },
    { pattern: 'src/handlers/**/*.ts', type: 'api', priority: 3 },
    { pattern: 'routes/**/*.ts', type: 'api', priority: 3 },
    { pattern: 'api/**/*.ts', type: 'api', priority: 3 },
    // Tier 4: Domain models
    { pattern: 'src/models/**/*.ts', type: 'model', priority: 4 },
    { pattern: 'src/types/**/*.ts', type: 'model', priority: 4 },
    { pattern: 'src/entities/**/*.ts', type: 'model', priority: 4 },
    { pattern: 'src/schemas/**/*.ts', type: 'model', priority: 4 },
    { pattern: 'src/domain/**/*.ts', type: 'model', priority: 4 },
    { pattern: 'models/**/*.ts', type: 'model', priority: 4 },
    { pattern: 'types/**/*.ts', type: 'model', priority: 4 },
];
/**
 * Sample files from a repo for LLM analysis
 */
export async function sampleRepoFiles(repoPath, config = DEFAULT_CONFIG) {
    const samples = [];
    const includedPaths = new Set();
    let totalChars = 0;
    // Phase 1: Include priority files
    for (const { pattern, type, priority } of PRIORITY_PATTERNS) {
        if (samples.length >= config.maxFiles)
            break;
        if (totalChars >= config.maxTotalChars)
            break;
        try {
            const matches = await glob(pattern, {
                cwd: repoPath,
                nodir: true,
                ignore: ['**/node_modules/**', '**/dist/**', '**/build/**', '**/.git/**'],
            });
            for (const match of matches.slice(0, priority === 1 ? 5 : 2)) {
                if (includedPaths.has(match))
                    continue;
                if (samples.length >= config.maxFiles)
                    break;
                const sample = await readFileSample(repoPath, match, type, config.maxLinesPerFile);
                if (sample && totalChars + sample.content.length <= config.maxTotalChars) {
                    samples.push(sample);
                    includedPaths.add(match);
                    totalChars += sample.content.length;
                }
            }
        }
        catch {
            // Pattern didn't match, continue
        }
    }
    // Phase 2: Fill remaining slots with other interesting files
    if (samples.length < config.maxFiles && totalChars < config.maxTotalChars) {
        try {
            const allFiles = await glob('**/*.{ts,js,tsx,jsx,py,go,java,rs}', {
                cwd: repoPath,
                nodir: true,
                ignore: [
                    '**/node_modules/**',
                    '**/dist/**',
                    '**/build/**',
                    '**/.git/**',
                    '**/*.test.*',
                    '**/*.spec.*',
                    '**/__tests__/**',
                ],
            });
            // Sort by likely importance (shorter paths, src/ prefix)
            const sortedFiles = allFiles
                .filter(f => !includedPaths.has(f))
                .sort((a, b) => {
                const aScore = getFileImportanceScore(a);
                const bScore = getFileImportanceScore(b);
                return bScore - aScore;
            });
            for (const file of sortedFiles) {
                if (samples.length >= config.maxFiles)
                    break;
                if (totalChars >= config.maxTotalChars)
                    break;
                const sample = await readFileSample(repoPath, file, 'other', config.maxLinesPerFile);
                if (sample && totalChars + sample.content.length <= config.maxTotalChars) {
                    samples.push(sample);
                    includedPaths.add(file);
                    totalChars += sample.content.length;
                }
            }
        }
        catch {
            // Glob failed, continue with what we have
        }
    }
    return samples;
}
/**
 * Read a file and create a sample
 */
async function readFileSample(repoPath, relativePath, type, maxLines) {
    const fullPath = path.join(repoPath, relativePath);
    try {
        const stats = fs.statSync(fullPath);
        if (stats.size > 500000)
            return null; // Skip files > 500KB
        let content = fs.readFileSync(fullPath, 'utf-8');
        let truncated = false;
        const lines = content.split('\n');
        if (lines.length > maxLines) {
            content = lines.slice(0, maxLines).join('\n');
            content += `\n\n// ... truncated (${lines.length - maxLines} more lines)`;
            truncated = true;
        }
        return {
            path: relativePath,
            content,
            type,
            truncated,
        };
    }
    catch {
        return null;
    }
}
/**
 * Score file importance for sorting
 */
function getFileImportanceScore(filePath) {
    let score = 0;
    // Prefer shorter paths (closer to root)
    score += Math.max(0, 10 - filePath.split('/').length);
    // Prefer src/ files
    if (filePath.startsWith('src/'))
        score += 5;
    // Prefer index files
    if (filePath.includes('index.'))
        score += 3;
    // Prefer files with meaningful names
    const name = path.basename(filePath, path.extname(filePath));
    if (['app', 'main', 'server', 'client', 'api', 'service', 'controller'].includes(name)) {
        score += 4;
    }
    // Deprioritize utility files
    if (['utils', 'helpers', 'constants', 'config'].includes(name)) {
        score -= 2;
    }
    return score;
}
/**
 * Format samples for LLM prompt
 */
export function formatSamplesForPrompt(samples) {
    const sections = [];
    for (const sample of samples) {
        const header = `=== ${sample.path} (${sample.type}) ===`;
        sections.push(`${header}\n${sample.content}`);
    }
    return sections.join('\n\n');
}
//# sourceMappingURL=file-sampler.js.map