// services/file-storage.service.js
const fs = require('fs');
const path = require('path');
const crypto = require('crypto');
const database = require('../config/database');

class FileStorageService {
  constructor() {
    this.supportedTextExtensions = new Set([
      '.js', '.ts', '.jsx', '.tsx', '.vue', '.py', '.java', '.cpp', '.c', '.cs',
      '.php', '.rb', '.go', '.rs', '.kt', '.swift', '.scala', '.clj', '.hs',
      '.elm', '.ml', '.fs', '.vb', '.pas', '.asm', '.sql', '.sh', '.bash',
      '.ps1', '.bat', '.cmd', '.html', '.htm', '.xml', '.css', '.scss', '.sass',
      '.less', '.json', '.yaml', '.yml', '.toml', '.ini', '.cfg', '.conf',
      '.env', '.md', '.txt', '.rst', '.adoc', '.tex', '.r', '.m', '.pl',
      '.lua', '.dart', '.jl', '.nim', '.zig', '.v', '.d', '.cr', '.ex', '.exs'
    ]);

    this.languageMap = {
      '.js': 'javascript', '.ts': 'typescript', '.jsx': 'javascript', '.tsx': 'typescript',
      '.vue': 'vue', '.py': 'python', '.java': 'java', '.cpp': 'cpp', '.c': 'c',
      '.cs': 'csharp', '.php': 'php', '.rb': 'ruby', '.go': 'go', '.rs': 'rust',
      '.kt': 'kotlin', '.swift': 'swift', '.scala': 'scala', '.clj': 'clojure',
      '.hs': 'haskell', '.elm': 'elm', '.ml': 'ocaml', '.fs': 'fsharp',
      '.vb': 'vbnet', '.html': 'html', '.css': 'css', '.scss': 'scss',
      '.json': 'json', '.yaml': 'yaml', '.yml': 'yaml', '.xml': 'xml',
      '.sql': 'sql', '.sh': 'bash', '.md': 'markdown'
    };
  }

  // Initialize storage record for a repository
  async initializeRepositoryStorage(repositoryId, localPath) {
    const query = `
      INSERT INTO repository_storage (
        repository_id, local_path, storage_status, download_started_at
      ) VALUES ($1, $2, $3, NOW())
      ON CONFLICT (repository_id) 
      DO UPDATE SET 
        local_path = $2,
        storage_status = $3,
        download_started_at = NOW(),
        updated_at = NOW()
      RETURNING *
    `;
    
    const result = await database.query(query, [repositoryId, localPath, 'downloading']);
    return result.rows[0];
  }

  // Process and store directory structure
  async processDirectoryStructure(storageId, repositoryId, basePath, currentPath = '', parentDirId = null, level = 0) {
    const fullPath = path.join(basePath, currentPath);
    
    if (!fs.existsSync(fullPath)) {
      return null;
    }

    const stats = fs.statSync(fullPath);
    
    if (!stats.isDirectory()) {
      return null;
    }

    // Skip any .git directory anywhere in the tree
    const normalizedRel = currentPath.replace(/\\/g, '/');
    if (
      normalizedRel === '.git' ||
      normalizedRel.startsWith('.git/') ||
      normalizedRel.includes('/.git/')
    ) {
      return null;
    }

    // Insert directory record
    const dirName = currentPath === '' ? '.' : path.basename(currentPath);
    const relativePath = currentPath === '' ? '' : currentPath;
    
    const dirQuery = `
      INSERT INTO repository_directories (
        repository_id, storage_id, parent_directory_id, directory_name,
        relative_path, absolute_path, level
      ) VALUES ($1, $2, $3, $4, $5, $6, $7)
      RETURNING *
    `;
    
    const dirResult = await database.query(dirQuery, [
      repositoryId, storageId, parentDirId, dirName,
      relativePath, fullPath, level
    ]);
    
    const directoryRecord = dirResult.rows[0];
    let totalFiles = 0;
    let totalSubDirs = 0;
    let totalSize = 0;
    const filesInDirectory = []; // Collect all files for JSON storage

    try {
      const items = fs.readdirSync(fullPath);
      
      for (const item of items) {
        const itemPath = path.join(fullPath, item);
        const itemRelativePath = currentPath ? path.join(currentPath, item) : item;
        const itemStats = fs.statSync(itemPath);
        
        // Skip .git directory and its contents
        if (item === '.git' || itemRelativePath.replace(/\\/g, '/').includes('/.git/')) {
          continue;
        }

        if (itemStats.isDirectory()) {
          // Recursively process subdirectory
          const subDir = await this.processDirectoryStructure(
            storageId, repositoryId, basePath, itemRelativePath, 
            directoryRecord.id, level + 1
          );
          if (subDir) {
            totalSubDirs++;
          }
        } else if (itemStats.isFile()) {
          // Process file and collect metadata
          const fileMetadata = await this.getFileMetadata(itemPath, itemRelativePath);
          if (fileMetadata) {
            filesInDirectory.push(fileMetadata);
            totalFiles++;
            totalSize += Number(itemStats.size) || 0;
          }
        }
      }

      // Store all files in this directory as a single JSON array
      if (filesInDirectory.length > 0) {
        await this.storeDirectoryFiles(
          storageId, repositoryId, directoryRecord.id, 
          relativePath, fullPath, filesInDirectory
        );
      }
    } catch (error) {
      console.warn(`Error processing directory ${fullPath}:`, error.message);
    }

    // Update directory stats
    await database.query(`
      UPDATE repository_directories 
      SET files_count = $1, subdirectories_count = $2, total_size_bytes = $3, updated_at = NOW()
      WHERE id = $4
    `, [totalFiles, totalSubDirs, totalSize, directoryRecord.id]);

    return directoryRecord;
  }

