import { Injectable } from '@nestjs/common';
import * as sharp from 'sharp';
import * as path from 'path';
import * as fs from 'fs';

export interface ImageResizeOptions {
  width?: number;
  height?: number;
  quality?: number;
  format?: 'jpeg' | 'png' | 'webp';
}

@Injectable()
export class ImageProcessorService {
  /**
   * Safely delete a file with retry logic for Windows file locking issues
   * @param filePath - Path to the file to delete
   * @param maxRetries - Maximum number of retry attempts
   * @param delayMs - Delay between retries in milliseconds
   */
  private async safeDeleteFile(
    filePath: string,
    maxRetries: number = 3,
    delayMs: number = 100,
  ): Promise<void> {
    for (let attempt = 0; attempt < maxRetries; attempt++) {
      try {
        if (fs.existsSync(filePath)) {
          // Use async unlink with promises
          await fs.promises.unlink(filePath);
          return; // Success
        }
        return; // File doesn't exist, nothing to delete
      } catch (error) {
        if (error.code === 'EPERM' || error.code === 'EBUSY') {
          // File is locked or permission denied, wait and retry
          if (attempt < maxRetries - 1) {
            await new Promise((resolve) => setTimeout(resolve, delayMs));
            continue;
          }
          // Last attempt failed, log warning but don't throw
          console.warn(
            `Unable to delete file ${filePath} after ${maxRetries} attempts. File may be in use.`,
          );
        } else {
          // Different error, log but don't throw
          console.error(`Error deleting file ${filePath}:`, error);
        }
      }
    }
  }
  /**
   * Resize and optimize an uploaded image
   * @param filePath - Path to the uploaded file
   * @param options - Resize options
   * @returns Promise<string> - Path to the processed image
   */
  async processImage(
    filePath: string,
    options: ImageResizeOptions = {},
  ): Promise<string> {
    try {
      const {
        width = 800,
        height = 600,
        quality = 80,
        format = 'jpeg',
      } = options;

      // Generate output filename
      const dir = path.dirname(filePath);
      const ext = path.extname(filePath);
      const name = path.basename(filePath, ext);
      const outputPath = path.join(dir, `${name}_processed.${format}`);

      // Process image with Sharp
      let sharpInstance = sharp(filePath);

      // Resize image to exact dimensions with proper fitting
      if (width && height) {
        sharpInstance = sharpInstance.resize(width, height, {
          fit: 'cover', // This ensures the image fills the exact dimensions
          position: 'center', // Center the image when cropping
        });
      } else if (width) {
        sharpInstance = sharpInstance.resize(width, null, {
          withoutEnlargement: false, // Allow enlarging if needed
        });
      } else if (height) {
        sharpInstance = sharpInstance.resize(null, height, {
          withoutEnlargement: false, // Allow enlarging if needed
        });
      }

      // Convert to specified format with quality settings
      switch (format) {
        case 'jpeg':
          sharpInstance = sharpInstance.jpeg({
            quality,
            progressive: true, // Better quality JPEG
            mozjpeg: true, // Use mozjpeg for better compression
          });
          break;
        case 'png':
          sharpInstance = sharpInstance.png({
            quality,
            compressionLevel: 9, // Maximum PNG compression
            adaptiveFiltering: true, // Better PNG quality
          });
          break;
        case 'webp':
          sharpInstance = sharpInstance.webp({
            quality,
            effort: 6, // Maximum WebP compression effort
          });
          break;
      }

      // Process and save the image
      await sharpInstance.toFile(outputPath);

      // Wait a moment to ensure Sharp releases the file handle
      await new Promise((resolve) => setTimeout(resolve, 50));

      // Remove the original file using safe deletion with retry logic
      await this.safeDeleteFile(filePath);

      return outputPath;
    } catch (error) {
      console.error('Error processing image:', error);
      throw new Error(`Failed to process image: ${error.message}`);
    }
  }

  /**
   * Process company logo with specific dimensions
   * @param filePath - Path to the uploaded logo file
   * @returns Promise<string> - Path to the processed logo
   */
  async processCompanyLogo(filePath: string): Promise<string> {
    // Determine format from original file extension
    const ext = path.extname(filePath).toLowerCase();
    let format: 'jpeg' | 'png' | 'webp' = 'png';

    if (ext === '.jpg' || ext === '.jpeg') {
      format = 'jpeg';
    } else if (ext === '.webp') {
      format = 'webp';
    }
    // PNG and other formats default to PNG

    return this.processImage(filePath, {
      width: 68,
      height: 68,
      quality: 95, // Increased quality for logos
      format,
    });
  }

  /**
   * Process company banner with specific dimensions
   * @param filePath - Path to the uploaded banner file
   * @returns Promise<string> - Path to the processed banner
   */
  async processCompanyBanner(filePath: string): Promise<string> {
    // Determine format from original file extension
    const ext = path.extname(filePath).toLowerCase();
    let format: 'jpeg' | 'png' | 'webp' = 'jpeg';

    if (ext === '.png') {
      format = 'png';
    } else if (ext === '.webp') {
      format = 'webp';
    }
    // JPG, JPEG and other formats default to JPEG

    return this.processImage(filePath, {
      width: 1920,
      height: 312,
      quality: 92, // Increased quality for banners
      format,
    });
  }

  /**
   * Get image metadata
   * @param filePath - Path to the image file
   * @returns Promise<sharp.Metadata> - Image metadata
   */
  async getImageMetadata(filePath: string): Promise<sharp.Metadata> {
    try {
      return await sharp(filePath).metadata();
    } catch (error) {
      console.error('Error getting image metadata:', error);
      throw new Error(`Failed to get image metadata: ${error.message}`);
    }
  }

  /**
   * Validate image dimensions
   * @param filePath - Path to the image file
   * @param maxWidth - Maximum allowed width
   * @param maxHeight - Maximum allowed height
   * @returns Promise<boolean> - Whether image dimensions are valid
   */
  async validateImageDimensions(
    filePath: string,
    maxWidth: number,
    maxHeight: number,
  ): Promise<boolean> {
    try {
      const metadata = await this.getImageMetadata(filePath);
      return (
        (metadata.width || 0) <= maxWidth && (metadata.height || 0) <= maxHeight
      );
    } catch (error) {
      console.error('Error validating image dimensions:', error);
      return false;
    }
  }
}
