/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */
import path from 'node:path';
import { makeRelative, shortenPath } from '../utils/paths.js';
import { BaseDeclarativeTool, BaseToolInvocation, Kind, } from './tools.js';
import { ToolErrorType } from './tool-error.js';
import { buildFilePathArgsPattern } from '../policy/utils.js';
import { processSingleFileContent, getSpecificMimeType, } from '../utils/fileUtils.js';
import { FileOperation } from '../telemetry/metrics.js';
import { getProgrammingLanguage } from '../telemetry/telemetry-utils.js';
import { logFileOperation } from '../telemetry/loggers.js';
import { FileOperationEvent } from '../telemetry/types.js';
import { READ_FILE_TOOL_NAME, READ_FILE_DISPLAY_NAME } from './tool-names.js';
import { FileDiscoveryService } from '../services/fileDiscoveryService.js';
import { READ_FILE_DEFINITION } from './definitions/coreTools.js';
import { resolveToolDeclaration } from './definitions/resolver.js';
import { discoverJitContext, appendJitContext, appendJitContextToParts, } from './jit-context.js';
class ReadFileToolInvocation extends BaseToolInvocation {
    config;
    resolvedPath;
    constructor(config, params, messageBus, _toolName, _toolDisplayName) {
        super(params, messageBus, _toolName, _toolDisplayName);
        this.config = config;
        this.resolvedPath = path.resolve(this.config.getTargetDir(), this.params.file_path);
    }
    getDescription() {
        const relativePath = makeRelative(this.resolvedPath, this.config.getTargetDir());
        return shortenPath(relativePath);
    }
    toolLocations() {
        return [
            {
                path: this.resolvedPath,
                line: this.params.start_line,
            },
        ];
    }
    getPolicyUpdateOptions(_outcome) {
        return {
            argsPattern: buildFilePathArgsPattern(this.params.file_path),
        };
    }
    async execute() {
        const validationError = this.config.validatePathAccess(this.resolvedPath, 'read');
        if (validationError) {
            return {
                llmContent: validationError,
                returnDisplay: 'Path not in workspace.',
                error: {
                    message: validationError,
                    type: ToolErrorType.PATH_NOT_IN_WORKSPACE,
                },
            };
        }
        const result = await processSingleFileContent(this.resolvedPath, this.config.getTargetDir(), this.config.getFileSystemService(), this.params.start_line, this.params.end_line);
        if (result.error) {
            return {
                llmContent: result.llmContent,
                returnDisplay: result.returnDisplay || 'Error reading file',
                error: {
                    message: result.error,
                    type: result.errorType,
                },
            };
        }
        let llmContent;
        if (result.isTruncated) {
            const [start, end] = result.linesShown;
            const total = result.originalLineCount;
            llmContent = `
IMPORTANT: The file content has been truncated.
Status: Showing lines ${start}-${end} of ${total} total lines.
Action: To read more of the file, you can use the 'start_line' and 'end_line' parameters in a subsequent 'read_file' call. For example, to read the next section of the file, use start_line: ${end + 1}.

--- FILE CONTENT (truncated) ---
${result.llmContent}`;
        }
        else {
            llmContent = result.llmContent || '';
        }
        const lines = typeof result.llmContent === 'string'
            ? result.llmContent.split('\n').length
            : undefined;
        const mimetype = getSpecificMimeType(this.resolvedPath);
        const programming_language = getProgrammingLanguage({
            file_path: this.resolvedPath,
        });
        logFileOperation(this.config, new FileOperationEvent(READ_FILE_TOOL_NAME, FileOperation.READ, lines, mimetype, path.extname(this.resolvedPath), programming_language));
        // Discover JIT subdirectory context for the accessed file path
        const jitContext = await discoverJitContext(this.config, this.resolvedPath);
        if (jitContext) {
            if (typeof llmContent === 'string') {
                llmContent = appendJitContext(llmContent, jitContext);
            }
            else {
                llmContent = appendJitContextToParts(llmContent, jitContext);
            }
        }
        return {
            llmContent,
            returnDisplay: result.returnDisplay || '',
        };
    }
}
/**
 * Implementation of the ReadFile tool logic
 */
export class ReadFileTool extends BaseDeclarativeTool {
    config;
    static Name = READ_FILE_TOOL_NAME;
    fileDiscoveryService;
    constructor(config, messageBus) {
        super(ReadFileTool.Name, READ_FILE_DISPLAY_NAME, READ_FILE_DEFINITION.base.description, Kind.Read, READ_FILE_DEFINITION.base.parametersJsonSchema, messageBus, true, false);
        this.config = config;
        this.fileDiscoveryService = new FileDiscoveryService(config.getTargetDir(), config.getFileFilteringOptions());
    }
    validateToolParamValues(params) {
        if (params.file_path.trim() === '') {
            return "The 'file_path' parameter must be non-empty.";
        }
        const resolvedPath = path.resolve(this.config.getTargetDir(), params.file_path);
        const validationError = this.config.validatePathAccess(resolvedPath, 'read');
        if (validationError) {
            return validationError;
        }
        if (params.start_line !== undefined && params.start_line < 1) {
            return 'start_line must be at least 1';
        }
        if (params.end_line !== undefined && params.end_line < 1) {
            return 'end_line must be at least 1';
        }
        if (params.start_line !== undefined &&
            params.end_line !== undefined &&
            params.start_line > params.end_line) {
            return 'start_line cannot be greater than end_line';
        }
        const fileFilteringOptions = this.config.getFileFilteringOptions();
        if (this.fileDiscoveryService.shouldIgnoreFile(resolvedPath, fileFilteringOptions)) {
            return `File path '${resolvedPath}' is ignored by configured ignore patterns.`;
        }
        return null;
    }
    createInvocation(params, messageBus, _toolName, _toolDisplayName) {
        return new ReadFileToolInvocation(this.config, params, messageBus, _toolName, _toolDisplayName);
    }
    getSchema(modelId) {
        return resolveToolDeclaration(READ_FILE_DEFINITION, modelId);
    }
}
//# sourceMappingURL=read-file.js.map