import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import path from 'node:path';
import { Box, Text } from 'ink';
import React from 'react';
import ToolMessage from '../components/tool-message.js';
import { DEFAULT_SEARCH_RESULTS, MAX_SEARCH_RESULTS } from '../constants.js';
import { ThemeContext } from '../hooks/useTheme.js';
import { jsonSchema, tool } from '../types/core.js';
import { formatError } from '../utils/error-formatter.js';
import { searchProjectContents } from '../utils/file-search.js';
import { isValidFilePath } from '../utils/path-validation.js';
import { calculateTokens } from '../utils/token-calculator.js';
const MAX_CONTEXT_LINES = 10;
const executeSearchFileContents = async (args) => {
    // Validate query
    if (!args.query || !args.query.trim()) {
        return 'Error: Search query cannot be empty';
    }
    const cwd = process.cwd();
    const maxResults = Math.min(args.maxResults || DEFAULT_SEARCH_RESULTS, MAX_SEARCH_RESULTS);
    const caseSensitive = args.caseSensitive || false;
    // Validate and resolve search path if provided
    let searchPath;
    if (args.path) {
        if (!isValidFilePath(args.path)) {
            return `Error: Invalid path "${args.path}"`;
        }
        searchPath = path.resolve(cwd, args.path);
        if (!searchPath.startsWith(path.resolve(cwd))) {
            return `Error: Path escapes project directory: ${args.path}`;
        }
    }
    try {
        const { matches, truncated } = await searchProjectContents(args.query, cwd, maxResults, caseSensitive, args.include, searchPath, args.wholeWord, Math.min(args.contextLines ?? 0, MAX_CONTEXT_LINES));
        if (matches.length === 0) {
            return `No matches found for "${args.query}"`;
        }
        // Format results with clear file:line format
        let output = `Found ${matches.length} match${matches.length === 1 ? '' : 'es'}${truncated ? ` (showing first ${maxResults})` : ''}:\n\n`;
        for (const match of matches) {
            output += `${match.file}:${match.line}\n`;
            output += `  ${match.content}\n\n`;
        }
        return output.trim();
    }
    catch (error) {
        const errorMessage = formatError(error);
        throw new Error(`Content search failed: ${errorMessage}`);
    }
};
const searchFileContentsCoreTool = tool({
    description: 'Search for text or code inside files. Use this INSTEAD OF bash grep/rg/ag/ack commands. Supports extended regex (e.g., "foo|bar", "func(tion)?"). Returns file:line with matching content. Use to find: function definitions, variable usage, import statements, TODO comments. Case-insensitive by default (use caseSensitive=true for exact matching). Use include to filter by file type (e.g., "*.ts") and path to scope to a directory (e.g., "src/components"). Use wholeWord=true for exact word boundaries. Use contextLines to see surrounding code.',
    inputSchema: jsonSchema({
        type: 'object',
        properties: {
            query: {
                type: 'string',
                description: 'Text or code to search for inside files. Supports extended regex (e.g., "foo|bar" for alternation, "func(tion)?" for optional groups). Examples: "handleSubmit", "import React", "TODO|FIXME", "export (interface|type)" (find type exports), "useState\\(" (find React hooks). Case-insensitive by default.',
            },
            maxResults: {
                type: 'number',
                description: 'Maximum number of matches to return (default: 30, max: 100)',
            },
            caseSensitive: {
                type: 'boolean',
                description: 'Whether to perform case-sensitive search (default: false)',
            },
            include: {
                type: 'string',
                description: 'Glob pattern to filter which files are searched (e.g., "*.ts", "*.{ts,tsx}", "*.spec.ts"). Only files matching this pattern will be searched.',
            },
            path: {
                type: 'string',
                description: 'Directory to scope the search to (relative path, e.g., "src/components", "source/tools"). Only files within this directory will be searched.',
            },
            wholeWord: {
                type: 'boolean',
                description: 'Match whole words only, preventing partial matches (default: false). Useful for finding exact variable/function names.',
            },
            contextLines: {
                type: 'number',
                description: 'Number of lines to show before and after each match (default: 0, max: 10). Useful for understanding surrounding code context.',
            },
        },
        required: ['query'],
    }),
    execute: async (args, _options) => {
        return await executeSearchFileContents(args);
    },
});
const SearchFileContentsFormatter = React.memo(({ args, result }) => {
    const themeContext = React.useContext(ThemeContext);
    if (!themeContext) {
        throw new Error('ThemeContext not found');
    }
    const { colors } = themeContext;
    // Parse result to get match count
    let matchCount = 0;
    if (result && !result.startsWith('Error:')) {
        const firstLine = result.split('\n')[0];
        const matchFound = firstLine.match(/Found (\d+)/);
        if (matchFound) {
            matchCount = parseInt(matchFound[1], 10);
        }
    }
    // Calculate tokens
    const tokens = result ? calculateTokens(result) : 0;
    const messageContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.tool, children: "\u2692 search_file_contents" }), _jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Query: " }), _jsx(Text, { wrap: "truncate-end", color: colors.text, children: args.query })] }), args.include && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Include: " }), _jsx(Text, { wrap: "truncate-end", color: colors.text, children: args.include })] })), args.path && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Path: " }), _jsx(Text, { wrap: "truncate-end", color: colors.text, children: args.path })] })), args.caseSensitive && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Case sensitive: " }), _jsx(Text, { color: colors.text, children: "yes" })] })), args.wholeWord && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Whole word: " }), _jsx(Text, { color: colors.text, children: "yes" })] })), args.contextLines !== undefined && args.contextLines > 0 && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Context: " }), _jsxs(Text, { color: colors.text, children: ["\u00B1", args.contextLines, " lines"] })] })), _jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Matches: " }), _jsx(Text, { color: colors.text, children: matchCount })] }), tokens > 0 && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Tokens: " }), _jsxs(Text, { color: colors.text, children: ["~", tokens.toLocaleString()] })] }))] }));
    return _jsx(ToolMessage, { message: messageContent, hideBox: true });
});
const searchFileContentsFormatter = (args, result) => {
    if (result && result.startsWith('Error:')) {
        return _jsx(_Fragment, {});
    }
    return _jsx(SearchFileContentsFormatter, { args: args, result: result });
};
export const searchFileContentsTool = {
    name: 'search_file_contents',
    tool: searchFileContentsCoreTool,
    formatter: searchFileContentsFormatter,
    readOnly: true,
};
//# sourceMappingURL=search-file-contents.js.map