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

class AIStreamingService {
  constructor() {
    this.fileStorageService = new FileStorageService();
    this.activeStreams = new Map(); // Track active streaming sessions
    this.chunkSize = 200; // Files per chunk
    this.maxFileSize = 1000000; // 1MB per file
    this.maxContentLength = 500000; // 500KB content limit
  }

  // Auto-detect available file types in repository
  async getAvailableFileTypes(repositoryId) {
    try {
      const query = `
        SELECT DISTINCT file->>'file_extension' as file_extension
        FROM repository_files rf,
             jsonb_array_elements(rf.files) as file
        WHERE rf.repository_id = $1
        AND file->>'file_extension' IS NOT NULL
        ORDER BY file->>'file_extension'
      `;
      
      const result = await database.query(query, [repositoryId]);
      const fileTypes = result.rows.map(row => row.file_extension);
      
      // Include files without extensions (empty string or null)
      const hasNoExtension = await database.query(`
        SELECT COUNT(*) as count
        FROM repository_files rf,
             jsonb_array_elements(rf.files) as file
        WHERE rf.repository_id = $1
        AND (file->>'file_extension' IS NULL OR file->>'file_extension' = '')
      `, [repositoryId]);
      
      if (parseInt(hasNoExtension.rows[0].count) > 0) {
        // Only add if not already present
        if (!fileTypes.includes('')) {
          fileTypes.push(''); // Add empty string for files without extensions
        }
      }
      
      console.log(`🔍 Auto-detected file types: ${fileTypes.join(', ')}`);
      console.log(`📊 Total file types found: ${fileTypes.length}`);
      return fileTypes;
      
    } catch (error) {
      console.error('Error auto-detecting file types:', error);
      // Fallback to common file types
      return ['js', 'jsx', 'ts', 'tsx', 'py', 'java', 'json', 'md', 'txt', 'css', 'html', 'php', 'rb', 'go', 'rs', 'kt', 'swift', 'scala', 'clj', 'hs', 'elm', 'ml', 'fs', 'vb', 'pas', 'asm', 'sql', 'sh', 'bash', 'ps1', 'bat', 'cmd', 'xml', 'scss', 'sass', 'less', 'toml', 'ini', 'cfg', 'conf', 'env', 'rst', 'adoc', 'tex', 'r', 'm', 'pl', 'lua', 'dart', 'jl', 'nim', 'zig', 'v', 'd', 'cr', 'ex', 'exs'];
    }
  }

  // Calculate optimal chunk size based on total files
  calculateOptimalChunkSize(totalFiles) {
    if (totalFiles <= 50) {
      return 10; // Small repos: 10 files per chunk
    } else if (totalFiles <= 200) {
      return 25; // Medium repos: 25 files per chunk
    } else if (totalFiles <= 500) {
      return 50; // Large repos: 50 files per chunk
    } else if (totalFiles <= 1000) {
      return 100; // Very large repos: 100 files per chunk
    } else {
      return 200; // Huge repos: 200 files per chunk
    }
  }

  // Get repository files count for streaming planning
  async getRepositoryFilesCount(repositoryId, options = {}) {
    const {
      fileTypes = ['js', 'jsx', 'ts', 'tsx', 'py', 'java', 'json', 'md', 'txt', 'css', 'html', 'php', 'rb', 'go', 'rs', 'kt', 'swift', 'scala', 'clj', 'hs', 'elm', 'ml', 'fs', 'vb', 'pas', 'asm', 'sql', 'sh', 'bash', 'ps1', 'bat', 'cmd', 'xml', 'scss', 'sass', 'less', 'toml', 'ini', 'cfg', 'conf', 'env', 'rst', 'adoc', 'tex', 'r', 'm', 'pl', 'lua', 'dart', 'jl', 'nim', 'zig', 'v', 'd', 'cr', 'ex', 'exs'],
      maxSize = 3000000, // Increased to 3MB
      includeBinary = true, // Changed to true to include binary files
      directoryFilter = '',
      excludePatterns = ['node_modules', 'dist', 'build', '.git', 'coverage']
    } = options;

    try {
      // Build conditions for JSONB query
      let conditions = ['rf.repository_id = $1'];
      const params = [repositoryId];
      let paramIndex = 2;

      // File type filter
      if (fileTypes.length > 0) {
        const typeConditions = [];
        const normalizedFileTypes = [];
        
        for (const ext of fileTypes) {
          if (ext === '') {
            // Files without extensions
            typeConditions.push(`(file->>'file_extension' IS NULL OR file->>'file_extension' = '')`);
          } else {
            // Files with extensions
            const normalizedExt = ext.startsWith('.') ? ext : `.${ext}`;
            typeConditions.push(`file->>'file_extension' = $${paramIndex + normalizedFileTypes.length}`);
            normalizedFileTypes.push(normalizedExt);
          }
        }
        
        if (typeConditions.length > 0) {
          conditions.push(`(${typeConditions.join(' OR ')})`);
          params.push(...normalizedFileTypes);
          paramIndex += normalizedFileTypes.length;
        }
      }

      // Size filter
      if (maxSize > 0) {
        conditions.push(`(file->>'file_size_bytes')::bigint <= $${paramIndex}`);
        params.push(maxSize);
        paramIndex++;
      }

      // Binary filter
      if (!includeBinary) {
        conditions.push(`(file->>'is_binary')::boolean = false`);
      }

      // Directory filter
      if (directoryFilter) {
        conditions.push(`rf.relative_path LIKE $${paramIndex}`);
        params.push(`%${directoryFilter}%`);
        paramIndex++;
      }

      // Exclude patterns
      if (excludePatterns.length > 0) {
        const excludeConditions = excludePatterns.map((_, index) => 
          `file->>'relative_path' NOT LIKE $${paramIndex + index}`
        ).join(' AND ');
        conditions.push(`(${excludeConditions})`);
        excludePatterns.forEach(pattern => {
          params.push(`%${pattern}%`);
        });
        paramIndex += excludePatterns.length;
      }

      const query = `
        SELECT COUNT(*) as total_count
        FROM repository_files rf,
             jsonb_array_elements(rf.files) as file
        WHERE ${conditions.join(' AND ')}
      `;

      const result = await database.query(query, params);
      return parseInt(result.rows[0].total_count);

    } catch (error) {
      console.error('Error getting repository files count:', error);
      return 0;
    }
  }