  // Get file metadata (for JSON storage)
  async getFileMetadata(absolutePath, relativePath) {
    try {
      const stats = fs.statSync(absolutePath);
      const filename = path.basename(absolutePath);
      const extension = path.extname(filename).toLowerCase();
      
      // Calculate file hash
      const fileBuffer = fs.readFileSync(absolutePath);
      const hash = crypto.createHash('sha256').update(fileBuffer).digest('hex');
      
      // Determine if file is binary
      const isBinary = !this.supportedTextExtensions.has(extension) || this.isBinaryContent(fileBuffer);
      
      // Get MIME type (simplified)
      const mimeType = this.getMimeType(extension, isBinary);
      
      return {
        filename,
        file_extension: extension,
        relative_path: relativePath,
        absolute_path: absolutePath,
        file_size_bytes: stats.size,
        file_hash: hash,
        mime_type: mimeType,
        is_binary: isBinary,
        encoding: isBinary ? null : 'utf-8',
        created_at: new Date().toISOString(),
        updated_at: new Date().toISOString()
      };
    } catch (error) {
      console.warn(`Error getting file metadata ${absolutePath}:`, error.message);
      return null;
    }
  }

  // Store all files in a directory as JSON array
  async storeDirectoryFiles(storageId, repositoryId, directoryId, relativePath, absolutePath, filesArray) {
    try {
      const fileQuery = `
        INSERT INTO repository_files (
          repository_id, storage_id, directory_id, relative_path, absolute_path, files
        ) VALUES ($1, $2, $3, $4, $5, $6::jsonb)
        ON CONFLICT (directory_id) 
        DO UPDATE SET 
          files = $6::jsonb,
          updated_at = NOW()
        RETURNING *
      `;
      
      const result = await database.query(fileQuery, [
        repositoryId, storageId, directoryId, relativePath, absolutePath,
        JSON.stringify(filesArray)
      ]);
      
      return result.rows[0];
    } catch (error) {
      console.error(`Error storing directory files for ${relativePath}:`, error.message);
      return null;
    }
  }

  // processFileContent method removed as repository_file_contents table was removed

  // Complete storage process for a repository
  async completeRepositoryStorage(storageId) {
    // Calculate totals
    const statsQuery = `
      SELECT 
        COUNT(DISTINCT rd.id) as total_directories,
        COUNT(rf.id) as total_files,
        COALESCE(SUM(rf.file_size_bytes), 0) as total_size
      FROM repository_storage rs
      LEFT JOIN repository_directories rd ON rs.id = rd.storage_id
      LEFT JOIN repository_files rf ON rs.id = rf.storage_id
      WHERE rs.id = $1
    `;
    
    const statsResult = await database.query(statsQuery, [storageId]);
    const stats = statsResult.rows[0];
    
    // Update storage record
    const updateQuery = `
      UPDATE repository_storage 
      SET 
        storage_status = 'completed',
        total_files_count = $1,
        total_directories_count = $2,
        total_size_bytes = $3,
        download_completed_at = NOW(),
        updated_at = NOW()
      WHERE id = $4
      RETURNING *
    `;
    
    const result = await database.query(updateQuery, [
      parseInt(stats.total_files),
      parseInt(stats.total_directories),
      parseInt(stats.total_size),
      storageId
    ]);
    
    return result.rows[0];
  }

  // Mark storage as failed
  async markStorageFailed(storageId, error) {
    const query = `
      UPDATE repository_storage 
      SET 
        storage_status = 'error',
        updated_at = NOW()
      WHERE id = $1
      RETURNING *
    `;
    
    const result = await database.query(query, [storageId]);
    return result.rows[0];
  }

  // Get repository file structure
  async getRepositoryStructure(repositoryId, directoryPath = null) {
    let query = `
      SELECT 
        rd.*,
        COUNT(DISTINCT rdf.id) as files_count,
        COUNT(DISTINCT rds.id) as subdirs_count
      FROM repository_directories rd
      LEFT JOIN repository_files rdf ON rd.id = rdf.directory_id
      LEFT JOIN repository_directories rds ON rd.id = rds.parent_directory_id
      WHERE rd.repository_id = $1
    `;
    
    const params = [repositoryId];
    
    if (directoryPath !== null) {
      query += ` AND rd.relative_path = $2`;
      params.push(directoryPath);
    }
    
    query += ` GROUP BY rd.id ORDER BY rd.level, rd.directory_name`;
    
    const result = await database.query(query, params);
    return result.rows;
  }

