/**
 * File Operation Tool
 * Handle file system operations safely
 */

const fs = require('fs').promises;
const path = require('path');
const { BaseTool, ToolResult } = require('./tool-base.cjs');

class FileOperationTool extends BaseTool {
  constructor(config = {}) {
    super({
      name: 'file',
      description: 'Perform file system operations (read, write, list, etc.)',
      parameters: [
        {
          name: 'operation',
          type: 'string',
          required: true,
          description: 'Operation to perform: read, write, list, delete, move, copy, exists, stat'
        },
        {
          name: 'path',
          type: 'string',
          required: false,
          description: 'File or directory path'
        },
        {
          name: 'content',
          type: 'string',
          required: false,
          description: 'Content for write operations'
        },
        {
          name: 'destination',
          type: 'string',
          required: false,
          description: 'Destination path for move/copy operations'
        },
        {
          name: 'encoding',
          type: 'string',
          required: false,
          description: 'File encoding (default: utf8)'
        }
      ],
      ...config
    });

    this.allowedPaths = config.allowedPaths || [];
    this.maxFileSize = config.maxFileSize || 1024 * 1024; // 1MB
  }

  async execute(params) {
    const { operation, path: filePath, content, destination, encoding = 'utf8' } = params;

    // Validate path
    const validation = this.validatePath(filePath);
    if (!validation.valid) {
      throw new Error(`Path validation failed: ${validation.reason}`);
    }

    try {
      switch (operation) {
        case 'read':
          return await this.readFile(filePath, encoding);

        case 'write':
          return await this.writeFile(filePath, content, encoding);

        case 'list':
          return await this.listFiles(filePath);

        case 'delete':
          return await this.deleteFile(filePath);

        case 'move':
          return await this.moveFile(filePath, destination);

        case 'copy':
          return await this.copyFile(filePath, destination);

        case 'exists':
          return await this.fileExists(filePath);

        case 'stat':
          return await this.getFileStats(filePath);

        default:
          throw new Error(`Unknown operation: ${operation}`);
      }
    } catch (error) {
      return ToolResult.failure(
        error,
        `File operation '${operation}' failed: ${error.message}`,
        { operation, path: filePath }
      );
    }
  }

  /**
   * Validate file path against security rules
   */
  validatePath(filePath) {
    if (!filePath) {
      return { valid: false, reason: 'Path is required' };
    }

    // Resolve absolute path
    const resolvedPath = path.resolve(filePath);

    // Check against allowed paths if configured
    if (this.allowedPaths.length > 0) {
      const isAllowed = this.allowedPaths.some(allowedPath => {
        const resolvedAllowed = path.resolve(allowedPath);
        return resolvedPath.startsWith(resolvedAllowed);
      });

      if (!isAllowed) {
        return { valid: false, reason: 'Path is outside allowed directories' };
      }
    }

    // Prevent path traversal
    if (filePath.includes('..')) {
      return { valid: false, reason: 'Path traversal not allowed' };
    }

    return { valid: true };
  }

  async readFile(filePath, encoding) {
    const stats = await fs.stat(filePath);

    if (stats.size > this.maxFileSize) {
      throw new Error(`File too large (${stats.size} bytes, max ${this.maxFileSize})`);
    }

    if (!stats.isFile()) {
      throw new Error('Path is not a file');
    }

    const content = await fs.readFile(filePath, encoding);

    return ToolResult.success(
      { content, size: stats.size },
      content,
      { operation: 'read', path: filePath, size: stats.size }
    );
  }

  async writeFile(filePath, content, encoding) {
    if (content === undefined || content === null) {
      throw new Error('Content is required for write operation');
    }

    // Create parent directories if needed
    const dir = path.dirname(filePath);
    await fs.mkdir(dir, { recursive: true });

    await fs.writeFile(filePath, content, encoding);

    const stats = await fs.stat(filePath);

    return ToolResult.success(
      { size: stats.size },
      `Wrote ${stats.size} bytes to ${filePath}`,
      { operation: 'write', path: filePath, size: stats.size }
    );
  }

  async listFiles(dirPath) {
    const stats = await fs.stat(dirPath);

    if (!stats.isDirectory()) {
      throw new Error('Path is not a directory');
    }

    const entries = await fs.readdir(dirPath, { withFileTypes: true });

    const files = entries.map(entry => ({
      name: entry.name,
      type: entry.isDirectory() ? 'directory' : 'file',
      path: path.join(dirPath, entry.name)
    }));

    const output = files
      .map(f => `${f.type === 'directory' ? 'D' : 'F'} ${f.name}`)
      .join('\n');

    return ToolResult.success(
      files,
      output || '[Empty directory]',
      { operation: 'list', path: dirPath, count: files.length }
    );
  }

  async deleteFile(filePath) {
    const stats = await fs.stat(filePath);

    if (stats.isDirectory()) {
      await fs.rmdir(filePath, { recursive: true });
    } else {
      await fs.unlink(filePath);
    }

    return ToolResult.success(
      { deleted: true },
      `Deleted: ${filePath}`,
      { operation: 'delete', path: filePath }
    );
  }

  async moveFile(source, destination) {
    if (!destination) {
      throw new Error('Destination is required for move operation');
    }

    const destValidation = this.validatePath(destination);
    if (!destValidation.valid) {
      throw new Error(`Destination validation failed: ${destValidation.reason}`);
    }

    // Create parent directories
    const destDir = path.dirname(destination);
    await fs.mkdir(destDir, { recursive: true });

    await fs.rename(source, destination);

    return ToolResult.success(
      { moved: true },
      `Moved ${source} to ${destination}`,
      { operation: 'move', source, destination }
    );
  }

  async copyFile(source, destination) {
    if (!destination) {
      throw new Error('Destination is required for copy operation');
    }

    const destValidation = this.validatePath(destination);
    if (!destValidation.valid) {
      throw new Error(`Destination validation failed: ${destValidation.reason}`);
    }

    // Create parent directories
    const destDir = path.dirname(destination);
    await fs.mkdir(destDir, { recursive: true });

    await fs.copyFile(source, destination);

    const stats = await fs.stat(destination);

    return ToolResult.success(
      { size: stats.size },
      `Copied ${source} to ${destination}`,
      { operation: 'copy', source, destination, size: stats.size }
    );
  }

  async fileExists(filePath) {
    try {
      await fs.access(filePath);
      return ToolResult.success(
        { exists: true },
        `File exists: ${filePath}`,
        { operation: 'exists', path: filePath, exists: true }
      );
    } catch {
      return ToolResult.success(
        { exists: false },
        `File does not exist: ${filePath}`,
        { operation: 'exists', path: filePath, exists: false }
      );
    }
  }

  async getFileStats(filePath) {
    const stats = await fs.stat(filePath);

    const info = {
      size: stats.size,
      created: stats.birthtime,
      modified: stats.mtime,
      accessed: stats.atime,
      isFile: stats.isFile(),
      isDirectory: stats.isDirectory(),
      permissions: stats.mode.toString(8)
    };

    const output = `
Size: ${info.size} bytes
Created: ${info.created}
Modified: ${info.modified}
Type: ${info.isFile ? 'File' : 'Directory'}
Permissions: ${info.permissions}
    `.trim();

    return ToolResult.success(
      info,
      output,
      { operation: 'stat', path: filePath }
    );
  }
}

module.exports = {
  FileOperationTool
};