  // Get files for a specific chunk
  async getFilesChunk(repositoryId, offset, limit, options = {}) {
    const {
      fileTypes = ['js', 'jsx', 'ts', 'tsx', 'py', 'java', 'json', 'md', 'txt', 'css', 'html', 'php', 'rb', 'go', 'rs', 'kt', 'swift', 'scala', 'clj', 'hs', 'elm', 'ml', 'fs', 'vb', 'pas', 'asm', 'sql', 'sh', 'bash', 'ps1', 'bat', 'cmd', 'xml', 'scss', 'sass', 'less', 'toml', 'ini', 'cfg', 'conf', 'env', 'rst', 'adoc', 'tex', 'r', 'm', 'pl', 'lua', 'dart', 'jl', 'nim', 'zig', 'v', 'd', 'cr', 'ex', 'exs'],
      maxSize = 3000000, // Increased to 3MB
      includeBinary = true, // Changed to true to include binary files
      directoryFilter = '',
      excludePatterns = ['node_modules', 'dist', 'build', '.git', 'coverage']
    } = options;

    try {
      // Build conditions for JSONB query
      let conditions = ['rf.repository_id = $1'];
      const params = [repositoryId];
      let paramIndex = 2;

      // File type filter
      if (fileTypes.length > 0) {
        const typeConditions = [];
        const normalizedFileTypes = [];
        
        for (const ext of fileTypes) {
          if (ext === '') {
            // Files without extensions
            typeConditions.push(`(file->>'file_extension' IS NULL OR file->>'file_extension' = '')`);
          } else {
            // Files with extensions
            const normalizedExt = ext.startsWith('.') ? ext : `.${ext}`;
            typeConditions.push(`file->>'file_extension' = $${paramIndex + normalizedFileTypes.length}`);
            normalizedFileTypes.push(normalizedExt);
          }
        }
        
        if (typeConditions.length > 0) {
          conditions.push(`(${typeConditions.join(' OR ')})`);
          params.push(...normalizedFileTypes);
          paramIndex += normalizedFileTypes.length;
        }
      }

      // Size filter
      if (maxSize > 0) {
        conditions.push(`(file->>'file_size_bytes')::bigint <= $${paramIndex}`);
        params.push(maxSize);
        paramIndex++;
      }

      // Binary filter
      if (!includeBinary) {
        conditions.push(`(file->>'is_binary')::boolean = false`);
      }

      // Directory filter
      if (directoryFilter) {
        conditions.push(`rf.relative_path LIKE $${paramIndex}`);
        params.push(`%${directoryFilter}%`);
        paramIndex++;
      }

      // Exclude patterns
      if (excludePatterns.length > 0) {
        const excludeConditions = excludePatterns.map((_, index) => 
          `file->>'relative_path' NOT LIKE $${paramIndex + index}`
        ).join(' AND ');
        conditions.push(`(${excludeConditions})`);
        excludePatterns.forEach(pattern => {
          params.push(`%${pattern}%`);
        });
        paramIndex += excludePatterns.length;
      }

      const query = `
        SELECT 
          COALESCE(rf.directory_id, rf.id) as id,
          file->>'filename' as filename,
          file->>'relative_path' as relative_path,
          file->>'file_extension' as file_extension,
          (file->>'file_size_bytes')::bigint as file_size_bytes,
          file->>'mime_type' as mime_type,
          (file->>'is_binary')::boolean as is_binary,
          file->>'absolute_path' as absolute_path,
          rf.relative_path as directory_path
        FROM repository_files rf,
             jsonb_array_elements(rf.files) as file
        WHERE ${conditions.join(' AND ')}
        ORDER BY file->>'relative_path'
        LIMIT $${paramIndex} OFFSET $${paramIndex + 1}
      `;
      
      params.push(limit, offset);

      const result = await database.query(query, params);
      return result.rows;

    } catch (error) {
      console.error('Error getting files chunk:', error);
      throw error;
    }
  }

  // Read file content for AI analysis
  async readFileContentForAI(filePath, isBinary) {
    if (isBinary) {
      return null;
    }

    try {
      // Check file size first
      const stats = fs.statSync(filePath);
      if (stats.size > this.maxFileSize) {
        console.warn(`File ${filePath} is too large (${stats.size} bytes), skipping content`);
        return null;
      }

      // Read file content
      const content = fs.readFileSync(filePath, 'utf8');
      
      // Truncate if too long
      if (content.length > this.maxContentLength) {
        console.warn(`File ${filePath} content is too long (${content.length} chars), truncating`);
        return content.substring(0, this.maxContentLength) + '\n\n... [Content truncated for performance]';
      }
      
      return content;
    } catch (error) {
      try {
        // Fallback to Latin-1
        const content = fs.readFileSync(filePath, 'latin1');
        
        if (content.length > this.maxContentLength) {
          return content.substring(0, this.maxContentLength) + '\n\n... [Content truncated for performance]';
        }
        
        return content;
      } catch (fallbackError) {
        console.warn(`Could not read file ${filePath}:`, fallbackError.message);
        return null;
      }
    }
  }

  // Detect programming language
  detectLanguage(fileExtension, content) {
    const 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'
    };

