import 'dart:isolate'; // AJOUT
import 'dart:io';
import 'dart:math' as math;
import 'package:image/image.dart' as img;
import 'package:path_provider/path_provider.dart';
import 'package:uuid/uuid.dart';

class CropRect {
  final double x;
  final double y;
  final double width;
  final double height;

  const CropRect({
    required this.x,
    required this.y,
    required this.width,
    required this.height,
  });

  @override
  String toString() => 'CropRect(x: $x, y: $y, w: $width, h: $height)';
}

// AJOUT : Paramètres passés à l'Isolate (tout doit être sérialisable)
class _CropParams {
  final String sourcePath;
  final double cropX;
  final double cropY;
  final double cropWidth;
  final double cropHeight;
  final double rotationDegrees;
  final int outputSize;
  final String outputPath;

  _CropParams({
    required this.sourcePath,
    required this.cropX,
    required this.cropY,
    required this.cropWidth,
    required this.cropHeight,
    required this.rotationDegrees,
    required this.outputSize,
    required this.outputPath,
  });
}

// AJOUT : Fonction statique exécutée dans l'Isolate (doit être top-level ou static)
void _cropIsolateEntry(_CropParams params) {
  final file = File(params.sourcePath);
  final bytes = file.readAsBytesSync();
  img.Image? originalImage = img.decodeImage(bytes);

  if (originalImage == null) {
    throw Exception('Impossible de décoder l\'image: ${params.sourcePath}');
  }

  // Rotation si nécessaire — LINEAR au lieu de CUBIC pour la vitesse
  if (params.rotationDegrees != 0.0) {
    originalImage = img.copyRotate(
      originalImage,
      angle: params.rotationDegrees,
      interpolation: img.Interpolation.linear, // OPTIMISATION : linear >> cubic en vitesse
    );
  }

  // Recadrage
  final srcX = (params.cropX * originalImage.width).round();
  final srcY = (params.cropY * originalImage.height).round();
  final srcWidth = (params.cropWidth * originalImage.width).round();
  final srcHeight = (params.cropHeight * originalImage.height).round();

  final clampedX = srcX.clamp(0, originalImage.width - 1);
  final clampedY = srcY.clamp(0, originalImage.height - 1);
  final clampedWidth = math.min(srcWidth, originalImage.width - clampedX);
  final clampedHeight = math.min(srcHeight, originalImage.height - clampedY);

  img.Image cropped = img.copyCrop(
    originalImage,
    x: clampedX,
    y: clampedY,
    width: clampedWidth,
    height: clampedHeight,
  );

  // Redimensionnement — LINEAR au lieu de CUBIC
  if (cropped.width != params.outputSize || cropped.height != params.outputSize) {
    cropped = img.copyResize(
      cropped,
      width: params.outputSize,
      height: params.outputSize,
      interpolation: img.Interpolation.linear, // OPTIMISATION : linear >> cubic en vitesse
    );
  }

  // Encodage JPEG qualité 85 au lieu de 90 (gain de vitesse non négligeable)
  final outputFile = File(params.outputPath);
  outputFile.writeAsBytesSync(img.encodeJpg(cropped, quality: 85));
}

class ImageCropService {
  final Uuid _uuid = const Uuid();

  static const int maxOutputSize = 1024;

  Future<String> cropToSquare(
      String sourcePath,
      CropRect cropRect, {
        double rotationDegrees = 0.0,
        int outputSize = maxOutputSize,
      }) async {
    final tempDir = await getTemporaryDirectory();
    final outputPath = '${tempDir.path}/cropped_${_uuid.v4()}.jpg';

    final params = _CropParams(
      sourcePath: sourcePath,
      cropX: cropRect.x,
      cropY: cropRect.y,
      cropWidth: cropRect.width,
      cropHeight: cropRect.height,
      rotationDegrees: rotationDegrees,
      outputSize: outputSize,
      outputPath: outputPath,
    );

    // OPTIMISATION CLEF : Tout le traitement lourd dans un Isolate séparé
    // → le thread UI reste fluide pendant le calcul
    await Isolate.run(() => _cropIsolateEntry(params));

    return outputPath;
  }

  /// Calcule la zone de recadrage carrée maximale centrée sur l'image
  CropRect getDefaultSquareCrop(int imageWidth, int imageHeight) {
    final aspectRatio = imageWidth / imageHeight;

    if (aspectRatio > 1) {
      final squareWidth = imageHeight / imageWidth;
      final x = (1 - squareWidth) / 2;
      return CropRect(x: x, y: 0, width: squareWidth, height: 1);
    } else if (aspectRatio < 1) {
      final squareHeight = imageWidth / imageHeight;
      final y = (1 - squareHeight) / 2;
      return CropRect(x: 0, y: y, width: 1, height: squareHeight);
    } else {
      return const CropRect(x: 0, y: 0, width: 1, height: 1);
    }
  }

  /// Nettoie les fichiers temporaires de crop anciens
  Future<void> cleanupOldCrops({Duration maxAge = const Duration(hours: 24)}) async {
    try {
      final tempDir = await getTemporaryDirectory();
      final dir = Directory(tempDir.path);
      final now = DateTime.now();

      await for (final entity in dir.list()) {
        if (entity is File && entity.path.contains('cropped_')) {
          final stat = await entity.stat();
          final age = now.difference(stat.modified);
          if (age > maxAge) {
            await entity.delete();
          }
        }
      }
    } catch (e) {
      // Ignorer les erreurs
    }
  }

  /// Coupe automatiquement une photo brute de l'appareil photo en carré
  /// calé sur la position haute du viseur de l'écran.
  Future<String> cropRawCameraToSquare(String sourcePath) async {
    final file = File(sourcePath);
    final bytes = await file.readAsBytes();
    final originalImage = img.decodeImage(bytes);

    if (originalImage == null) return sourcePath;

    final int imgW = originalImage.width;
    final int imgH = originalImage.height;

    final int cropSizePixels = (math.min(imgW, imgH) * 0.85).round();

    final int x = ((imgW - cropSizePixels) / 2).round();
    final int y = ((imgH - cropSizePixels) / 2).round();

    final squareImage = img.copyCrop(
      originalImage,
      x: x.clamp(0, imgW - 1),
      y: y.clamp(0, imgH - 1),
      width: cropSizePixels,
      height: cropSizePixels,
    );

    final tempDir = await getTemporaryDirectory();
    final outputPath = '${tempDir.path}/camera_square_${DateTime.now().millisecondsSinceEpoch}.jpg';
    await File(outputPath).writeAsBytes(img.encodeJpg(squareImage, quality: 85));

    return outputPath;
  }
}