package com.example.demo.tools;

import com.example.demo.config.AppProperties;
import com.example.demo.utils.PathUtils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 文件操作工具类 - 使用Spring AI 1.0.0 @Tool注解
 */
@Component
public class FileOperationTools {

    private static final Logger logger = LoggerFactory.getLogger(FileOperationTools.class);
    
    private final String rootDirectory;
    private final AppProperties appProperties;

    // 在构造函数中
    public FileOperationTools(AppProperties appProperties) {
        this.appProperties = appProperties;
        // 使用规范化的路径
        this.rootDirectory = PathUtils.normalizePath(appProperties.getWorkspace().getRootDirectory());
    }

    @Tool(description = "Read the content of a file from the local filesystem. Supports pagination for large files.")
    public String readFile(
            @ToolParam(description = "The absolute path to the file to read. Must be within the workspace directory.")
            String absolutePath,
            @ToolParam(description = "Optional: For text files, the 0-based line number to start reading from.", required = false)
            Integer offset,
            @ToolParam(description = "Optional: For text files, the number of lines to read from the offset.", required = false)
            Integer limit) {

        long startTime = System.currentTimeMillis();
        try {
            logger.debug("Starting readFile operation for: {}", absolutePath);
            // 验证路径
            String validationError = validatePath(absolutePath);
            if (validationError != null) {
                return "Error: " + validationError;
            }

            Path filePath = Paths.get(absolutePath);

            // 检查文件是否存在
            if (!Files.exists(filePath)) {
                return "Error: File not found: " + absolutePath;
            }

            // 检查是否为文件
            if (!Files.isRegularFile(filePath)) {
                return "Error: Path is not a regular file: " + absolutePath;
            }

            // 检查文件大小
            long fileSize = Files.size(filePath);
            if (fileSize > appProperties.getWorkspace().getMaxFileSize()) {
                return "Error: File too large: " + fileSize + " bytes. Maximum allowed: " +
                    appProperties.getWorkspace().getMaxFileSize() + " bytes";
            }

            // 检查文件扩展名
            String fileName = filePath.getFileName().toString();
            if (!isAllowedFileType(fileName)) {
                return "Error: File type not allowed: " + fileName +
                    ". Allowed extensions: " + appProperties.getWorkspace().getAllowedExtensions();
            }

            // 读取文件
            if (offset != null && limit != null) {
                return readFileWithPagination(filePath, offset, limit);
            } else {
                return readFullFile(filePath);
            }

        } catch (IOException e) {
            long duration = System.currentTimeMillis() - startTime;
            logger.error("Error reading file: {} (duration: {}ms)", absolutePath, duration, e);
            return String.format("❌ Error reading file: %s\n⏱️ Duration: %dms\n🔍 Details: %s",
                absolutePath, duration, e.getMessage());
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            logger.error("Unexpected error reading file: {} (duration: {}ms)", absolutePath, duration, e);
            return String.format("❌ Unexpected error reading file: %s\n⏱️ Duration: %dms\n🔍 Details: %s",
                absolutePath, duration, e.getMessage());
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            logger.debug("Completed readFile operation for: {} (duration: {}ms)", absolutePath, duration);
        }
    }

