import { spawn } from 'child_process';
import fs from 'fs/promises';
import path from 'path';
import { logger } from '../../../utils/logger';

export interface ThumbnailOptions {
  videoPath: string;
  videoId: number;
  duration: number;
  orientation: string;
  outputDir?: string;
}

export class ThumbnailService {
  private static readonly THUMBNAIL_DIR = '/media/local/thumbnails';
  private static readonly FFMPEG_TIMEOUT = 30000; // 30 seconds

  /**
   * Generate thumbnail for a video
   * Extracts frame at 10% duration (minimum 1 second)
   */
  static async generateThumbnail(options: ThumbnailOptions): Promise<string> {
    const { videoPath, videoId, duration, orientation, outputDir } = options;

    const thumbnailDir = outputDir || this.THUMBNAIL_DIR;
    const thumbnailPath = path.join(thumbnailDir, `${videoId}.jpg`);

    // Calculate timestamp (10% of duration, minimum 1 second)
    const timestamp = Math.max(1, Math.floor(duration * 0.1));

    // Determine thumbnail size based on orientation
    const isPortrait = orientation === 'portrait';
    const scale = isPortrait ? '180:320' : '320:180';

    try {
      // Ensure thumbnail directory exists
      await fs.mkdir(thumbnailDir, { recursive: true });

      // Extract frame using FFmpeg
      await this.runFFmpegCommand([
        '-ss', timestamp.toString(),  // Seek to timestamp
        '-i', videoPath,               // Input video
        '-vframes', '1',               // Extract 1 frame
        '-vf', `scale=${scale}`,       // Scale to target size
        '-q:v', '2',                   // Quality (1-31, lower is better)
        '-y',                          // Overwrite existing
        thumbnailPath                  // Output path
      ]);

      logger.info(`Generated thumbnail for video ${videoId} at ${thumbnailPath}`);
      return thumbnailPath;
    } catch (error) {
      logger.error(`Failed to generate thumbnail for video ${videoId}:`, error);
      throw new Error(`Thumbnail generation failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
    }
  }

  /**
   * Delete thumbnail file
   */
  static async deleteThumbnail(thumbnailPath: string): Promise<void> {
    try {
      await fs.unlink(thumbnailPath);
      logger.info(`Deleted thumbnail: ${thumbnailPath}`);
    } catch (error) {
      // Ignore errors if file doesn't exist
      if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
        logger.error(`Failed to delete thumbnail ${thumbnailPath}:`, error);
      }
    }
  }

  /**
   * Check if thumbnail exists
   */
  static async thumbnailExists(videoId: number, outputDir?: string): Promise<boolean> {
    const thumbnailDir = outputDir || this.THUMBNAIL_DIR;
    const thumbnailPath = path.join(thumbnailDir, `${videoId}.jpg`);

    try {
      await fs.access(thumbnailPath);
      return true;
    } catch {
      return false;
    }
  }

  /**
   * Run FFmpeg command with timeout
   */
  private static runFFmpegCommand(args: string[]): Promise<void> {
    return new Promise((resolve, reject) => {
      const ffmpeg = spawn('ffmpeg', args);
      let stderr = '';

      const timeout = setTimeout(() => {
        ffmpeg.kill('SIGKILL');
        reject(new Error('FFmpeg timeout exceeded'));
      }, this.FFMPEG_TIMEOUT);

      ffmpeg.stderr.on('data', (data) => {
        stderr += data.toString();
      });

      ffmpeg.on('close', (code) => {
        clearTimeout(timeout);
        if (code === 0) {
          resolve();
        } else {
          reject(new Error(`FFmpeg exited with code ${code}: ${stderr}`));
        }
      });

      ffmpeg.on('error', (error) => {
        clearTimeout(timeout);
        reject(error);
      });
    });
  }
}
