import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
import { lstat, readdir } from 'node:fs/promises';
import { join } from 'node:path';
import { Box, Text } from 'ink';
import React from 'react';
import ToolMessage from '../components/tool-message.js';
import { ThemeContext } from '../hooks/useTheme.js';
import { jsonSchema, tool } from '../types/core.js';
import { loadGitignore } from '../utils/gitignore-loader.js';
import { isValidFilePath, resolveFilePath } from '../utils/path-validation.js';
import { calculateTokens } from '../utils/token-calculator.js';
const executeListDirectory = async (args) => {
    const dirPath = args.path || '.';
    const recursive = args.recursive ?? false;
    const maxDepth = args.maxDepth ?? 3;
    const tree = args.tree ?? false;
    const showHiddenFiles = args.showHiddenFiles ?? false;
    // Validate path
    if (!isValidFilePath(dirPath)) {
        throw new Error(`Invalid path: "${dirPath}". Path must be relative and within the project directory.`);
    }
    const cwd = process.cwd();
    const resolvedPath = resolveFilePath(dirPath, cwd);
    const ig = loadGitignore(cwd);
    try {
        const entries = [];
        const walkDirectory = async (currentPath, relativeTo, depth) => {
            if (depth > maxDepth)
                return;
            try {
                const items = await readdir(currentPath, { withFileTypes: true });
                for (const item of items) {
                    // Skip hidden files unless showHiddenFiles is true
                    if (!showHiddenFiles &&
                        item.name.startsWith('.') &&
                        !dirPath.startsWith('.')) {
                        continue;
                    }
                    // Check if this item should be ignored using gitignore patterns
                    const itemPath = relativeTo ? join(relativeTo, item.name) : item.name;
                    if (ig.ignores(itemPath)) {
                        continue;
                    }
                    let type = 'file';
                    if (item.isSymbolicLink()) {
                        type = 'symlink';
                    }
                    else if (item.isDirectory()) {
                        type = 'directory';
                    }
                    const fullPath = join(currentPath, item.name);
                    const relativePath = join(relativeTo, item.name);
                    // Only get stats for files (to get size)
                    let size;
                    if (type === 'file') {
                        try {
                            const stats = await lstat(fullPath);
                            size = stats.size;
                        }
                        catch {
                            // Skip files we can't stat
                            size = undefined;
                        }
                    }
                    entries.push({
                        name: item.name,
                        relativePath,
                        type,
                        size,
                    });
                    // Recurse into directories if enabled
                    if (recursive && item.isDirectory() && depth < maxDepth) {
                        await walkDirectory(fullPath, relativePath, depth + 1);
                    }
                }
            }
            catch (error) {
                if (error instanceof Error &&
                    'code' in error &&
                    error.code === 'EACCES') {
                    // Skip directories we can't read
                    return;
                }
                throw error;
            }
        };
        await walkDirectory(resolvedPath, '', 0);
        if (entries.length === 0) {
            return `Directory "${dirPath}" is empty`;
        }
        // Sort directories first, then files, alphabetically
        entries.sort((a, b) => {
            if (a.type === 'directory' && b.type !== 'directory')
                return -1;
            if (a.type !== 'directory' && b.type === 'directory')
                return 1;
            return a.relativePath.localeCompare(b.relativePath);
        });
        // Format output
        let output = `Directory contents for "${dirPath}":\n\n`;
        if (tree) {
            // Tree format: flat paths, one per line
            for (const entry of entries) {
                output += `${entry.relativePath}\n`;
            }
        }
        else {
            // Standard format with icons
            for (const entry of entries) {
                const icon = entry.type === 'directory'
                    ? '📁 '
                    : entry.type === 'symlink'
                        ? '🔗 '
                        : '📄 ';
                const displayPath = recursive ? entry.relativePath : entry.name;
                const sizeStr = entry.size
                    ? ` (${entry.size.toLocaleString()} bytes)`
                    : '';
                output += `${icon}${displayPath}${sizeStr}\n`;
            }
        }
        if (recursive && entries.length > 0) {
            output += `\n[Recursive: showing entries up to depth ${maxDepth}]`;
        }
        if (tree) {
            output += `\n[Tree format: flat paths]`;
        }
        return output;
    }
    catch (error) {
        if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
            throw new Error(`Directory "${dirPath}" does not exist`);
        }
        const errorMessage = error instanceof Error ? error.message : 'Unknown error';
        throw new Error(`Failed to list directory: ${errorMessage}`);
    }
};
const listDirectoryCoreTool = tool({
    description: 'List directory contents with file sizes. AUTO-ACCEPTED (no user approval needed). Use this INSTEAD OF bash ls/ls -la/ls -R commands. Use recursive=true with maxDepth for nested exploration. Use tree=true for flat paths (easier to parse). Best for: exploring unknown directories, seeing file sizes, understanding project structure. For finding specific files by pattern, use find_files instead.',
    inputSchema: jsonSchema({
        type: 'object',
        properties: {
            path: {
                type: 'string',
                description: 'Directory path to list (default: "." current directory). Examples: ".", "src", "source/tools"',
            },
            recursive: {
                type: 'boolean',
                description: 'If true, recursively list subdirectories (default: false)',
            },
            maxDepth: {
                type: 'number',
                description: 'Maximum recursion depth when recursive=true (default: 3, min: 1, max: 10)',
            },
            tree: {
                type: 'boolean',
                description: 'If true, show flat paths output (one per line) instead of formatted tree. Great for LLM to see project structure.',
            },
            showHiddenFiles: {
                type: 'boolean',
                description: 'If true, include hidden files and directories (default: false). Use with caution to avoid exposing sensitive files like .env.',
            },
        },
        required: [],
    }),
    // Low risk: read-only operation, never requires approval
    needsApproval: false,
    execute: async (args, _options) => {
        return await executeListDirectory(args);
    },
});
const ListDirectoryFormatter = React.memo(({ args, result, tokens }) => {
    const themeContext = React.useContext(ThemeContext);
    if (!themeContext) {
        throw new Error('ThemeContext not found');
    }
    const { colors } = themeContext;
    // Parse result to extract entry count
    let entryCount = 0;
    if (result &&
        !result.startsWith('Error:') &&
        !result.includes('is empty')) {
        const lines = result.split('\n');
        for (const line of lines) {
            // Count lines with emojis (standard format) or paths (tree format)
            if (line.match(/^[📁🔗📄]/) ||
                (args.tree &&
                    line.trim() &&
                    !line.startsWith('[') &&
                    !line.startsWith('Directory'))) {
                entryCount++;
            }
        }
    }
    const messageContent = (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: colors.tool, children: "\u2692 list_directory" }), _jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Path: " }), _jsx(Text, { color: colors.text, children: args.path || '.' })] }), entryCount > 0 && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Entries: " }), _jsx(Text, { color: colors.text, children: entryCount })] })), args.recursive && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Recursive: " }), _jsxs(Text, { color: colors.text, children: ["yes (max depth: ", args.maxDepth ?? 3, ")"] })] })), args.tree && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Format: " }), _jsx(Text, { color: colors.text, children: "tree" })] })), args.showHiddenFiles && (_jsxs(Box, { children: [_jsx(Text, { color: colors.secondary, children: "Hidden files: " }), _jsx(Text, { color: colors.text, children: "shown" })] })), tokens !== undefined && 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 listDirectoryFormatter = (args, result) => {
    if (result && result.startsWith('Error:')) {
        return _jsx(_Fragment, {});
    }
    // Calculate tokens from the result
    let tokens = 0;
    if (result) {
        tokens = calculateTokens(result);
    }
    return _jsx(ListDirectoryFormatter, { args: args, result: result, tokens: tokens });
};
export const listDirectoryTool = {
    name: 'list_directory',
    tool: listDirectoryCoreTool,
    formatter: listDirectoryFormatter,
};
//# sourceMappingURL=list-directory.js.map