  // Get files in a directory
  async getDirectoryFiles(repositoryId, directoryPath = '') {
    const query = `
      SELECT rf.*
      FROM repository_files rf
      LEFT JOIN repository_directories rd ON rf.directory_id = rd.id
      WHERE rf.repository_id = $1 AND rd.relative_path = $2
      ORDER BY rf.filename
    `;
    
    const result = await database.query(query, [repositoryId, directoryPath]);
    return result.rows;
  }

  // Search files by content - disabled as repository_file_contents table was removed
  async searchFileContent(repositoryId, searchQuery) {
    // Content search disabled as repository_file_contents table was removed
    return [];
  }

  // Utility methods
  isBinaryContent(buffer) {
    // Simple binary detection - check for null bytes in first 1024 bytes
    const sample = buffer.slice(0, Math.min(1024, buffer.length));
    return sample.includes(0);
  }

  getMimeType(extension, isBinary) {
    const mimeTypes = {
      '.js': 'application/javascript',
      '.ts': 'application/typescript',
      '.json': 'application/json',
      '.html': 'text/html',
      '.css': 'text/css',
      '.md': 'text/markdown',
      '.txt': 'text/plain',
      '.xml': 'application/xml',
      '.yml': 'application/x-yaml',
      '.yaml': 'application/x-yaml',
      '.pdf': 'application/pdf',
      '.png': 'image/png',
      '.jpg': 'image/jpeg',
      '.jpeg': 'image/jpeg',
      '.gif': 'image/gif',
      '.svg': 'image/svg+xml'
    };

    if (mimeTypes[extension]) {
      return mimeTypes[extension];
    }

    return isBinary ? 'application/octet-stream' : 'text/plain';
  }

  // Clean up storage for a repository
  // Get file tree with change indicators for commit view
  async getFileTreeWithChanges(repositoryId, baseCommit, targetCommit) {
    try {
      // Get basic file tree structure
      const fileTree = await this.getRepositoryFileStructure(repositoryId);
      
      // For now, return tree without change indicators
      // In real implementation, you would compare commits and add change indicators
      return this.addChangeIndicators(fileTree, baseCommit, targetCommit);
    } catch (error) {
      console.error('Error getting file tree with changes:', error);
      throw error;
    }
  }

  // Get diff summary for commit view
  async getDiffSummary(repositoryId, baseCommit, targetCommit) {
    try {
      // Mock diff summary - in real implementation, calculate from git diff
      return {
        total_files_changed: 13,
        total_additions: 4139,
        total_deletions: 0
      };
    } catch (error) {
      console.error('Error getting diff summary:', error);
      throw error;
    }
  }

  // Get file diff for specific file
  async getFileDiff(repositoryId, filePath, baseCommit, targetCommit) {
    try {
      // Mock diff data - in real implementation, get actual git diff
      return {
        changes: [
          {
            type: "addition",
            old_line_number: null,
            new_line_number: 75,
            content: "try:"
          },
          {
            type: "addition",
            old_line_number: null,
            new_line_number: 76,
            content: "    logger.info(\"handler starting generation.\")"
          },
          {
            type: "addition",
            old_line_number: null,
            new_line_number: 77,
            content: "    # Step 1: Prepare context chunks for Claude"
          },
          {
            type: "addition",
            old_line_number: null,
            new_line_number: 78,
            content: "    await self._prepare_context_chunks()"
          }
        ]
      };
    } catch (error) {
      console.error('Error getting file diff:', error);
      throw error;
    }
  }

  // Get file content for blob view
  async getFileContent(repositoryId, filePath) {
    try {
      // Get file from database
      const query = `
        SELECT rf.*, rd.relative_path as directory_path
        FROM repository_files rf
        LEFT JOIN repository_directories rd ON rf.directory_id = rd.id
        WHERE rf.repository_id = $1 AND rf.relative_path = $2
      `;
      
      const result = await database.query(query, [repositoryId, filePath]);
      
      if (result.rows.length === 0) {
        throw new Error('File not found');
      }
      
      const file = result.rows[0];
      
      // Read file content from local storage
      const content = await this.readFileContentForAI(file.absolute_path, file.is_binary);
      
      return {
        id: file.id,
        path: file.relative_path,
        filename: file.filename,
        extension: file.file_extension,
        size_bytes: file.file_size_bytes,
        is_binary: file.is_binary,
        mime_type: file.mime_type,
        language: this.detectLanguage(file.file_extension, content),
        content: content,
        line_count: content ? content.split('\n').length : 0
      };
    } catch (error) {
      console.error('Error getting file content:', error);
      throw error;
    }
  }

