import { READ_TOOL_SCHEMA } from './schema.js';
import { toRecord } from '../../utils/record-coerce.js';
import { FileStateCache } from '../../state/file-cache.js';
import { ProjectIndex } from '../../state/project-index.js';
import { CodeIntelligence } from '../../intelligence/facade.js';
import { logger } from '../../utils/logger.js';
import { summarizeError } from '../../utils/error-display.js';
import { paginateFiles, readOneFile, } from './file-readers.js';
import { mapWithConcurrency } from '../../utils/concurrency.js';
const MAX_READ_FILES = 50;
const MAX_PARALLEL_READ_FILES = 8;
export class ReadTool {
    definition = {
        name: 'read',
        description: 'Read one or more files from disk. Supports extract modes (content, outline, symbols, lines, ast)'
            + ' for token-efficient reading, per-file caching, pagination via token_budget, and batch processing.',
        parameters: toRecord(READ_TOOL_SCHEMA),
        sideEffects: ['read_fs'],
        concurrency: 'parallel',
        supportsProgress: true,
    };
    fileCache;
    projectIndex;
    codeIntelligence;
    constructor(projectIndex, fileCache, codeIntelligence) {
        this.fileCache = fileCache ?? new FileStateCache();
        this.projectIndex = projectIndex;
        this.codeIntelligence = codeIntelligence ?? new CodeIntelligence({});
    }
    async execute(args) {
        if (!Array.isArray(args.files) || args.files.length === 0) {
            return { success: false, error: 'Missing or empty "files" array' };
        }
        if (args.files.length > MAX_READ_FILES) {
            return { success: false, error: `Too many files: maximum ${MAX_READ_FILES} per read call` };
        }
        try {
            return await this._execute(args);
        }
        catch (err) {
            const message = summarizeError(err);
            logger.error('read tool: unexpected error', { error: message });
            return { success: false, error: `Unexpected error: ${message}` };
        }
    }
    async _execute(input) {
        const globalExtract = input.extract ?? 'content';
        const format = input.output?.format ?? 'standard';
        const includeLineNumbers = input.output?.include_line_numbers ?? true;
        const maxPerItem = input.output?.max_per_item;
        const maxTokens = input.output?.max_tokens;
        const tokenBudget = input.token_budget;
        const page = Math.max(1, input.page ?? 1);
        const globalImageMode = input.image_mode;
        const globalMaxImageSize = input.max_image_size;
        const allFiles = input.files;
        let filesToProcess = allFiles;
        let paginationInfo;
        if (tokenBudget !== undefined) {
            const pages = paginateFiles(allFiles, tokenBudget, this.projectIndex.baseDir);
            const totalPages = Math.max(1, pages.length);
            const pageIdx = Math.min(page - 1, totalPages - 1);
            const pageIndices = pages[pageIdx] ?? [];
            filesToProcess = pageIndices.map((i) => allFiles[i]).filter((f) => f !== undefined);
            const deliveredSet = new Set(pages.slice(0, pageIdx + 1).flat());
            const pendingFiles = allFiles
                .map((f, i) => ({ f, i }))
                .filter(({ i }) => !deliveredSet.has(i))
                .map(({ f }) => f.path);
            paginationInfo = { page, total_pages: totalPages, pending_files: pendingFiles };
        }
        const results = await mapWithConcurrency(filesToProcess, MAX_PARALLEL_READ_FILES, (f) => readOneFile(f, globalExtract, format, includeLineNumbers, maxPerItem, this.fileCache, this.projectIndex, globalImageMode, globalMaxImageSize, this.codeIntelligence));
        if (maxTokens !== undefined) {
            let usedTokens = 0;
            for (const r of results) {
                if (r.content === undefined)
                    continue;
                const contentTokens = Math.ceil(r.content.length / 4);
                if (usedTokens + contentTokens > maxTokens) {
                    const remaining = Math.max(0, maxTokens - usedTokens);
                    r.content = r.content.slice(0, remaining * 4);
                }
                usedTokens += Math.ceil((r.content?.length ?? 0) / 4);
            }
        }
        const images = [];
        const fileResults = results.map((r) => {
            if (r.imageData) {
                images.push({
                    path: r.path,
                    base64: r.imageData.base64,
                    mediaType: r.imageData.mediaType,
                    description: r.content ?? `Image: ${r.path}`,
                });
                const { imageData: _imageData, ...rest } = r;
                return rest;
            }
            return r;
        });
        const filesBinary = fileResults.filter((r) => r.binary === true).length;
        const filesErrored = fileResults.filter((r) => r.error !== undefined && !r.binary).length;
        const filesRead = fileResults.length - filesBinary - filesErrored;
        const totalLines = fileResults.reduce((s, r) => s + r.lineCount, 0);
        const totalTokens = fileResults.reduce((s, r) => s + r.tokenEstimate, 0);
        const output = {
            success: true,
            summary: {
                files_read: filesRead,
                files_binary: filesBinary,
                files_errored: filesErrored,
                total_lines: totalLines,
                total_tokens: totalTokens,
            },
            ...(images.length > 0 ? { images } : {}),
        };
        if (format === 'count_only') {
            // summary only
        }
        else if (format === 'minimal') {
            output.files = fileResults.map((r) => ({
                path: r.path,
                resolvedPath: r.resolvedPath,
                lineCount: r.lineCount,
                byteSize: r.byteSize,
                tokenEstimate: r.tokenEstimate,
                extract: r.extract,
                binary: r.binary,
                error: r.error,
                cache: r.cache,
            }));
        }
        else if (format === 'standard' || format === 'verbose') {
            output.files = fileResults;
        }
        if (paginationInfo) {
            output.pagination = paginationInfo;
        }
        return { success: true, output: JSON.stringify(output) };
    }
}