    @Tool(description = "Write content to a file. Creates new file or overwrites existing file.")
    public String writeFile(
            @ToolParam(description = "The absolute path to the file to write. Must be within the workspace directory.")
            String filePath,
            @ToolParam(description = "The content to write to the file")
            String content) {

        long startTime = System.currentTimeMillis();
        try {
            logger.debug("Starting writeFile operation for: {}", filePath);
            // 验证路径
            String validationError = validatePath(filePath);
            if (validationError != null) {
                return "Error: " + validationError;
            }

            // 验证内容大小
            byte[] contentBytes = content.getBytes(StandardCharsets.UTF_8);
            if (contentBytes.length > appProperties.getWorkspace().getMaxFileSize()) {
                return "Error: Content too large: " + contentBytes.length + " bytes. Maximum allowed: " + 
                    appProperties.getWorkspace().getMaxFileSize() + " bytes";
            }

            Path path = Paths.get(filePath);
            boolean isNewFile = !Files.exists(path);
            
            // 确保父目录存在
            Files.createDirectories(path.getParent());
            
            // 写入文件
            Files.writeString(path, content, StandardCharsets.UTF_8,
                StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
            
            long lineCount = content.lines().count();
            String absolutePath = path.toAbsolutePath().toString();
            String relativePath = getRelativePath(path);

            if (isNewFile) {
                return String.format("Successfully created file:\n📁 Full path: %s\n📂 Relative path: %s\n📊 Stats: %d lines, %d bytes",
                    absolutePath, relativePath, lineCount, contentBytes.length);
            } else {
                return String.format("Successfully wrote to file:\n📁 Full path: %s\n📂 Relative path: %s\n📊 Stats: %d lines, %d bytes",
                    absolutePath, relativePath, lineCount, contentBytes.length);
            }

        } catch (IOException e) {
            long duration = System.currentTimeMillis() - startTime;
            logger.error("Error writing file: {} (duration: {}ms)", filePath, duration, e);
            return String.format("❌ Error writing file: %s\n⏱️ Duration: %dms\n🔍 Details: %s",
                filePath, duration, e.getMessage());
        } catch (Exception e) {
            long duration = System.currentTimeMillis() - startTime;
            logger.error("Unexpected error writing file: {} (duration: {}ms)", filePath, duration, e);
            return String.format("❌ Unexpected error writing file: %s\n⏱️ Duration: %dms\n🔍 Details: %s",
                filePath, duration, e.getMessage());
        } finally {
            long duration = System.currentTimeMillis() - startTime;
            logger.debug("Completed writeFile operation for: {} (duration: {}ms)", filePath, duration);
        }
    }

    @Tool(description = "Edit a file by replacing specific text content.")
    public String editFile(
            @ToolParam(description = "The absolute path to the file to edit. Must be within the workspace directory.") 
            String filePath,
            @ToolParam(description = "The text to find and replace in the file") 
            String oldText,
            @ToolParam(description = "The new text to replace the old text with") 
            String newText) {
        
        try {
            // 验证路径
            String validationError = validatePath(filePath);
            if (validationError != null) {
                return "Error: " + validationError;
            }

            Path path = Paths.get(filePath);
            
            // 检查文件是否存在
            if (!Files.exists(path)) {
                return "Error: File not found: " + filePath;
            }

            // 检查是否为文件
            if (!Files.isRegularFile(path)) {
                return "Error: Path is not a regular file: " + filePath;
            }

            // 读取原始内容
            String originalContent = Files.readString(path, StandardCharsets.UTF_8);
            
            // 执行替换
            if (!originalContent.contains(oldText)) {
                return "Error: Could not find the specified text to replace in file: " + filePath;
            }
            
            String newContent = originalContent.replace(oldText, newText);
            
            // 写入新内容
            Files.writeString(path, newContent, StandardCharsets.UTF_8);

            String absolutePath = path.toAbsolutePath().toString();
            String relativePath = getRelativePath(path);
            return String.format("Successfully edited file:\n📁 Full path: %s\n📂 Relative path: %s\n✏️ Replaced text successfully",
                absolutePath, relativePath);

        } catch (IOException e) {
            logger.error("Error editing file: " + filePath, e);
            return "Error editing file: " + e.getMessage();
        } catch (Exception e) {
            logger.error("Unexpected error editing file: " + filePath, e);
            return "Unexpected error: " + e.getMessage();
        }
    }

    @Tool(description = "List the contents of a directory.")
    public String listDirectory(
            @ToolParam(description = "The absolute path to the directory to list. Must be within the workspace directory.") 
            String directoryPath,
            @ToolParam(description = "Whether to list contents recursively", required = false) 
            Boolean recursive) {
        
        try {
            // 验证路径
            String validationError = validatePath(directoryPath);
            if (validationError != null) {
                return "Error: " + validationError;
            }

            Path path = Paths.get(directoryPath);
            
            // 检查目录是否存在
            if (!Files.exists(path)) {
                return "Error: Directory not found: " + directoryPath;
            }

            // 检查是否为目录
            if (!Files.isDirectory(path)) {
                return "Error: Path is not a directory: " + directoryPath;
            }

            boolean isRecursive = recursive != null && recursive;
            String absolutePath = path.toAbsolutePath().toString();
            String relativePath = getRelativePath(path);

            if (isRecursive) {
                return listDirectoryRecursive(path, absolutePath, relativePath);
            } else {
                return listDirectorySimple(path, absolutePath, relativePath);
            }

        } catch (IOException e) {
            logger.error("Error listing directory: " + directoryPath, e);
            return "Error listing directory: " + e.getMessage();
        } catch (Exception e) {
            logger.error("Unexpected error listing directory: " + directoryPath, e);
            return "Unexpected error: " + e.getMessage();
        }
    }

    // 辅助方法
    private String validatePath(String path) {
        if (path == null || path.trim().isEmpty()) {
            return "Path cannot be empty";
        }

        Path filePath = Paths.get(path);
        
        // 验证是否为绝对路径
        if (!filePath.isAbsolute()) {
            return "Path must be absolute: " + path;
        }

        // 验证是否在工作目录内
        if (!isWithinWorkspace(filePath)) {
            return "Path must be within the workspace directory (" + rootDirectory + "): " + path;
        }

        return null;
    }

    private boolean isWithinWorkspace(Path path) {
        try {
            Path workspacePath = Paths.get(rootDirectory).toRealPath();
            Path targetPath = path.toRealPath();
            return targetPath.startsWith(workspacePath);
        } catch (IOException e) {
            // 如果路径不存在，检查其父目录
            try {
                Path workspacePath = Paths.get(rootDirectory).toRealPath();
                Path normalizedPath = path.normalize();
                return normalizedPath.startsWith(workspacePath.normalize());
            } catch (IOException ex) {
                return false;
            }
        }
    }

    private boolean isAllowedFileType(String fileName) {
        List<String> allowedExtensions = appProperties.getWorkspace().getAllowedExtensions();
        return allowedExtensions.stream()
            .anyMatch(ext -> fileName.toLowerCase().endsWith(ext.toLowerCase()));
    }

    private String getRelativePath(Path path) {
        try {
            Path workspacePath = Paths.get(rootDirectory);
            return workspacePath.relativize(path).toString();
        } catch (Exception e) {
            return path.toString();
        }
    }

    private String readFullFile(Path filePath) throws IOException {
        String content = Files.readString(filePath, StandardCharsets.UTF_8);
        String absolutePath = filePath.toAbsolutePath().toString();
        String relativePath = getRelativePath(filePath);

        long lineCount = content.lines().count();
        return String.format("📁 Full path: %s\n📂 Relative path: %s\n📊 Stats: %d lines, %d bytes\n\n📄 Content:\n%s",
            absolutePath, relativePath, lineCount, content.getBytes(StandardCharsets.UTF_8).length, content);
    }

    private String readFileWithPagination(Path filePath, int offset, int limit) throws IOException {
        List<String> allLines = Files.readAllLines(filePath, StandardCharsets.UTF_8);

        if (offset >= allLines.size()) {
            return "Error: Offset " + offset + " is beyond file length (" + allLines.size() + " lines)";
        }

        int endIndex = Math.min(offset + limit, allLines.size());
        List<String> selectedLines = allLines.subList(offset, endIndex);
        String content = String.join("\n", selectedLines);

        String absolutePath = filePath.toAbsolutePath().toString();
        String relativePath = getRelativePath(filePath);
        return String.format("📁 Full path: %s\n📂 Relative path: %s\n📊 Showing lines %d-%d of %d total\n\n📄 Content:\n%s",
            absolutePath, relativePath, offset + 1, endIndex, allLines.size(), content);
    }

    private String listDirectorySimple(Path path, String absolutePath, String relativePath) throws IOException {
        StringBuilder result = new StringBuilder();
        result.append("📁 Full path: ").append(absolutePath).append("\n");
        result.append("📂 Relative path: ").append(relativePath).append("\n\n");
        result.append("📋 Directory contents:\n");

        try (Stream<Path> entries = Files.list(path)) {
            List<Path> sortedEntries = entries.sorted().collect(Collectors.toList());

            for (Path entry : sortedEntries) {
                String name = entry.getFileName().toString();
                String entryAbsolutePath = entry.toAbsolutePath().toString();
                if (Files.isDirectory(entry)) {
                    result.append("📁 [DIR]  ").append(name).append("/\n");
                    result.append("   └─ ").append(entryAbsolutePath).append("\n");
                } else {
                    long size = Files.size(entry);
                    result.append("📄 [FILE] ").append(name).append(" (").append(size).append(" bytes)\n");
                    result.append("   └─ ").append(entryAbsolutePath).append("\n");
                }
            }
        }

        return result.toString();
    }

    private String listDirectoryRecursive(Path path, String absolutePath, String relativePath) throws IOException {
        StringBuilder result = new StringBuilder();
        result.append("📁 Full path: ").append(absolutePath).append("\n");
        result.append("📂 Relative path: ").append(relativePath).append("\n\n");
        result.append("🌳 Directory tree (recursive):\n");

        try (Stream<Path> entries = Files.walk(path)) {
            entries.sorted()
                .forEach(entry -> {
                    if (!entry.equals(path)) {
                        String entryAbsolutePath = entry.toAbsolutePath().toString();
                        String entryRelativePath = getRelativePath(entry);

                        // 计算缩进级别
                        int depth = entry.getNameCount() - path.getNameCount();
                        String indent = "  ".repeat(depth);

                        if (Files.isDirectory(entry)) {
                            result.append(indent).append("📁 ").append(entryRelativePath).append("/\n");
                            result.append(indent).append("   └─ ").append(entryAbsolutePath).append("\n");
                        } else {
                            try {
                                long size = Files.size(entry);
                                result.append(indent).append("📄 ").append(entryRelativePath).append(" (").append(size).append(" bytes)\n");
                                result.append(indent).append("   └─ ").append(entryAbsolutePath).append("\n");
                            } catch (IOException e) {
                                result.append(indent).append("📄 ").append(entryRelativePath).append(" (size unknown)\n");
                                result.append(indent).append("   └─ ").append(entryAbsolutePath).append("\n");
                            }
                        }
                    }
                });
        }

        return result.toString();
    }
}