  // Add change indicators to file tree
  addChangeIndicators(fileTree, baseCommit, targetCommit) {
    // Mock change indicators - in real implementation, compare commits
    const addChangeIndicators = (node) => {
      if (node.type === 'file') {
        node.change_status = 'added';
        node.additions = Math.floor(Math.random() * 50) + 1;
        node.deletions = 0;
      } else if (node.children) {
        node.children.forEach(addChangeIndicators);
        node.change_status = 'modified';
        node.additions = node.children.reduce((sum, child) => sum + (child.additions || 0), 0);
        node.deletions = node.children.reduce((sum, child) => sum + (child.deletions || 0), 0);
      }
    };
    
    addChangeIndicators(fileTree);
    return fileTree;
  }

  // Get file record by path for blob view
  async getFileByPath(repositoryId, filePath) {
    try {
      const query = `
        SELECT 
          rf.absolute_path,
          rf.is_binary,
          rf.file_size_bytes,
          rf.mime_type,
          rf.filename,
          rf.file_extension,
          rf.relative_path
        FROM repository_files rf
        WHERE rf.repository_id = $1 AND rf.relative_path = $2
        LIMIT 1
      `;

      const result = await database.query(query, [repositoryId, filePath]);
      
      if (result.rows.length === 0) {
        return null;
      }

      return result.rows[0];

    } catch (error) {
      console.error('Error getting file by path:', error);
      return null;
    }
  }

  // Get repository file tree for UI display
  async getRepositoryFileTree(repositoryId, options = {}) {
    const {
      includeChanges = false,
      baseCommit = null,
      targetCommit = null
    } = options;

    try {
      // Get directories from database
      const dirsQuery = `
        SELECT 
          rd.id,
          rd.directory_name,
          rd.relative_path,
          rd.level,
          rd.parent_directory_id,
          COUNT(rf.id) as files_count,
          COUNT(rd2.id) as subdirectories_count
        FROM repository_directories rd
        LEFT JOIN repository_files rf ON rd.id = rf.directory_id
        LEFT JOIN repository_directories rd2 ON rd.id = rd2.parent_directory_id
        WHERE rd.repository_id = $1
        GROUP BY rd.id, rd.directory_name, rd.relative_path, rd.level, rd.parent_directory_id
        ORDER BY rd.level, rd.relative_path
      `;
      
      const dirsResult = await database.query(dirsQuery, [repositoryId]);
      const directories = dirsResult.rows;
      
      // Get files from database
      const filesQuery = `
        SELECT 
          rf.filename,
          rf.file_extension,
          rf.relative_path,
          rf.absolute_path,
          rf.file_size_bytes,
          rf.is_binary,
          rf.mime_type,
          rd.relative_path as directory_path
        FROM repository_files rf
        LEFT JOIN repository_directories rd ON rf.directory_id = rd.id
        WHERE rf.repository_id = $1
        ORDER BY rf.relative_path
      `;
      
      const filesResult = await database.query(filesQuery, [repositoryId]);
      const files = filesResult.rows;
      
      // Build tree structure
      const fileTree = this.buildFileTreeForUI(directories, files);
      
      // Add change indicators if requested
      if (includeChanges && baseCommit && targetCommit) {
        const changes = await this.getFileChanges(repositoryId, baseCommit, targetCommit);
        this.addChangeIndicators(fileTree, changes);
      }
      
      return fileTree;
      
    } catch (error) {
      console.error('Error getting file tree:', error);
      throw error;
    }
  }

  // Build file tree for UI display
  buildFileTreeForUI(directories, files) {
    const root = {
      type: 'directory',
      name: 'root',
      path: '',
      level: 0,
      children: []
    };
    
    // Group directories by level
    const dirsByLevel = {};
    directories.forEach(dir => {
      if (!dirsByLevel[dir.level]) {
        dirsByLevel[dir.level] = [];
      }
      dirsByLevel[dir.level].push(dir);
    });
    
    // Build tree level by level
    this.buildTreeRecursiveForUI(root, dirsByLevel, 0);
    
    // Add files to directories
    this.addFilesToTreeForUI(root, files);
    
    return root;
  }

  // Recursive tree building for UI
  buildTreeRecursiveForUI(parent, dirsByLevel, currentLevel) {
    const currentDirs = dirsByLevel[currentLevel] || [];
    
    currentDirs.forEach(dir => {
      if (dir.parent_directory_id === parent.id || 
          (dir.parent_directory_id === null && parent.path === '')) {
        
        const dirNode = {
          type: 'directory',
          name: dir.directory_name,
          path: dir.relative_path,
          level: dir.level,
          files_count: parseInt(dir.files_count),
          subdirectories_count: parseInt(dir.subdirectories_count),
          children: []
        };
        
        parent.children.push(dirNode);
        
        // Recursively add subdirectories
        this.buildTreeRecursiveForUI(dirNode, dirsByLevel, currentLevel + 1);
      }
    });
  }

  // Add files to tree for UI
  addFilesToTreeForUI(tree, files) {
    files.forEach(file => {
      const pathParts = file.relative_path.split('/');
      const fileName = pathParts.pop();
      const dirPath = pathParts.join('/');
      
      // Find directory in tree
      const directory = this.findDirectoryByPath(tree, dirPath);
      
      if (directory) {
        directory.children.push({
          type: 'file',
          name: fileName,
          path: file.relative_path,
          size_bytes: file.file_size_bytes,
          extension: file.file_extension,
          is_binary: file.is_binary,
          mime_type: file.mime_type
        });
      }
    });
  }

