import path from 'path';
import fs from 'fs/promises';
import archiver from 'archiver';
import { createWriteStream } from 'fs';
import { imageProcessor } from './imageProcessor.js';
import { ProcessingJob } from '../models/ProcessingJob.js';
import { ProcessingBatch } from '../models/ProcessingBatch.js';
import { User } from '../models/User.js';
// import { fileCleanupService } from './fileCleanup.js';

export class BatchProcessor {
  constructor() {
    this.uploadsDir = './uploads';
    this.tempDir = './temp';
    this.outputDir = './output';
    this.ensureDirectories();
  }

  async ensureDirectories() {
    try {
      await fs.mkdir(this.uploadsDir, { recursive: true });
      await fs.mkdir(this.tempDir, { recursive: true });
      await fs.mkdir(this.outputDir, { recursive: true });
    } catch (error) {
      console.error('Error creating directories:', error);
    }
  }

  async processBatch(batchId, files, options, userId) {
    try {
      const batch = await ProcessingBatch.findById(batchId);
      if (!batch) throw new Error('Batch not found');

      await ProcessingBatch.updateStatus(batchId, 'processing');

      const batchDir = path.join(this.outputDir, batchId);
      await fs.mkdir(batchDir, { recursive: true });

      const processedFiles = [];
      let completedFiles = 0;
      let failedFiles = 0;

      for (const file of files) {
        try {
          const jobId = `${batchId}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
          const originalFormat = path.extname(file.originalname).substring(1).toLowerCase();
          
          // Generate SEO filename using the actual options
          const seoFilename = imageProcessor.generateSeoFilename(
            file.originalname,
            options.namingTemplate,
            options.seoKeywords
          );
          
          const outputExtension = imageProcessor.getOutputExtension(options.format);
          const outputFilename = `${seoFilename}.${outputExtension}`;
          const outputPath = path.join(batchDir, outputFilename);

          // Create processing job
          await ProcessingJob.create({
            id: jobId,
            userId,
            batchId,
            originalFilename: file.originalname,
            originalSize: file.size,
            originalFormat,
            outputFilename,
            outputFormat: options.format,
            qualitySetting: options.quality
          });

          // Process the image with actual options
          const result = await imageProcessor.processImage(
            jobId,
            file.path,
            outputPath,
            options
          );

          processedFiles.push({
            jobId,
            originalName: file.originalname,
            outputName: outputFilename,
            outputPath,
            originalSize: file.size,
            optimizedSize: result.optimizedSize
          });

          completedFiles++;
          await ProcessingBatch.updateProgress(batchId, completedFiles, failedFiles);

        } catch (error) {
          console.error(`Error processing file ${file.originalname}:`, error);
          failedFiles++;
          
          // Update job status to failed
          const jobId = `${batchId}-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;
          await ProcessingJob.updateStatus(jobId, 'failed', 0, error.message);
          await ProcessingBatch.updateProgress(batchId, completedFiles, failedFiles);
        }
      }

      // Create ZIP archive of processed images
      const zipPath = path.join(this.outputDir, `${batchId}.zip`);
      await this.createZipArchive(processedFiles, zipPath);

      // Update batch with completion status
      const downloadUrl = `/downloads/${batchId}.zip`;
      await ProcessingBatch.updateStatus(batchId, 'completed', downloadUrl);

      // Calculate total optimized size
      const totalOptimizedSize = processedFiles.reduce((sum, file) => sum + file.optimizedSize, 0);
      await ProcessingBatch.updateOptimizedSize(batchId, totalOptimizedSize);

      // Update user usage stats
      await User.updateUsage(userId, completedFiles);

      // Clean up uploaded files after processing
      setTimeout(async () => {
        try {
          for (const file of files) {
            await fs.unlink(file.path).catch(() => {}); // Ignore errors if file already deleted
          }
        } catch (error) {
          console.error('Error cleaning up uploaded files:', error);
        }
      }, 5000); // Wait 5 seconds to ensure download is complete

      return {
        batchId,
        completedFiles,
        failedFiles,
        downloadUrl,
        totalSavings: processedFiles.reduce((sum, file) => 
          sum + (file.originalSize - file.optimizedSize), 0
        )
      };

    } catch (error) {
      await ProcessingBatch.updateStatus(batchId, 'failed');
      
      // Clean up uploaded files even on failure
      setTimeout(async () => {
        try {
          for (const file of files) {
            await fs.unlink(file.path).catch(() => {}); // Ignore errors if file already deleted
          }
        } catch (error) {
          console.error('Error cleaning up uploaded files after failure:', error);
        }
      }, 1000);
      
      throw error;
    }
  }

  async createZipArchive(files, outputPath) {
    return new Promise(async (resolve, reject) => {
      const output = createWriteStream(outputPath);
      const archive = archiver('zip', { zlib: { level: 9 } });

      output.on('close', () => {
        console.log(`ZIP archive created: ${archive.pointer()} total bytes`);
        resolve(outputPath);
      });
      
      output.on('error', reject);
      archive.on('error', reject);

      archive.pipe(output);

      // Add each processed file to the archive
      for (const file of files) {
        try {
          await fs.access(file.outputPath);
          archive.file(file.outputPath, { name: file.outputName });
        } catch (err) {
          console.warn(`File not found for ZIP: ${file.outputPath}`);
        }
      }

      archive.finalize();
    });
  }

  async cleanupTempFiles(batchId) {
    try {
      const batchDir = path.join(this.outputDir, batchId);
      await fs.rm(batchDir, { recursive: true, force: true });
    } catch (error) {
      console.error('Cleanup error:', error);
    }
  }

  async getBatchStatus(batchId) {
    const batch = await ProcessingBatch.findById(batchId);
    if (!batch) return null;

    const jobs = await ProcessingJob.findByBatchId(batchId);
    
    return {
      ...batch,
      jobs,
      progress: batch.total_files > 0 ? 
        Math.round((batch.completed_files / batch.total_files) * 100) : 0
    };
  }
}

export const batchProcessor = new BatchProcessor();