import 'dart:io';

import 'package:injectable/injectable.dart';

@singleton
class VideoDownloader {
  static const cacheDir = 'cache/video';
  static const int maxWidth = 480;
  static const int maxHeight = 320;

  /// Downloads and resizes a YouTube video
  /// Returns the path to the resized video file, or null on failure
  Future<String?> downloadAndResize(String videoId) async {
    final videoDir = Directory('$cacheDir/$videoId');
    final originalVideoFile = File('${videoDir.path}/video_original.mp4');
    final resizedVideoFile = File('${videoDir.path}/video.mp4');

    // Check if resized video already exists in cache
    if (await resizedVideoFile.exists()) {
      return resizedVideoFile.path;
    }

    // Create cache directory if it doesn't exist
    if (!await videoDir.exists()) {
      await videoDir.create(recursive: true);
    }

    final youtubeUrl = 'https://www.youtube.com/watch?v=$videoId';

    try {
      // Use yt-dlp to download the video
      final downloadResult = await Process.run(
        'yt-dlp',
        [
          '--js-runtimes', 'node',
          '-f', 'bestvideo[ext=mp4]+bestaudio[ext=m4a]/best[ext=mp4]/best',
          '-o', originalVideoFile.path,
          '--merge-output-format', 'mp4',
          youtubeUrl,
        ],
      );

      if (downloadResult.exitCode != 0) {
        print('Failed to download video $videoId: ${downloadResult.stderr}');
        await _cleanup(videoDir);
        return null;
      }

      // Resize video to fit within maxWidth x maxHeight using ffmpeg
      final resizeResult = await Process.run(
        'ffmpeg',
        [
          '-i', originalVideoFile.path,
          '-vf', 'scale=w=$maxWidth:h=$maxHeight:force_original_aspect_ratio=decrease',
          '-c:v', 'libx264',
          '-preset', 'medium',
          '-crf', '23',
          '-c:a', 'aac',
          '-b:a', '128k',
          '-movflags', '+faststart',
          '-y',
          resizedVideoFile.path,
        ],
      );

      if (resizeResult.exitCode != 0) {
        print('Failed to resize video $videoId: ${resizeResult.stderr}');
        await _cleanup(videoDir);
        return null;
      }

      // Delete original video to save space
      if (await originalVideoFile.exists()) {
        await originalVideoFile.delete();
      }

      return resizedVideoFile.path;
    } catch (e) {
      print('Error processing video $videoId: $e');
      await _cleanup(videoDir);
      return null;
    }
  }

  /// Check if video is already cached
  Future<bool> isCached(String videoId) async {
    final resizedVideoFile = File('$cacheDir/$videoId/video.mp4');
    return resizedVideoFile.exists();
  }

  /// Get all cached video IDs
  Future<List<String>> getCachedVideos() async {
    final dir = Directory(cacheDir);
    if (!await dir.exists()) {
      return [];
    }

    final videos = <String>[];
    await for (final entity in dir.list()) {
      if (entity is Directory) {
        final videoId = entity.path.split('/').last;
        videos.add(videoId);
      }
    }
    return videos;
  }

  Future<void> _cleanup(Directory videoDir) async {
    if (await videoDir.exists()) {
      await videoDir.delete(recursive: true);
    }
  }
}