  // Find directory by path in tree
  findDirectoryByPath(tree, targetPath) {
    if (tree.path === targetPath) {
      return tree;
    }
    
    for (const child of tree.children || []) {
      if (child.type === 'directory') {
        const found = this.findDirectoryByPath(child, targetPath);
        if (found) return found;
      }
    }
    
    return null;
  }

  // Get file changes between commits
  async getFileChanges(repositoryId, baseCommit, targetCommit) {
    try {
      // Get repository storage path
      const repoQuery = `
        SELECT rs.local_path
        FROM repository_storage rs
        WHERE rs.repository_id = $1
      `;
      
      const repoResult = await database.query(repoQuery, [repositoryId]);
      
      if (repoResult.rows.length === 0) {
        return {};
      }
      
      const localPath = repoResult.rows[0].local_path;
      
      // Execute git diff command
      const { exec } = require('child_process');
      const util = require('util');
      const execAsync = util.promisify(exec);
      
      const gitDiffCommand = `cd "${localPath}" && git diff --name-status ${baseCommit}..${targetCommit}`;
      const { stdout } = await execAsync(gitDiffCommand);
      
      // Parse git diff output
      const changes = {};
      const lines = stdout.trim().split('\n');
      
      lines.forEach(line => {
        if (line.trim()) {
          const [status, filePath] = line.split('\t');
          changes[filePath] = {
            status: status.charAt(0), // M, A, D, R, etc.
            additions: 0,
            deletions: 0
          };
        }
      });
      
      return changes;
      
    } catch (error) {
      console.warn('Error getting file changes:', error.message);
      return {};
    }
  }

  // Add change indicators to file tree
  addChangeIndicators(tree, changes) {
    if (tree.type === 'file') {
      const change = changes[tree.path];
      if (change) {
        tree.change_status = change.status;
        tree.additions = change.additions;
        tree.deletions = change.deletions;
      }
    }
    
    if (tree.children) {
      tree.children.forEach(child => {
        this.addChangeIndicators(child, changes);
      });
    }
  }

  // Get file diff for specific file
  async getFileDiff(repositoryId, filePath, baseCommit, targetCommit) {
    try {
      // Get repository storage path
      const repoQuery = `
        SELECT rs.local_path
        FROM repository_storage rs
        WHERE rs.repository_id = $1
      `;
      
      const repoResult = await database.query(repoQuery, [repositoryId]);
      
      if (repoResult.rows.length === 0) {
        return { changes: [] };
      }
      
      const localPath = repoResult.rows[0].local_path;
      
      // Execute git diff for specific file
      const { exec } = require('child_process');
      const util = require('util');
      const execAsync = util.promisify(exec);
      
      const gitDiffCommand = `cd "${localPath}" && git diff ${baseCommit}..${targetCommit} -- "${filePath}"`;
      const { stdout } = await execAsync(gitDiffCommand);
      
      // Parse git diff output
      const changes = this.parseGitDiff(stdout);
      
      return { changes };
      
    } catch (error) {
      console.warn('Error getting file diff:', error.message);
      return { changes: [] };
    }
  }

  // Parse git diff output
  parseGitDiff(diffOutput) {
    const changes = [];
    const lines = diffOutput.split('\n');
    let currentOldLine = 0;
    let currentNewLine = 0;
    
    for (const line of lines) {
      if (line.startsWith('@@')) {
        // Parse hunk header: @@ -old_start,old_count +new_start,new_count @@
        const match = line.match(/@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@/);
        if (match) {
          currentOldLine = parseInt(match[1]) - 1;
          currentNewLine = parseInt(match[3]) - 1;
        }
      } else if (line.startsWith('+') && !line.startsWith('+++')) {
        // Addition
        currentNewLine++;
        changes.push({
          type: 'addition',
          old_line_number: null,
          new_line_number: currentNewLine,
          content: line.substring(1)
        });
      } else if (line.startsWith('-') && !line.startsWith('---')) {
        // Deletion
        currentOldLine++;
        changes.push({
          type: 'deletion',
          old_line_number: currentOldLine,
          new_line_number: null,
          content: line.substring(1)
        });
      } else if (line.startsWith(' ')) {
        // Context
        currentOldLine++;
        currentNewLine++;
        changes.push({
          type: 'context',
          old_line_number: currentOldLine,
          new_line_number: currentNewLine,
          content: line.substring(1)
        });
      }
    }
    
    return changes;
  }

