/**
 * @-file Autocomplete
 *
 * Provides file path completion for @-references in user input,
 * supporting line range syntax and .gitignore filtering.
 */
import * as fs from 'fs';
import * as path from 'path';
import { logger } from '../utils/logger.js';
const MAX_RESULTS = 20;
/**
 * FileAutocomplete provides completion suggestions for @-file references.
 */
export class FileAutocomplete {
    gitignorePatterns = [];
    /**
     * Parse a .gitignore file and extract patterns.
     */
    loadGitignore(workDir) {
        this.gitignorePatterns = [];
        const gitignorePath = path.join(workDir, '.gitignore');
        try {
            const content = fs.readFileSync(gitignorePath, 'utf-8');
            this.gitignorePatterns = content
                .split('\n')
                .map((line) => line.trim())
                .filter((line) => line && !line.startsWith('#'));
        }
        catch {
            // No .gitignore or unreadable
        }
    }
    /**
     * Check if a relative path matches any gitignore pattern.
     */
    isIgnored(relativePath) {
        for (const pattern of this.gitignorePatterns) {
            const cleanPattern = pattern.replace(/\/$/, '');
            // Simple matching: exact name match or directory prefix
            const basename = path.basename(relativePath);
            if (basename === cleanPattern)
                return true;
            if (relativePath.startsWith(cleanPattern + '/'))
                return true;
            if (relativePath === cleanPattern)
                return true;
            // Glob-style: pattern with * (simple wildcard)
            if (cleanPattern.includes('*')) {
                const regex = new RegExp('^' + cleanPattern.replace(/\./g, '\\.').replace(/\*/g, '.*') + '$');
                if (regex.test(basename) || regex.test(relativePath))
                    return true;
            }
        }
        return false;
    }
    /**
     * Complete a partial path, returning matching files and directories.
     */
    complete(partial, workDir) {
        this.loadGitignore(workDir);
        // Strip line range suffix for path matching
        const colonIdx = partial.indexOf(':');
        let pathPart = partial;
        let lineRange;
        if (colonIdx !== -1) {
            pathPart = partial.slice(0, colonIdx);
            lineRange = partial.slice(colonIdx + 1);
        }
        const fullPath = path.resolve(workDir, pathPart);
        const results = [];
        // Determine directory to scan and prefix to match
        let dirToScan;
        let prefix;
        try {
            const stat = fs.statSync(fullPath);
            if (stat.isDirectory()) {
                dirToScan = fullPath;
                prefix = '';
            }
            else {
                dirToScan = path.dirname(fullPath);
                prefix = path.basename(fullPath);
            }
        }
        catch {
            dirToScan = path.dirname(fullPath);
            prefix = path.basename(fullPath);
        }
        try {
            const entries = fs.readdirSync(dirToScan, { withFileTypes: true });
            for (const entry of entries) {
                if (results.length >= MAX_RESULTS)
                    break;
                if (prefix && !entry.name.startsWith(prefix))
                    continue;
                const entryRelative = path.relative(workDir, path.join(dirToScan, entry.name));
                if (this.isIgnored(entryRelative))
                    continue;
                const isDir = entry.isDirectory();
                const displayPath = entryRelative + (isDir ? '/' : '');
                results.push({
                    path: entryRelative,
                    display: displayPath + (lineRange ? ':' + lineRange : ''),
                    isDirectory: isDir,
                    lineRange,
                });
            }
        }
        catch {
            logger.debug(`[FileAutocomplete] Cannot read directory: ${dirToScan}`);
        }
        return results;
    }
    /**
     * Parse an @-reference string into a path and optional line range.
     * Returns null if the input is not a valid @-reference.
     */
    parseAtReference(input) {
        if (!input || !input.startsWith('@')) {
            return null;
        }
        const ref = input.slice(1); // Remove @
        if (!ref)
            return null;
        const colonIdx = ref.indexOf(':');
        if (colonIdx === -1) {
            return { path: ref };
        }
        const filePath = ref.slice(0, colonIdx);
        const rangeStr = ref.slice(colonIdx + 1);
        if (!filePath)
            return null;
        const rangeMatch = rangeStr.match(/^(\d+)(?:-(\d+))?$/);
        if (!rangeMatch) {
            // Colon but no valid range - still return path
            return { path: filePath };
        }
        const lineStart = parseInt(rangeMatch[1], 10);
        const lineEnd = rangeMatch[2] ? parseInt(rangeMatch[2], 10) : undefined;
        return { path: filePath, lineStart, lineEnd };
    }
}
// Singleton
let instance = null;
export function getFileAutocomplete() {
    if (!instance) {
        instance = new FileAutocomplete();
    }
    return instance;
}
export function resetFileAutocomplete() {
    instance = null;
}
//# sourceMappingURL=file-autocomplete.js.map