    return languageMap[fileExtension] || 'unknown';
  }

  // Detect framework
  detectFramework(content, filename) {
    if (!content) return null;

    const frameworkHints = {
      'react': /import.*from\s+['"]react['"]|React\.|useState|useEffect|jsx/i,
      'vue': /import.*from\s+['"]vue['"]|Vue\.|createApp|defineComponent/i,
      'angular': /@angular|@Component|@Injectable|@NgModule/i,
      'express': /require\s*\(\s*['"]express['"]|app\.get|app\.post|app\.use/i,
      'fastapi': /from\s+fastapi|@app\.|FastAPI\(/i,
      'django': /from\s+django|@csrf_exempt|HttpResponse/i,
      'spring': /@SpringBootApplication|@RestController|@Service|@Repository/i,
      'laravel': /use\s+Illuminate|Route::|@extends\s+['"]layouts/i
    };

    for (const [framework, pattern] of Object.entries(frameworkHints)) {
      if (pattern.test(content)) {
        return framework;
      }
    }

    return null;
  }

  // Generate analysis hints
  generateAnalysisHints(file, content, language) {
    if (!content) return {};

    const hints = {
      is_component: false,
      has_imports: false,
      has_exports: false,
      has_functions: false,
      has_classes: false,
      complexity_level: 'low',
      framework_hints: []
    };

    // Check for imports
    hints.has_imports = /import\s+.*from|require\s*\(/.test(content);
    
    // Check for exports
    hints.has_exports = /export\s+|module\.exports|exports\./.test(content);
    
    // Check for functions
    hints.has_functions = /function\s+\w+|const\s+\w+\s*=\s*\(|def\s+\w+/.test(content);
    
    // Check for classes
    hints.has_classes = /class\s+\w+|@Component|@Service/.test(content);

    // Check if it's a component (React, Vue, Angular)
    if (language === 'javascript' || language === 'typescript') {
      hints.is_component = /export\s+default|export\s+const.*=.*\(/i.test(content) && 
                          (hints.has_imports || /jsx|tsx/.test(file.file_extension));
    }

    // Calculate complexity
    const lines = content.split('\n').length;
    const functions = (content.match(/function\s+\w+|const\s+\w+\s*=\s*\(|def\s+\w+/g) || []).length;
    const classes = (content.match(/class\s+\w+/g) || []).length;
    
    if (lines > 500 || functions > 20 || classes > 10) {
      hints.complexity_level = 'high';
    } else if (lines > 200 || functions > 10 || classes > 5) {
      hints.complexity_level = 'medium';
    }

    return hints;
  }

  // Process files chunk for AI analysis
  async processFilesChunk(files, chunkNumber, totalChunks) {
    const processedFiles = [];
    const startTime = Date.now();

    for (const file of files) {
      try {
        // Read file content
        const content = await this.readFileContentForAI(file.absolute_path, file.is_binary);
        
        // Detect language and framework
        const language = this.detectLanguage(file.file_extension, content);
        const framework = this.detectFramework(content, file.filename);
        
        // Generate analysis hints
        const analysisHints = this.generateAnalysisHints(file, content, language);

        const processedFile = {
          id: file.id,
          filename: file.filename,
          relative_path: file.relative_path,
          file_extension: file.file_extension,
          file_size_bytes: file.file_size_bytes,
          mime_type: file.mime_type,
          language: language,
          is_binary: file.is_binary,
          line_count: content ? content.split('\n').length : 0,
          char_count: content ? content.length : 0,
          content: content,
          analysis_hints: analysisHints,
          framework: framework,
          chunk_info: {
            chunk_number: chunkNumber,
            total_chunks: totalChunks,
            file_index_in_chunk: processedFiles.length + 1,
            total_files_in_chunk: files.length
          }
        };

        processedFiles.push(processedFile);

      } catch (error) {
        console.warn(`Error processing file ${file.relative_path}:`, error.message);
        // Continue with other files
      }
    }

    const processingTime = Date.now() - startTime;
    
    return {
      files: processedFiles,
      chunk_number: chunkNumber,
      total_chunks: totalChunks,
      files_processed: processedFiles.length,
      processing_time_ms: processingTime,
      timestamp: new Date().toISOString()
    };
  }

  // Create streaming session
  createStreamingSession(repositoryId, options = {}) {
    const sessionId = `stream_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    this.activeStreams.set(sessionId, {
      repositoryId,
      options,
      status: 'initializing',
      currentChunk: 0,
      totalChunks: 0,
      totalFiles: 0,
      processedFiles: 0,
      startTime: Date.now(),
      lastActivity: Date.now()
    });

    return sessionId;
  }

  // Get streaming session
  getStreamingSession(sessionId) {
    return this.activeStreams.get(sessionId);
  }

  // Update streaming session
  updateStreamingSession(sessionId, updates) {
    const session = this.activeStreams.get(sessionId);
    if (session) {
      Object.assign(session, updates);
      session.lastActivity = Date.now();
    }
  }

  // Remove streaming session
  removeStreamingSession(sessionId) {
    this.activeStreams.delete(sessionId);
  }

  // Clean up old sessions (older than 1 hour)
  cleanupOldSessions() {
    const oneHourAgo = Date.now() - (60 * 60 * 1000);
    
    for (const [sessionId, session] of this.activeStreams.entries()) {
      if (session.lastActivity < oneHourAgo) {
        this.activeStreams.delete(sessionId);
        console.log(`Cleaned up old streaming session: ${sessionId}`);
      }
    }
  }

  // Get repository info for streaming
  async getRepositoryInfo(repositoryId) {
    try {
      const query = `
        SELECT 
          gr.id,
          gr.repository_name as name,
          gr.repository_url,
          gr.owner_name,
          gr.branch_name,
          gr.is_public,
          gr.requires_auth,
          gr.last_synced_at,
          gr.sync_status,
          gr.metadata,
          gr.codebase_analysis,
          gr.created_at,
          gr.updated_at,
          rs.local_path,
          rs.storage_status
        FROM all_repositories gr
        LEFT JOIN repository_storage rs ON gr.id = rs.repository_id
        WHERE gr.id = $1
      `;
      
      const result = await database.query(query, [repositoryId]);
      
      if (result.rows.length === 0) {
        throw new Error('Repository not found');
      }

      const repo = result.rows[0];
      
      // Create a full_name from owner_name and repository_name
      const fullName = `${repo.owner_name}/${repo.repository_name}`;
      
      return {
        ...repo,
        full_name: fullName,
        description: repo.metadata?.description || '',
        language: repo.metadata?.language || 'Unknown',
        size: repo.metadata?.size || 0,
        stars_count: repo.metadata?.stargazers_count || 0,
        forks_count: repo.metadata?.forks_count || 0
      };
    } catch (error) {
      console.error('Error getting repository info:', error);
      throw error;
    }
  }

  // Store diff content for AI analysis
  async storeDiffContent(diffData) {
    const {
      repositoryId,
      diffContent,
      commitSha,
      parentSha,
      commitMessage,
      authorName,
      authorEmail,
      commitDate,
      fileChanges
    } = diffData;

    try {
      // First, create or get commit record
      let commitId;
      const commitQuery = `
        SELECT id FROM repository_commit_details 
        WHERE repository_id = $1 AND commit_sha = $2
      `;
      const commitResult = await database.query(commitQuery, [repositoryId, commitSha]);
      
      if (commitResult.rows.length === 0) {
        // Create new commit record
        const insertCommitQuery = `
          INSERT INTO repository_commit_details (
            repository_id, commit_sha, message, 
            author_name, author_email, committed_at, created_at
          ) VALUES ($1, $2, $3, $4, $5, $6, $7, NOW())
          RETURNING id
        `;
        const commitInsertResult = await database.query(insertCommitQuery, [
          repositoryId, commitSha, parentSha, commitMessage,
          authorName, authorEmail, commitDate
        ]);
        commitId = commitInsertResult.rows[0].id;
      } else {
        commitId = commitResult.rows[0].id;
      }

      // Store diff content
      const diffId = require('crypto').randomUUID();
      const diffSize = Buffer.byteLength(diffContent, 'utf8');
      
      // Store diff content in external file
      const diffStoragePath = await this.storeDiffToFile(diffId, diffContent);
      
      const insertDiffQuery = `
        INSERT INTO diff_contents (
          id, commit_id, diff_header, diff_size_bytes, storage_type,
          external_storage_path, file_path, change_type, processing_status
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
        ON CONFLICT (commit_id, file_path) 
        DO UPDATE SET
          diff_header = EXCLUDED.diff_header,
          diff_size_bytes = EXCLUDED.diff_size_bytes,
          storage_type = EXCLUDED.storage_type,
          external_storage_path = EXCLUDED.external_storage_path,
          change_type = EXCLUDED.change_type,
          processing_status = EXCLUDED.processing_status,
          updated_at = NOW()
        RETURNING id
      `;
      
      const diffResult = await database.query(insertDiffQuery, [
        diffId,
        commitId,
        `diff --git commit ${commitSha}`,
        diffSize,
        'external',
        diffStoragePath,
        'multiple_files',
        'modified',
        'pending'
      ]);

      // Process file changes if provided
      if (fileChanges && fileChanges.length > 0) {
        await this.processFileChanges(commitId, fileChanges);
      }

      return diffId;
    } catch (error) {
      console.error('Error storing diff content:', error);
      throw error;
    }
  }

  // Store diff content to external file
  async storeDiffToFile(diffId, diffContent) {
    console.log('🔍 storeDiffToFile called with:', {
      diffId,
      hasContent: !!diffContent,
      contentLength: diffContent ? diffContent.length : 'undefined'
    });
    
    if (!diffContent) {
      throw new Error('diffContent is undefined in storeDiffToFile');
    }
    
    // Use DIFF_STORAGE_DIR instead of ATTACHED_REPOS_DIR
    const diffDir = process.env.DIFF_STORAGE_DIR || '/home/tech4biz/Desktop/today work/git-diff';
    if (!fs.existsSync(diffDir)) {
      fs.mkdirSync(diffDir, { recursive: true });
    }
    
    const diffFilePath = path.join(diffDir, `${diffId}.diff`);
    fs.writeFileSync(diffFilePath, diffContent, 'utf8');
    
    return diffFilePath;
  }

  // Process file changes for commit
  async processFileChanges(commitId, fileChanges) {
    for (const fileChange of fileChanges) {
      try {
        const insertFileQuery = `
          INSERT INTO repository_commit_files (
            commit_id, file_path, change_type, created_at
          ) VALUES ($1, $2, $3, NOW())
        `;
        
        await database.query(insertFileQuery, [
          commitId,
          fileChange.file_path,
          fileChange.change_type || 'modified'
        ]);
      } catch (error) {
        console.warn(`Error processing file change for ${fileChange.file_path}:`, error.message);
      }
    }
  }

  // Get repository diffs
  async getRepositoryDiffs(repositoryId, options = {}) {
    const { limit = 10, offset = 0 } = options;
    
    try {
      const query = `
        SELECT 
          dc.id as diff_id,
          dc.diff_header,
          dc.diff_size_bytes,
          dc.external_storage_path,
          dc.change_type,
          dc.processing_status,
          dc.created_at,
          rcd.commit_sha,
          rcd.parent_sha,
          rcd.message,
          rcd.author_name,
          rcd.author_email,
          rcd.committed_at
        FROM diff_contents dc
        JOIN repository_commit_details rcd ON dc.commit_id = rcd.id
        WHERE rcd.repository_id = $1
        ORDER BY dc.created_at DESC
        LIMIT $2 OFFSET $3
      `;
      
      const result = await database.query(query, [repositoryId, limit, offset]);
      return result.rows;
    } catch (error) {
      console.error('Error getting repository diffs:', error);
      throw error;
    }
  }

  // Analyze diff content
  async analyzeDiffContent(repositoryId, diffId, options = {}) {
    const { includeContext = true } = options;
    
    try {
      // Get diff content
      const diffQuery = `
        SELECT 
          dc.*,
          rcd.commit_sha,
          rcd.parent_sha,
          rcd.message,
          rcd.author_name,
          rcd.author_email,
          rcd.committed_at
        FROM diff_contents dc
        JOIN repository_commit_details rcd ON dc.commit_id = rcd.id
        WHERE dc.id = $1 AND rcd.repository_id = $2
      `;
      
      const diffResult = await database.query(diffQuery, [diffId, repositoryId]);
      
      if (diffResult.rows.length === 0) {
        throw new Error('Diff not found');
      }
      
      const diff = diffResult.rows[0];
      
      // Read diff content from file
      const diffContent = fs.readFileSync(diff.external_storage_path, 'utf8');
      
      // Parse diff content
      const parsedDiff = this.parseDiffContent(diffContent);
      
      // Get repository context if requested
      let repositoryContext = null;
      if (includeContext) {
        repositoryContext = await this.getRepositoryInfo(repositoryId);
      }
      
      // Generate analysis
      const analysis = {
        diff_id: diffId,
        repository_id: repositoryId,
        commit_info: {
          commit_sha: diff.commit_sha,
          parent_sha: diff.parent_sha,
          commit_message: diff.message,
          author_name: diff.author_name,
          author_email: diff.author_email,
          commit_date: diff.committed_at
        },
        diff_summary: {
          total_files_changed: parsedDiff.files.length,
          total_additions: parsedDiff.totalAdditions,
          total_deletions: parsedDiff.totalDeletions,
          files_changed: parsedDiff.files.map(f => ({
            file_path: f.filePath,
            change_type: f.changeType,
            additions: f.additions,
            deletions: f.deletions
          }))
        },
        analysis_hints: this.generateDiffAnalysisHints(parsedDiff),
        repository_context: repositoryContext,
        created_at: diff.created_at
      };
      
      return analysis;
    } catch (error) {
      console.error('Error analyzing diff content:', error);
      throw error;
    }
  }

  // Parse diff content
  parseDiffContent(diffContent) {
    console.log('🔍 parseDiffContent called with:', {
      hasContent: !!diffContent,
      contentType: typeof diffContent,
      contentLength: diffContent ? diffContent.length : 'undefined'
    });
    
    if (!diffContent) {
      console.warn('⚠️ parseDiffContent received undefined/null diffContent');
      return {
        files: [],
        totalAdditions: 0,
        totalDeletions: 0
      };
    }
    
    const files = [];
    let totalAdditions = 0;
    let totalDeletions = 0;
    
    const lines = diffContent.split('\n');
    let currentFile = null;
    
    for (const line of lines) {
      if (line.startsWith('diff --git')) {
        if (currentFile) {
          files.push(currentFile);
        }
        currentFile = {
          filePath: this.extractFilePathFromDiffHeader(line),
          changeType: 'modified',
          additions: 0,
          deletions: 0,
          hunks: []
        };
      } else if (line.startsWith('+++') || line.startsWith('---')) {
        // Skip file headers
        continue;
      } else if (line.startsWith('@@')) {
        // Hunk header
        if (currentFile) {
          currentFile.hunks.push({
            header: line,
            lines: []
          });
        }
      } else if (line.startsWith('+')) {
        if (currentFile) {
          currentFile.additions++;
          totalAdditions++;
        }
      } else if (line.startsWith('-')) {
        if (currentFile) {
          currentFile.deletions++;
          totalDeletions++;
        }
      }
    }
    
    if (currentFile) {
      files.push(currentFile);
    }
    
    return {
      files,
      totalAdditions,
      totalDeletions
    };
  }

  // Extract file path from diff header
  extractFilePathFromDiffHeader(header) {
    const match = header.match(/diff --git a\/(.+) b\/(.+)/);
    if (match) {
      return match[2]; // Return the 'b' file path (new version)
    }
    return 'unknown';
  }

  // Generate diff analysis hints
  generateDiffAnalysisHints(parsedDiff) {
    const hints = {
      complexity_level: 'low',
      change_types: [],
      affected_areas: [],
      risk_level: 'low'
    };
    
    // Analyze change complexity
    const totalChanges = parsedDiff.totalAdditions + parsedDiff.totalDeletions;
    if (totalChanges > 1000) {
      hints.complexity_level = 'high';
    } else if (totalChanges > 100) {
      hints.complexity_level = 'medium';
    }
    
    // Analyze change types
    const changeTypes = new Set();
    const affectedAreas = new Set();
    
    for (const file of parsedDiff.files) {
      changeTypes.add(file.changeType);
      
      // Categorize by file type
      if (file.filePath.endsWith('.js') || file.filePath.endsWith('.ts')) {
        affectedAreas.add('frontend');
      } else if (file.filePath.endsWith('.py')) {
        affectedAreas.add('backend');
      } else if (file.filePath.endsWith('.sql')) {
        affectedAreas.add('database');
      } else if (file.filePath.endsWith('.md')) {
        affectedAreas.add('documentation');
      }
    }
    
    hints.change_types = Array.from(changeTypes);
    hints.affected_areas = Array.from(affectedAreas);
    
    // Assess risk level
    if (hints.affected_areas.includes('database') || hints.complexity_level === 'high') {
      hints.risk_level = 'high';
    } else if (hints.complexity_level === 'medium' || (parsedDiff.files && parsedDiff.files.length > 10)) {
      hints.risk_level = 'medium';
    }
    
    return hints;
  }

  // Create diff streaming session
  createDiffStreamingSession(repositoryId, options = {}) {
    const sessionId = `diff_stream_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
    
    this.activeStreams.set(sessionId, {
      repositoryId,
      options,
      status: 'initializing',
      currentChunk: 0,
      totalChunks: 0,
      totalFiles: 0,
      processedFiles: 0,
      startTime: Date.now(),
      lastActivity: Date.now(),
      sessionType: 'diff_analysis'
    });

    return sessionId;
  }

  // Stream diff analysis
  async streamDiffAnalysis(sessionId, repositoryId, diffId, options = {}) {
    const { includeContext = true } = options;
    const startTime = Date.now();
    const chunks = [];
    
    try {
      // Get diff analysis
      const analysis = await this.analyzeDiffContent(repositoryId, diffId, { includeContext });
      
      // Split analysis into chunks for streaming
      const chunkSize = 5; // Files per chunk
      const files = analysis.diff_summary.files_changed || [];
      const totalChunks = Math.ceil(files.length / chunkSize);
      
      for (let i = 0; i < files.length; i += chunkSize) {
        const chunkFiles = files.slice(i, i + chunkSize);
        const chunkNumber = Math.floor(i / chunkSize) + 1;
        
        const chunk = {
          chunk_number: chunkNumber,
          total_chunks: totalChunks,
          files: chunkFiles,
          analysis_hints: analysis.analysis_hints,
          progress: {
            current_chunk: chunkNumber,
            total_chunks: totalChunks,
            processed_files: Math.min(i + chunkSize, files.length),
            total_files: files.length,
            percentage: Math.round((Math.min(i + chunkSize, files.length) / files.length) * 100)
          }
        };
        
        chunks.push(chunk);
      }
      
      const processingTime = Date.now() - startTime;
      
      return {
        chunks,
        processingTime,
        totalFiles: files.length,
        totalChunks
      };
    } catch (error) {
      console.error('Error streaming diff analysis:', error);
      throw error;
    }
  }

  // Check if this is first-time analysis for a repository
  async isFirstTimeAnalysis(repositoryId, commitId) {
    try {
      const query = `
        SELECT COUNT(*) as count
        FROM diff_contents dc
        JOIN repository_commit_details rcd ON dc.commit_id = rcd.id
        WHERE rcd.repository_id = $1
      `;
      
      const result = await database.query(query, [repositoryId]);
      return parseInt(result.rows[0].count) === 0;
    } catch (error) {
      console.error('Error checking first-time analysis:', error);
      return true; // Default to first-time if error
    }
  }

  // Get diff content for a specific commit
  async getDiffForCommit(repoPath, commitId, isFirstTime) {
    try {
      // First, try to get stored diff content from database
      const storedDiffContent = await this.getStoredDiffFromDatabase(commitId);
      if (storedDiffContent) {
        console.log(`📁 Using stored diff content from database for commit ${commitId}`);
        return storedDiffContent;
      }

      // If no stored content in database, check if diff file exists locally
      const localDiffContent = await this.getStoredDiffContent(commitId);
      if (localDiffContent) {
        console.log(`📁 Using local diff file for commit ${commitId}`);
        return localDiffContent;
      }

      // If no diff content found anywhere, return null (don't generate new)
      console.log(`⚠️ No diff content found for commit ${commitId} - skipping analysis`);
      return null;
    } catch (error) {
      console.error('Error getting diff for commit:', error);
      return null;
    }
  }

  // Get stored diff content from database
  async getStoredDiffFromDatabase(commitId) {
    try {
      const query = `
        SELECT dc.external_storage_path, dc.diff_size_bytes
        FROM diff_contents dc
        JOIN repository_commit_details rcd ON dc.commit_id = rcd.id
        WHERE rcd.commit_sha = $1
      `;
      
      const result = await database.query(query, [commitId]);
      
      if (result.rows.length > 0) {
        const diffRecord = result.rows[0];
        const diffFilePath = diffRecord.external_storage_path;
        
        if (fs.existsSync(diffFilePath)) {
          const content = fs.readFileSync(diffFilePath, 'utf8');
          console.log(`📖 Found diff content from database: ${diffFilePath} (${content ? content.length : 'undefined'} characters)`);
          return content;
        }
      }
      
      return null;
    } catch (error) {
      console.error('Error reading diff content from database:', error);
      return null;
    }
  }

  // Get stored diff content from local files
  async getStoredDiffContent(commitId) {
    try {
      const diffDir = process.env.DIFF_STORAGE_DIR || '/home/tech4biz/Desktop/today work/git-diff';
      const commitDir = path.join(diffDir, commitId);
      
      if (fs.existsSync(commitDir)) {
        console.log(`📁 Found commit directory: ${commitDir}`);
        
        // Read all diff files in the commit directory
        const files = fs.readdirSync(commitDir);
        const diffFiles = files.filter(file => file.endsWith('.diff'));
        
        if (diffFiles.length > 0) {
          console.log(`📄 Found ${diffFiles.length} diff files in commit directory`);
          
          // Combine all diff files into one content
          let combinedContent = '';
          for (const diffFile of diffFiles) {
            const filePath = path.join(commitDir, diffFile);
            const content = fs.readFileSync(filePath, 'utf8');
            combinedContent += `\n--- ${diffFile} ---\n${content}\n`;
          }
          
          console.log(`📖 Combined diff content: ${combinedContent ? combinedContent.length : 'undefined'} characters`);
          return combinedContent;
        } else {
          console.log(`❌ No .diff files found in commit directory`);
          return null;
        }
      }
      
      // Fallback: check for single diff file (old format)
      const diffFilePath = path.join(diffDir, `${commitId}.diff`);
      if (fs.existsSync(diffFilePath)) {
        const content = fs.readFileSync(diffFilePath, 'utf8');
        console.log(`📖 Found stored diff file: ${diffFilePath} (${content ? content.length : 'undefined'} characters)`);
        return content;
      }
      
      return null;
    } catch (error) {
      console.error('Error reading stored diff content:', error);
      return null;
    }
  }

  // Store diff content locally for future use
  async storeDiffContentLocally(commitId, diffContent) {
    try {
      const diffDir = process.env.DIFF_STORAGE_DIR || '/home/tech4biz/Desktop/today work/git-diff';
      if (!fs.existsSync(diffDir)) {
        fs.mkdirSync(diffDir, { recursive: true });
      }
      
      const diffFilePath = path.join(diffDir, `${commitId}.diff`);
      fs.writeFileSync(diffFilePath, diffContent, 'utf8');
      console.log(`💾 Stored diff content locally: ${diffFilePath}`);
      
      return diffFilePath;
    } catch (error) {
      console.error('Error storing diff content locally:', error);
      return null;
    }
  }

  // Generate rich diff metadata using existing database tables
  async generateRichDiffMetadata(options) {
    const {
      repositoryId,
      commitId,
      diffContent,
      repositoryName,
      ownerName,
      isFirstTime,
      includeContext
    } = options;

    console.log('🔍 generateRichDiffMetadata called with:', {
      repositoryId,
      commitId,
      hasDiffContent: !!diffContent,
      diffContentLength: diffContent ? diffContent.length : 'undefined',
      repositoryName,
      ownerName,
      isFirstTime
    });

    // Get commit details from database
    const commitDetails = await this.getCommitDetailsFromDatabase(commitId);
    
    // Get file changes from database
    const fileChanges = await this.getFileChangesFromDatabase(commitId);
    
    // Parse diff content for additional analysis
    console.log('🔍 Parsing diff content...');
    const parsedDiff = this.parseDiffContent(diffContent);
    console.log('📊 Parsed diff result:', {
      hasFiles: !!parsedDiff.files,
      filesLength: parsedDiff.files ? parsedDiff.files.length : 'undefined',
      totalAdditions: parsedDiff.totalAdditions,
      totalDeletions: parsedDiff.totalDeletions
    });
    
    // Ensure parsedDiff has the expected structure
    if (!parsedDiff || !parsedDiff.files) {
      console.warn('⚠️ Parsed diff is missing files array, using empty array');
      parsedDiff.files = [];
    }
    if (!parsedDiff.totalAdditions) parsedDiff.totalAdditions = 0;
    if (!parsedDiff.totalDeletions) parsedDiff.totalDeletions = 0;
    
    // Generate comprehensive metadata using database data
    const metadata = {
      repository_id: repositoryId,
      commit_id: commitId,
      repository_name: repositoryName,
      owner_name: ownerName,
      full_name: `${ownerName}/${repositoryName}`,
      is_first_time: isFirstTime,
      analysis_timestamp: new Date().toISOString(),
      
      // Commit details from database
      commit_details: commitDetails,
      
      // File changes from database
      file_changes: fileChanges,
      
      // Additional analysis from diff content
      diff_analysis: {
        total_additions: parsedDiff.totalAdditions,
        total_deletions: parsedDiff.totalDeletions,
        total_changes: parsedDiff.totalAdditions + parsedDiff.totalDeletions,
        files_changed: parsedDiff.files.map(file => ({
          file_path: file.filePath,
          change_type: file.changeType,
          additions: file.additions,
          deletions: file.deletions,
          changes: file.additions + file.deletions,
          file_extension: path.extname(file.filePath),
          language: this.detectLanguage(path.extname(file.filePath)),
          is_binary: this.isBinaryFile(file.filePath),
          complexity_score: this.calculateFileComplexity(file)
        }))
      },
      
      // Analysis metadata
      complexity_score: this.calculateOverallComplexity(parsedDiff),
      risk_level: this.assessRiskLevel(parsedDiff),
      affected_areas: this.getAffectedAreas(parsedDiff.files || []),
      change_categories: this.categorizeChanges(parsedDiff.files || []),
      
      // Repository context (if requested)
      repository_context: includeContext ? await this.getRepositoryContext(repositoryId) : null,
      
      // Processing metadata
      diff_size_bytes: diffContent ? Buffer.byteLength(diffContent, 'utf8') : 0,
      processing_time_ms: Date.now(),
      analysis_version: '1.0'
    };

    return metadata;
  }

  // Store diff for analysis
  async storeDiffForAnalysis(options) {
    const {
      repositoryId,
      commitId,
      diffContent,
      diffMetadata,
      isFirstTime
    } = options;

    try {
      // Create or get commit record
      let commitId_db;
      const commitQuery = `
        SELECT id FROM repository_commit_details 
        WHERE repository_id = $1 AND commit_sha = $2
      `;
      const commitResult = await database.query(commitQuery, [repositoryId, commitId]);
      
      if (commitResult.rows.length === 0) {
        // Create new commit record
        const insertCommitQuery = `
          INSERT INTO repository_commit_details (
            repository_id, commit_sha, message, 
            author_name, author_email, committed_at, created_at
          ) VALUES ($1, $2, $3, $4, $5, $6, NOW())
          RETURNING id
        `;
        const commitInsertResult = await database.query(insertCommitQuery, [
          repositoryId, 
          commitId, 
          diffMetadata.message || 'Auto-generated commit',
          diffMetadata.author_name || 'System',
          diffMetadata.author_email || 'system@example.com',
          new Date().toISOString()
        ]);
        commitId_db = commitInsertResult.rows[0].id;
      } else {
        commitId_db = commitResult.rows[0].id;
      }

      // Store diff content
      const diffId = require('crypto').randomUUID();
      console.log('🔍 About to calculate diffSize in storeDiffForAnalysis:', {
        hasDiffContent: !!diffContent,
        diffContentType: typeof diffContent,
        diffContentLength: diffContent ? diffContent.length : 'undefined'
      });
      const diffSize = Buffer.byteLength(diffContent, 'utf8');
      
      // Store diff content in external file
      const diffStoragePath = await this.storeDiffToFile(diffId, diffContent);
      
      const insertDiffQuery = `
        INSERT INTO diff_contents (
          id, commit_id, diff_header, diff_size_bytes, storage_type,
          external_storage_path, file_path, change_type, processing_status
        ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
        ON CONFLICT (commit_id, file_path) 
        DO UPDATE SET
          diff_header = EXCLUDED.diff_header,
          diff_size_bytes = EXCLUDED.diff_size_bytes,
          storage_type = EXCLUDED.storage_type,
          external_storage_path = EXCLUDED.external_storage_path,
          change_type = EXCLUDED.change_type,
          processing_status = EXCLUDED.processing_status,
          updated_at = NOW()
        RETURNING id
      `;
      
      await database.query(insertDiffQuery, [
        diffId,
        commitId_db,
        `diff --git commit ${commitId}`,
        diffSize,
        'external',
        diffStoragePath,
        'multiple_files',
        isFirstTime ? 'initial' : 'incremental',
        'processed'
      ]);

      return diffId;
    } catch (error) {
      console.error('Error storing diff for analysis:', error);
      throw error;
    }
  }

  // Generate full analysis
  async generateFullAnalysis(diffMetadata, repositoryId) {
    // Get the actual diff content for AI analysis
    const actualDiffContent = await this.getStoredDiffContent(diffMetadata.commit_id);
    
    return {
      analysis_type: 'full',
      summary: {
        description: 'Complete repository analysis with full context',
        scope: 'All files and dependencies',
        confidence_level: 'high'
      },
      insights: {
        architecture_impact: this.analyzeArchitectureImpact(diffMetadata),
        performance_impact: this.analyzePerformanceImpact(diffMetadata),
        security_considerations: this.analyzeSecurityImpact(diffMetadata),
        maintainability_score: this.calculateMaintainabilityScore(diffMetadata)
      },
      recommendations: this.generateFullRecommendations(diffMetadata),
      next_steps: this.generateNextSteps(diffMetadata, 'full'),
      // Include actual diff content for AI analysis
      diff_content: {
        raw_content: actualDiffContent,
        content_length: actualDiffContent ? actualDiffContent.length : 0,
        content_preview: actualDiffContent ? actualDiffContent.substring(0, 500) + '...' : null,
        file_path: actualDiffContent ? `${diffMetadata.commit_id}.diff` : null
      }
    };
  }

  // Generate incremental analysis
  async generateIncrementalAnalysis(diffMetadata, repositoryId, commitId) {
    // Get the actual diff content for AI analysis
    const actualDiffContent = await this.getStoredDiffContent(commitId);
    
    return {
      analysis_type: 'incremental',
      summary: {
        description: 'Incremental analysis focusing on changes',
        scope: 'Modified files and their dependencies',
        confidence_level: 'medium'
      },
      insights: {
        change_impact: this.analyzeChangeImpact(diffMetadata),
        regression_risk: this.assessRegressionRisk(diffMetadata),
        integration_points: this.identifyIntegrationPoints(diffMetadata),
        testing_requirements: this.suggestTestingRequirements(diffMetadata)
      },
      recommendations: this.generateIncrementalRecommendations(diffMetadata),
      next_steps: this.generateNextSteps(diffMetadata, 'incremental'),
      // Include actual diff content for AI analysis
      diff_content: {
        raw_content: actualDiffContent,
        content_length: actualDiffContent ? actualDiffContent.length : 0,
        content_preview: actualDiffContent ? actualDiffContent.substring(0, 500) + '...' : null,
        file_path: actualDiffContent ? `${commitId}.diff` : null
      }
    };
  }

  // Stream analysis chunks
  async streamAnalysisChunks(analysis, diffMetadata) {
    const chunks = [];
    const chunkSize = 3; // Files per chunk
    
    // Get actual diff content for streaming
    const actualDiffContent = await this.getStoredDiffContent(diffMetadata.commit_id);
    
    const filesChanged = diffMetadata.files_changed || [];
    
    for (let i = 0; i < filesChanged.length; i += chunkSize) {
      const chunkFiles = filesChanged.slice(i, i + chunkSize);
      const chunkNumber = Math.floor(i / chunkSize) + 1;
      
      const chunk = {
        chunk_number: chunkNumber,
        total_chunks: Math.ceil(filesChanged.length / chunkSize),
        files: chunkFiles,
        analysis: {
          insights: analysis.insights,
          recommendations: analysis.recommendations.slice(0, chunkNumber * 2) // Limit recommendations per chunk
        },
        // Include diff content in each chunk
        diff_content: {
          raw_content: actualDiffContent,
          content_length: actualDiffContent ? actualDiffContent.length : 0,
          content_preview: actualDiffContent ? actualDiffContent.substring(0, 500) + '...' : null,
          file_path: actualDiffContent ? `${diffMetadata.commit_id}.diff` : null
        },
        progress: {
          current_chunk: chunkNumber,
          total_chunks: Math.ceil(filesChanged.length / chunkSize),
          processed_files: Math.min(i + chunkSize, filesChanged.length),
          total_files: filesChanged.length,
          percentage: Math.round((Math.min(i + chunkSize, filesChanged.length) / filesChanged.length) * 100)
        }
      };
      
      chunks.push(chunk);
    }
    
    return chunks;
  }

  // Helper methods for analysis
  calculateFileComplexity(file) {
    const baseComplexity = file.additions + file.deletions;
    const extension = path.extname(file.filePath);
    
    // Adjust complexity based on file type
    const complexityMultipliers = {
      '.js': 1.2,
      '.ts': 1.3,
      '.py': 1.1,
      '.java': 1.4,
      '.cpp': 1.5,
      '.c': 1.3
    };
    
    const multiplier = complexityMultipliers[extension] || 1.0;
    return Math.round(baseComplexity * multiplier);
  }

  calculateOverallComplexity(parsedDiff) {
    if (!parsedDiff) return 'low';
    
    const totalChanges = (parsedDiff.totalAdditions || 0) + (parsedDiff.totalDeletions || 0);
    const fileCount = parsedDiff.files ? parsedDiff.files.length : 0;
    
    if (totalChanges > 1000 || fileCount > 20) return 'high';
    if (totalChanges > 100 || fileCount > 5) return 'medium';
    return 'low';
  }

  assessRiskLevel(parsedDiff) {
    if (!parsedDiff) return 'low';
    
    const totalChanges = (parsedDiff.totalAdditions || 0) + (parsedDiff.totalDeletions || 0);
    const fileCount = parsedDiff.files ? parsedDiff.files.length : 0;
    
    // Check for high-risk file types
    const highRiskFiles = parsedDiff.files ? parsedDiff.files.filter(file => 
      file.filePath.includes('config') || 
      file.filePath.includes('database') ||
      file.filePath.includes('security')
    ) : [];
    
    if (highRiskFiles.length > 0 || totalChanges > 500) return 'high';
    if (totalChanges > 100 || fileCount > 10) return 'medium';
    return 'low';
  }

  getAffectedAreas(files) {
    const areas = new Set();
    
    if (!files || !Array.isArray(files)) {
      return [];
    }
    
    files.forEach(file => {
      const path = file.filePath.toLowerCase();
      if (path.includes('frontend') || path.includes('ui') || path.includes('component')) {
        areas.add('frontend');
      }
      if (path.includes('backend') || path.includes('api') || path.includes('service')) {
        areas.add('backend');
      }
      if (path.includes('database') || path.includes('sql') || path.includes('migration')) {
        areas.add('database');
      }
      if (path.includes('test') || path.includes('spec')) {
        areas.add('testing');
      }
      if (path.includes('config') || path.includes('env')) {
        areas.add('configuration');
      }
    });
    
    return Array.from(areas);
  }

  categorizeChanges(files) {
    if (!files || !Array.isArray(files)) {
      return {
        new_features: 0,
        bug_fixes: 0,
        refactoring: 0,
        documentation: 0,
        configuration: 0
      };
    }
    
    const categories = {
      new_features: files.filter(f => f.changeType === 'added').length,
      bug_fixes: files.filter(f => f.changeType === 'modified' && f.filePath.includes('fix')).length,
      refactoring: files.filter(f => f.changeType === 'modified' && f.additions === f.deletions).length,
      documentation: files.filter(f => f.filePath.endsWith('.md')).length,
      configuration: files.filter(f => f.filePath.includes('config')).length
    };
    
    return categories;
  }

  // Additional helper methods would be implemented here...
  analyzeArchitectureImpact(diffMetadata) {
    return {
      impact_level: diffMetadata.complexity_score,
      affected_components: diffMetadata.affected_areas,
      architectural_changes: (diffMetadata.files_changed || []).filter(f => f.filePath.includes('architecture')).length
    };
  }

  analyzePerformanceImpact(diffMetadata) {
    return {
      potential_impact: diffMetadata.complexity_score,
      performance_critical_files: (diffMetadata.files_changed || []).filter(f => 
        f.filePath.includes('performance') || f.filePath.includes('optimization')
      ).length
    };
  }

  analyzeSecurityImpact(diffMetadata) {
    return {
      security_relevant_changes: (diffMetadata.files_changed || []).filter(f => 
        f.filePath.includes('auth') || f.filePath.includes('security') || f.filePath.includes('crypto')
      ).length,
      risk_assessment: diffMetadata.risk_level
    };
  }

  calculateMaintainabilityScore(diffMetadata) {
    const complexity = diffMetadata.complexity_score === 'high' ? 3 : diffMetadata.complexity_score === 'medium' ? 2 : 1;
    const fileCount = (diffMetadata.files_changed || []).length;
    const risk = diffMetadata.risk_level === 'high' ? 3 : diffMetadata.risk_level === 'medium' ? 2 : 1;
    
    return Math.max(1, 10 - (complexity + fileCount + risk));
  }

  generateFullRecommendations(diffMetadata) {
    const recommendations = [];
    
    if (diffMetadata.complexity_score === 'high') {
      recommendations.push('Consider breaking down complex changes into smaller, manageable commits');
    }
    
    if (diffMetadata.risk_level === 'high') {
      recommendations.push('Implement comprehensive testing before deployment');
    }
    
    if (diffMetadata.affected_areas.includes('database')) {
      recommendations.push('Review database migration scripts and backup procedures');
    }
    
    return recommendations;
  }

  generateIncrementalRecommendations(diffMetadata) {
    const recommendations = [];
    
    recommendations.push('Review changed files for potential side effects');
    recommendations.push('Run relevant test suites for modified components');
    
    if (diffMetadata.affected_areas.includes('frontend')) {
      recommendations.push('Test UI changes across different browsers and devices');
    }
    
    if (diffMetadata.affected_areas.includes('backend')) {
      recommendations.push('Verify API endpoints and data validation');
    }
    
    return recommendations;
  }

  generateNextSteps(diffMetadata, analysisType) {
    const steps = [];
    
    if (analysisType === 'full') {
      steps.push('Review overall architecture and design patterns');
      steps.push('Plan comprehensive testing strategy');
      steps.push('Document architectural decisions');
    } else {
      steps.push('Test specific changes in isolation');
      steps.push('Verify integration with existing components');
      steps.push('Update relevant documentation');
    }
    
    return steps;
  }

  analyzeChangeImpact(diffMetadata) {
    return {
      scope: diffMetadata.affected_areas,
      magnitude: diffMetadata.complexity_score,
      files_affected: (diffMetadata.files_changed || []).length
    };
  }

  assessRegressionRisk(diffMetadata) {
    return {
      risk_level: diffMetadata.risk_level,
      critical_files: (diffMetadata.files_changed || []).filter(f => f.filePath.includes('core')).length,
      testing_priority: diffMetadata.complexity_score
    };
  }

  identifyIntegrationPoints(diffMetadata) {
    return (diffMetadata.files_changed || []).filter(f => 
      f.filePath.includes('api') || f.filePath.includes('interface') || f.filePath.includes('service')
    ).map(f => f.filePath);
  }

  suggestTestingRequirements(diffMetadata) {
    const requirements = ['Unit tests for modified functions'];
    
    if (diffMetadata.affected_areas.includes('frontend')) {
      requirements.push('UI component testing');
    }
    
    if (diffMetadata.affected_areas.includes('backend')) {
      requirements.push('API endpoint testing');
    }
    
    if (diffMetadata.affected_areas.includes('database')) {
      requirements.push('Database integration testing');
    }
    
    return requirements;
  }

  async getRepositoryContext(repositoryId) {
    try {
      return await this.getRepositoryInfo(repositoryId);
    } catch (error) {
      console.error('Error getting repository context:', error);
      return null;
    }
  }

  // Get commit details from database
  async getCommitDetailsFromDatabase(commitId) {
    try {
      const query = `
        SELECT 
          commit_sha,
          message,
          author_name,
          author_email,
          committed_at,
          created_at
        FROM repository_commit_details
        WHERE commit_sha = $1
      `;
      
      const result = await database.query(query, [commitId]);
      
      if (result.rows.length > 0) {
        return result.rows[0];
      }
      
      return null;
    } catch (error) {
      console.error('Error getting commit details from database:', error);
      return null;
    }
  }

  // Get file changes from database
  async getFileChangesFromDatabase(commitId) {
    try {
      const query = `
        SELECT 
          rcf.id,
          rcf.commit_id,
          rcf.file_path,
          rcf.change_type,
          rcf.created_at
        FROM repository_commit_files rcf
        JOIN repository_commit_details rcd ON rcf.commit_id = rcd.id
        WHERE rcd.commit_sha = $1
        ORDER BY rcf.file_path
      `;
      
      const result = await database.query(query, [commitId]);
      return result.rows;
    } catch (error) {
      console.error('Error getting file changes from database:', error);
      return [];
    }
  }

  // Get simple file changes
  async getSimpleFileChanges(commitId) {
    try {
      // First try repository_commit_files table
      const commitQuery = `
        SELECT id FROM repository_commit_details 
        WHERE commit_sha = $1
      `;
      
      const commitResult = await database.query(commitQuery, [commitId]);
      
      if (commitResult.rows.length > 0) {
        const internalCommitId = commitResult.rows[0].id;
        
        const fileQuery = `
          SELECT 
            rcf.file_path,
            rcf.change_type
          FROM repository_commit_files rcf
          WHERE rcf.commit_id = $1
        `;
        
        const result = await database.query(fileQuery, [internalCommitId]);
        
        if (result.rows.length > 0) {
          return result.rows;
        }
      }
      
      // If no data in repository_commit_files, try diff_contents table
      const diffQuery = `
        SELECT 
          dc.file_path,
          dc.change_type
        FROM diff_contents dc
        WHERE dc.commit_id = $1
      `;
      
      const diffResult = await database.query(diffQuery, [commitId]);
      return diffResult.rows;
    } catch (error) {
      console.error('Error getting file changes:', error);
      return [];
    }
  }

  // Get actual diff content from database first, then local files
  async getActualDiffContent(commitId) {
    try {
      // First check database - query directly by commit_id
      const query = `
        SELECT 
          dc.external_storage_path,
          dc.file_path,
          dc.change_type
        FROM diff_contents dc
        WHERE dc.commit_id = $1
        LIMIT 1
      `;
      
      const result = await database.query(query, [commitId]);
      
      if (result.rows.length === 0) {
        return null; // No data in database
      }
      
      const diffInfo = result.rows[0];
      const fs = require('fs');
      const path = require('path');
      
      // Read the actual diff file
      if (fs.existsSync(diffInfo.external_storage_path)) {
        const diffContent = fs.readFileSync(diffInfo.external_storage_path, 'utf8');
        return {
          file_path: diffInfo.file_path,
          change_type: diffInfo.change_type,
          local_path: diffInfo.external_storage_path,
          content: diffContent,
          size: diffContent.length
        };
      }
      
      return null;
    } catch (error) {
      console.error('Error getting diff content:', error);
      return null;
    }
  }

  // ==================== BULK COMMIT ANALYSIS METHODS ====================

  // Get multiple commits with their diff contents
  async getBulkCommitDetails(commitIds) {
    try {
      console.log(`🔍 Getting bulk commit details for ${commitIds.length} commits`);
      
      const results = [];
      
      for (const commitId of commitIds) {
        try {
          // Get commit details
          const commitQuery = `
            SELECT 
              id,
              commit_sha,
              message,
              committed_at
            FROM repository_commit_details 
            WHERE id = $1
          `;
          
          const commitResult = await database.query(commitQuery, [commitId]);
          
          if (commitResult.rows.length === 0) {
            results.push({
              commitId: commitId,
              status: 'not_found',
              message: 'Commit not found in database'
            });
            continue;
          }
          
          const commitDetails = commitResult.rows[0];
          
          // Get file changes for this commit
          const fileChanges = await this.getSimpleFileChanges(commitId);
          
          // Get diff content for this commit
          const diffContent = await this.getActualDiffContent(commitId);
          
          results.push({
            commitId: commitId,
            status: 'success',
            commitDetails: commitDetails,
            fileChanges: fileChanges,
            diffContent: diffContent,
            filesCount: fileChanges.length,
            hasDiffContent: !!diffContent
          });
          
        } catch (error) {
          console.error(`Error processing commit ${commitId}:`, error);
          results.push({
            commitId: commitId,
            status: 'error',
            message: error.message
          });
        }
      }
      
      return results;
    } catch (error) {
      console.error('Error in getBulkCommitDetails:', error);
      throw error;
    }
  }

  // Batch file reading for multiple commits
  async batchReadDiffFiles(commitResults) {
    try {
      console.log(`📁 Batch reading diff files for ${commitResults.length} commits`);
      
      const fileReadPromises = [];
      
      for (const commitResult of commitResults) {
        if (commitResult.status === 'success' && commitResult.diffContent) {
          const filePath = commitResult.diffContent.local_path;
          
          if (filePath) {
            fileReadPromises.push(
              this.readSingleDiffFile(filePath, commitResult.commitId)
            );
          }
        }
      }
      
      // Read all files in parallel
      const fileContents = await Promise.all(fileReadPromises);
      
      // Merge results back with commit data
      const enrichedResults = commitResults.map(commitResult => {
        if (commitResult.status === 'success') {
          const fileContent = fileContents.find(fc => fc.commitId === commitResult.commitId);
          if (fileContent) {
            commitResult.diffContent.content = fileContent.content;
            commitResult.diffContent.size = fileContent.size;
            commitResult.diffContent.readStatus = fileContent.status;
          }
        }
        return commitResult;
      });
      
      return enrichedResults;
    } catch (error) {
      console.error('Error in batchReadDiffFiles:', error);
      throw error;
    }
  }

  // Read a single diff file
  async readSingleDiffFile(filePath, commitId) {
    try {
      const fs = require('fs');
      
      if (fs.existsSync(filePath)) {
        const content = fs.readFileSync(filePath, 'utf8');
        return {
          commitId: commitId,
          filePath: filePath,
          content: content,
          size: content.length,
          status: 'success'
        };
      } else {
        return {
          commitId: commitId,
          filePath: filePath,
          content: null,
          size: 0,
          status: 'file_not_found'
        };
      }
    } catch (error) {
      console.error(`Error reading file ${filePath}:`, error);
      return {
        commitId: commitId,
        filePath: filePath,
        content: null,
        size: 0,
        status: 'error',
        error: error.message
      };
    }
  }

  // Get bulk analysis summary
  async getBulkAnalysisSummary(commitResults) {
    try {
      const summary = {
        total_commits: commitResults.length,
        successful_commits: commitResults.filter(r => r.status === 'success').length,
        failed_commits: commitResults.filter(r => r.status === 'error').length,
        not_found_commits: commitResults.filter(r => r.status === 'not_found').length,
        total_files: commitResults.reduce((sum, r) => sum + (r.filesCount || 0), 0),
        commits_with_diff: commitResults.filter(r => r.hasDiffContent).length,
        total_content_size: commitResults.reduce((sum, r) => {
          return sum + (r.diffContent?.size || 0);
        }, 0)
      };
      
      return summary;
    } catch (error) {
      console.error('Error in getBulkAnalysisSummary:', error);
      throw error;
    }
  }

  // Process bulk commits for AI analysis
  async processBulkCommitsForAI(commitResults) {
    try {
      console.log(`🤖 Processing ${commitResults.length} commits for AI analysis`);
      
      const aiInputs = [];
      
      for (const commitResult of commitResults) {
        if (commitResult.status === 'success' && commitResult.diffContent?.content) {
          aiInputs.push({
            commitId: commitResult.commitId,
            commitDetails: commitResult.commitDetails,
            fileChanges: commitResult.fileChanges,
            diffContent: commitResult.diffContent,
            analysisReady: true
          });
        }
      }
      
      return aiInputs;
    } catch (error) {
      console.error('Error in processBulkCommitsForAI:', error);
      throw error;
    }
  }

  // Helper method to detect if file is binary
  isBinaryFile(filePath) {
    const binaryExtensions = ['.exe', '.dll', '.so', '.dylib', '.bin', '.img', '.iso', '.zip', '.tar', '.gz', '.rar', '.7z', '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.svg', '.ico', '.woff', '.woff2', '.ttf', '.eot'];
    const extension = path.extname(filePath).toLowerCase();
    return binaryExtensions.includes(extension);
  }

  // Helper method to detect language from file extension
  detectLanguage(fileExtension) {
    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',
      '.html': 'html',
      '.css': 'css',
      '.scss': 'scss',
      '.json': 'json',
      '.yaml': 'yaml',
      '.yml': 'yaml',
      '.xml': 'xml',
      '.sql': 'sql',
      '.sh': 'bash',
      '.md': 'markdown'
    };

    return languageMap[fileExtension] || 'unknown';
  }
}

module.exports = AIStreamingService;