  // Get diff summary
  async getDiffSummary(repositoryId, baseCommit, targetCommit) {
    try {
      // Get repository storage path
      const repoQuery = `
        SELECT rs.local_path
        FROM repository_storage rs
        WHERE rs.repository_id = $1
      `;
      
      const repoResult = await database.query(repoQuery, [repositoryId]);
      
      if (repoResult.rows.length === 0) {
        return { total_files_changed: 0, total_additions: 0, total_deletions: 0 };
      }
      
      const localPath = repoResult.rows[0].local_path;
      
      // Execute git diff --stat command
      const { exec } = require('child_process');
      const util = require('util');
      const execAsync = util.promisify(exec);
      
      const gitStatCommand = `cd "${localPath}" && git diff --stat ${baseCommit}..${targetCommit}`;
      const { stdout } = await execAsync(gitStatCommand);
      
      // Parse git stat output
      const lines = stdout.trim().split('\n');
      const lastLine = lines[lines.length - 1];
      
      // Extract summary from last line: "X files changed, Y insertions(+), Z deletions(-)"
      const match = lastLine.match(/(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?/);
      
      if (match) {
        return {
          total_files_changed: parseInt(match[1]),
          total_additions: parseInt(match[2]) || 0,
          total_deletions: parseInt(match[3]) || 0
        };
      }
      
      return { total_files_changed: 0, total_additions: 0, total_deletions: 0 };
      
    } catch (error) {
      console.warn('Error getting diff summary:', error.message);
      return { total_files_changed: 0, total_additions: 0, total_deletions: 0 };
    }
  }

  // Get repository file tree for UI display
  async getRepositoryFileTree(repositoryId, options = {}) {
    const {
      includeChanges = false,
      baseCommit = null,
      targetCommit = null
    } = options;

    try {
      // Get directories from database
      const dirsQuery = `
        SELECT 
          rd.id,
          rd.directory_name,
          rd.relative_path,
          rd.level,
          rd.parent_directory_id,
          COUNT(rf.id) as files_count,
          COUNT(rd2.id) as subdirectories_count
        FROM repository_directories rd
        LEFT JOIN repository_files rf ON rd.id = rf.directory_id
        LEFT JOIN repository_directories rd2 ON rd.id = rd2.parent_directory_id
        WHERE rd.repository_id = $1
        GROUP BY rd.id, rd.directory_name, rd.relative_path, rd.level, rd.parent_directory_id
        ORDER BY rd.level, rd.relative_path
      `;
      
      const dirsResult = await database.query(dirsQuery, [repositoryId]);
      const directories = dirsResult.rows;
      
      // Get files from database
      const filesQuery = `
        SELECT 
          rf.filename,
          rf.file_extension,
          rf.relative_path,
          rf.absolute_path,
          rf.file_size_bytes,
          rf.is_binary,
          rf.mime_type,
          rd.relative_path as directory_path
        FROM repository_files rf
        LEFT JOIN repository_directories rd ON rf.directory_id = rd.id
        WHERE rf.repository_id = $1
        ORDER BY rf.relative_path
      `;
      
      const filesResult = await database.query(filesQuery, [repositoryId]);
      const files = filesResult.rows;
      
      // Build tree structure
      const fileTree = this.buildFileTreeForUI(directories, files);
      
      // Add change indicators if requested
      if (includeChanges && baseCommit && targetCommit) {
        const changes = await this.getFileChanges(repositoryId, baseCommit, targetCommit);
        this.addChangeIndicators(fileTree, changes);
      }
      
      return fileTree;
      
    } catch (error) {
      console.error('Error getting file tree:', error);
      throw error;
    }
  }

  // Build file tree for UI display
  buildFileTreeForUI(directories, files) {
    // Create a map of directories by ID for easier lookup
    const dirMap = new Map();
    directories.forEach(dir => {
      dirMap.set(dir.id, dir);
    });
    
    // Find root directories (parent_directory_id is null) and exclude .git folder
    const rootDirs = directories.filter(dir => 
      dir.parent_directory_id === null && dir.directory_name !== '.git'
    );
    
    // Build tree recursively starting from root directories
    const root = {
      type: 'directory',
      name: 'root',
      path: '',
      level: -1,
      children: []
    };
    
    // Add root directories to the tree
    rootDirs.forEach(dir => {
      const dirNode = this.buildDirectoryNode(dir, dirMap, files);
      root.children.push(dirNode);
    });
    
    return root;
  }

  // Build directory node recursively
  buildDirectoryNode(dir, dirMap, files) {
    const dirNode = {
      type: 'directory',
      name: dir.directory_name,
      path: dir.relative_path,
      level: dir.level,
      files_count: parseInt(dir.files_count),
      subdirectories_count: parseInt(dir.subdirectories_count),
      children: []
    };
    
    // Find subdirectories (excluding .git folder)
    const subdirs = Array.from(dirMap.values()).filter(subdir => 
      subdir.parent_directory_id === dir.id && subdir.directory_name !== '.git'
    );
    
    // Add subdirectories
    subdirs.forEach(subdir => {
      const subdirNode = this.buildDirectoryNode(subdir, dirMap, files);
      dirNode.children.push(subdirNode);
    });
    
    // Add files in this directory
    const dirFiles = files.filter(file => {
      const pathParts = file.relative_path.split('/');
      const fileName = pathParts.pop();
      const dirPath = pathParts.join('/');
      return dirPath === dir.relative_path;
    });
    
    dirFiles.forEach(file => {
      const pathParts = file.relative_path.split('/');
      const fileName = pathParts.pop();
      
      dirNode.children.push({
        type: 'file',
        name: fileName,
        path: file.relative_path,
        // absolute_path: file.absolute_path, // Removed for security and cleaner response
        size_bytes: file.file_size_bytes,
        extension: file.file_extension,
        is_binary: file.is_binary,
        mime_type: file.mime_type
      });
    });
    
    return dirNode;
  }

  // Get file changes between commits
  async getFileChanges(repositoryId, baseCommit, targetCommit) {
    try {
      // Get repository storage path
      const repoQuery = `
        SELECT rs.local_path
        FROM repository_storage rs
        WHERE rs.repository_id = $1
      `;
      
      const repoResult = await database.query(repoQuery, [repositoryId]);
      
      if (repoResult.rows.length === 0) {
        return {};
      }
      
      const localPath = repoResult.rows[0].local_path;
      
      // Execute git diff command
      const { exec } = require('child_process');
      const util = require('util');
      const execAsync = util.promisify(exec);
      
      const gitDiffCommand = `cd "${localPath}" && git diff --name-status ${baseCommit}..${targetCommit}`;
      const { stdout } = await execAsync(gitDiffCommand);
      
      // Parse git diff output
      const changes = {};
      const lines = stdout.trim().split('\n');
      
      lines.forEach(line => {
        if (line.trim()) {
          const [status, filePath] = line.split('\t');
          changes[filePath] = {
            status: status.charAt(0), // M, A, D, R, etc.
            additions: 0,
            deletions: 0
          };
        }
      });
      
      return changes;
      
    } catch (error) {
      console.warn('Error getting file changes:', error.message);
      return {};
    }
  }

  // Add change indicators to file tree
  addChangeIndicators(tree, changes) {
    if (tree.type === 'file') {
      const change = changes[tree.path];
      if (change) {
        tree.change_status = change.status;
        tree.additions = change.additions;
        tree.deletions = change.deletions;
      }
    }
    
    if (tree.children) {
      tree.children.forEach(child => {
        this.addChangeIndicators(child, changes);
      });
    }
  }

  // Get file diff for specific file
  async getFileDiff(repositoryId, filePath, baseCommit, targetCommit) {
    try {
      // Get repository storage path
      const repoQuery = `
        SELECT rs.local_path
        FROM repository_storage rs
        WHERE rs.repository_id = $1
      `;
      
      const repoResult = await database.query(repoQuery, [repositoryId]);
      
      if (repoResult.rows.length === 0) {
        return { changes: [] };
      }
      
      const localPath = repoResult.rows[0].local_path;
      
      // Execute git diff for specific file
      const { exec } = require('child_process');
      const util = require('util');
      const execAsync = util.promisify(exec);
      
      const gitDiffCommand = `cd "${localPath}" && git diff ${baseCommit}..${targetCommit} -- "${filePath}"`;
      const { stdout } = await execAsync(gitDiffCommand);
      
      // Parse git diff output
      const changes = this.parseGitDiff(stdout);
      
      return { changes };
      
    } catch (error) {
      console.warn('Error getting file diff:', error.message);
      return { changes: [] };
    }
  }

  // Parse git diff output
  parseGitDiff(diffOutput) {
    const changes = [];
    const lines = diffOutput.split('\n');
    let currentOldLine = 0;
    let currentNewLine = 0;
    
    for (const line of lines) {
      if (line.startsWith('@@')) {
        // Parse hunk header: @@ -old_start,old_count +new_start,new_count @@
        const match = line.match(/@@ -(\d+),?(\d*) \+(\d+),?(\d*) @@/);
        if (match) {
          currentOldLine = parseInt(match[1]) - 1;
          currentNewLine = parseInt(match[3]) - 1;
        }
      } else if (line.startsWith('+') && !line.startsWith('+++')) {
        // Addition
        currentNewLine++;
        changes.push({
          type: 'addition',
          old_line_number: null,
          new_line_number: currentNewLine,
          content: line.substring(1)
        });
      } else if (line.startsWith('-') && !line.startsWith('---')) {
        // Deletion
        currentOldLine++;
        changes.push({
          type: 'deletion',
          old_line_number: currentOldLine,
          new_line_number: null,
          content: line.substring(1)
        });
      } else if (line.startsWith(' ')) {
        // Context
        currentOldLine++;
        currentNewLine++;
        changes.push({
          type: 'context',
          old_line_number: currentOldLine,
          new_line_number: currentNewLine,
          content: line.substring(1)
        });
      }
    }
    
    return changes;
  }

  // Get diff summary
  async getDiffSummary(repositoryId, baseCommit, targetCommit) {
    try {
      // Get repository storage path
      const repoQuery = `
        SELECT rs.local_path
        FROM repository_storage rs
        WHERE rs.repository_id = $1
      `;
      
      const repoResult = await database.query(repoQuery, [repositoryId]);
      
      if (repoResult.rows.length === 0) {
        return { total_files_changed: 0, total_additions: 0, total_deletions: 0 };
      }
      
      const localPath = repoResult.rows[0].local_path;
      
      // Execute git diff --stat command
      const { exec } = require('child_process');
      const util = require('util');
      const execAsync = util.promisify(exec);
      
      const gitStatCommand = `cd "${localPath}" && git diff --stat ${baseCommit}..${targetCommit}`;
      const { stdout } = await execAsync(gitStatCommand);
      
      // Parse git stat output
      const lines = stdout.trim().split('\n');
      const lastLine = lines[lines.length - 1];
      
      // Extract summary from last line: "X files changed, Y insertions(+), Z deletions(-)"
      const match = lastLine.match(/(\d+) files? changed(?:, (\d+) insertions?\(\+\))?(?:, (\d+) deletions?\(-\))?/);
      
      if (match) {
        return {
          total_files_changed: parseInt(match[1]),
          total_additions: parseInt(match[2]) || 0,
          total_deletions: parseInt(match[3]) || 0
        };
      }
      
      return { total_files_changed: 0, total_additions: 0, total_deletions: 0 };
      
    } catch (error) {
      console.warn('Error getting diff summary:', error.message);
      return { total_files_changed: 0, total_additions: 0, total_deletions: 0 };
    }
  }

  // Detect programming language from file extension and content
  detectLanguage(fileExtension, content) {
    const extension = fileExtension ? fileExtension.toLowerCase() : '';
    
    const languageMap = {
      '.js': 'javascript',
      '.jsx': 'javascript',
      '.ts': 'typescript',
      '.tsx': 'typescript',
      '.py': 'python',
      '.java': 'java',
      '.cpp': 'cpp',
      '.c': 'c',
      '.cs': 'csharp',
      '.php': 'php',
      '.rb': 'ruby',
      '.go': 'go',
      '.rs': 'rust',
      '.kt': 'kotlin',
      '.swift': 'swift',
      '.scala': 'scala',
      '.clj': 'clojure',
      '.hs': 'haskell',
      '.elm': 'elm',
      '.ml': 'ocaml',
      '.fs': 'fsharp',
      '.vb': 'vbnet',
      '.pas': 'pascal',
      '.asm': 'assembly',
      '.sql': 'sql',
      '.sh': 'bash',
      '.bash': 'bash',
      '.ps1': 'powershell',
      '.bat': 'batch',
      '.cmd': 'batch',
      '.html': 'html',
      '.htm': 'html',
      '.xml': 'xml',
      '.css': 'css',
      '.scss': 'scss',
      '.sass': 'sass',
      '.less': 'less',
      '.json': 'json',
      '.yaml': 'yaml',
      '.yml': 'yaml',
      '.toml': 'toml',
      '.ini': 'ini',
      '.cfg': 'ini',
      '.conf': 'ini',
      '.env': 'env',
      '.md': 'markdown',
      '.txt': 'text',
      '.rst': 'restructuredtext',
      '.adoc': 'asciidoc',
      '.tex': 'latex',
      '.r': 'r',
      '.m': 'matlab',
      '.pl': 'perl',
      '.lua': 'lua',
      '.dart': 'dart',
      '.jl': 'julia',
      '.nim': 'nim',
      '.zig': 'zig',
      '.v': 'verilog',
      '.d': 'd',
      '.cr': 'crystal',
      '.ex': 'elixir',
      '.exs': 'elixir'
    };

    return languageMap[extension] || 'text';
  }

  // Read file content for AI analysis with performance optimizations
  async readFileContentForAI(filePath, isBinary) {
    if (isBinary) {
      return null; // Don't read binary files
    }

    try {
      // Check file size first to avoid reading huge files
      const stats = fs.statSync(filePath);
      if (stats.size > 1000000) { // 1MB limit
        console.warn(`File ${filePath} is too large (${stats.size} bytes), skipping content`);
        return null;
      }

      // Try UTF-8 first
      const content = fs.readFileSync(filePath, 'utf8');

      // Truncate very long files to prevent memory issues
      if (content.length > 500000) { // 500KB content limit
        console.warn(`File ${filePath} content is too long (${content.length} chars), truncating`);
        return content.substring(0, 500000) + '\n\n... [Content truncated for performance]';
      }

      return content;
    } catch (error) {
      try {
        // Fallback to Latin-1
        const content = fs.readFileSync(filePath, 'latin1');

        // Truncate if too long
        if (content.length > 500000) {
          return content.substring(0, 500000) + '\n\n... [Content truncated for performance]';
        }

        return content;
      } catch (fallbackError) {
        console.warn(`Could not read file ${filePath}:`, fallbackError.message);
        return null;
      }
    }
  }

  async cleanupRepositoryStorage(repositoryId) {
    const queries = [
      'DELETE FROM repository_files WHERE repository_id = $1',
      'DELETE FROM repository_directories WHERE repository_id = $1',
      'DELETE FROM repository_storage WHERE repository_id = $1'
    ];

    for (const query of queries) {
      await database.query(query, [repositoryId]);
    }
  }
}

module.exports = FileStorageService;
