import 'dart:math' as math;
import 'package:flutter/foundation.dart';
import '../data/models/target_type.dart';
import 'image_processing_service.dart';
import 'opencv_impact_detection_service.dart';
export 'image_processing_service.dart'
    show ImpactDetectionSettings, ReferenceImpact, ImpactCharacteristics;
export 'opencv_impact_detection_service.dart'
    show OpenCVDetectionSettings, OpenCVDetectedImpact;

// ============================================================================
// STRUCT ET FONCTION GLOBALE POUR LE PARALLÉLISME (THREAD SECONDAIRE)
// ============================================================================

/// Conteneur de données pour envoyer les paramètres à l'Isolate d'arrière-plan.
class DetectionPayload {
  final String imagePath;
  final TargetType targetType;

  // On recrée les instances à l'intérieur du thread isolé car les objets ne se partagent pas entre threads.
  DetectionPayload({
    required this.imagePath,
    required this.targetType,
  });
}

/// FONCTION EXÉCUTÉE EN PARALLÈLE : Tourne sur un autre cœur du processeur.
/// L'interface graphique reste à 120 FPS et totalement fluide.
TargetDetectionResult runParallelTargetDetection(DetectionPayload payload) {
  // 1. Initialisation locale des services dans le sous-thread
  final imageProcessingService = ImageProcessingService();

  try {
    // 2. Détection de la cible principale (Calcul lourd)
    final mainTarget = imageProcessingService.detectMainTarget(payload.imagePath);

    double centerX = 0.5;
    double centerY = 0.5;
    double radius = 0.4;

    if (mainTarget != null) {
      centerX = mainTarget.centerX;
      centerY = mainTarget.centerY;
      radius = mainTarget.radius;
    }

    // 3. Détection des impacts (Calcul lourd)
    final impacts = imageProcessingService.detectImpacts(payload.imagePath);

    // 4. Calcul mathématique des scores relatifs
    final detectedImpacts = impacts.map((impact) {
      final score = payload.targetType == TargetType.concentric
          ? _staticCalculateConcentricScore(
        impact.x,
        impact.y,
        centerX,
        centerY,
        radius,
      )
          : _staticCalculateSilhouetteScore(impact.x, impact.y, centerX, centerY);

      return DetectedImpactResult(
        x: impact.x,
        y: impact.y,
        radius: impact.radius,
        suggestedScore: score,
      );
    }).toList();

    return TargetDetectionResult(
      centerX: centerX,
      centerY: centerY,
      radius: radius,
      impacts: detectedImpacts,
    );
  } catch (e) {
    return TargetDetectionResult.error('Erreur de detection parallèle: $e');
  }
}

// Fonctions mathématiques pures nécessaires à l'Isolate (statiques)
int _staticCalculateConcentricScore(double impactX, double impactY, double centerX, double centerY, double targetRadius) {
  final dx = impactX - centerX;
  final dy = impactY - centerY;
  final distance = math.sqrt(dx * dx + dy * dy) / targetRadius;
  if (distance <= 0.1) return 10;
  if (distance <= 0.2) return 9;
  if (distance <= 0.3) return 8;
  if (distance <= 0.4) return 7;
  if (distance <= 0.5) return 6;
  if (distance <= 0.6) return 5;
  if (distance <= 0.7) return 4;
  if (distance <= 0.8) return 3;
  if (distance <= 0.9) return 2;
  if (distance <= 1.0) return 1;
  return 0;
}

int _staticCalculateSilhouetteScore(double impactX, double impactY, double centerX, double centerY) {
  final dx = (impactX - centerX).abs();
  final dy = impactY - centerY;
  if (dx > 0.15) return 0;
  if (dy < -0.25) return 5;
  if (dy < 0.0) return 5;
  if (dy < 0.15) return 4;
  if (dy < 0.35) return 3;
  return 0;
}

// ============================================================================
// FIN DU BLOC DE PARALLÉLISME
// ============================================================================


class TargetDetectionResult {
  final double centerX; // Relative (0-1)
  final double centerY; // Relative (0-1)
  final double radius; // Relative (0-1)
  final List<DetectedImpactResult> impacts;
  final bool success;
  final String? errorMessage;

  TargetDetectionResult({
    required this.centerX,
    required this.centerY,
    required this.radius,
    required this.impacts,
    this.success = true,
    this.errorMessage,
  });

  factory TargetDetectionResult.error(String message) {
    return TargetDetectionResult(
      centerX: 0.5,
      centerY: 0.5,
      radius: 0.4,
      impacts: [],
      success: false,
      errorMessage: message,
    );
  }
}

class DetectedImpactResult {
  final double x; // Relative (0-1)
  final double y; // Relative (0-1)
  final double radius; // Absolute pixels
  final int suggestedScore;

  DetectedImpactResult({
    required this.x,
    required this.y,
    required this.radius,
    required this.suggestedScore,
  });
}

class TargetDetectionService {
  final ImageProcessingService _imageProcessingService;
  final OpenCVImpactDetectionService _opencvService;

  TargetDetectionService({
    ImageProcessingService? imageProcessingService,
    OpenCVImpactDetectionService? opencvService,
  }) : _imageProcessingService =
      imageProcessingService ?? ImageProcessingService(),
        _opencvService = opencvService ?? OpenCVImpactDetectionService();

  /// Detect target and impacts from an image file ASYNCHRONOUSLY in a separate Thread.
  /// CORRECTION : Utilise désormais 'compute' pour basculer en arrière-plan immédiat.
  Future<TargetDetectionResult> detectTargetAsync(String imagePath, TargetType targetType) async {
    final payload = DetectionPayload(imagePath: imagePath, targetType: targetType);
    // Déclenche l'exécution isolée en tâche de fond
    return await compute(runParallelTargetDetection, payload);
  }

  /// Gardée pour rétrocompatibilité synchrone si nécessaire
  TargetDetectionResult detectTarget(String imagePath, TargetType targetType) {
    try {
      final mainTarget = _imageProcessingService.detectMainTarget(imagePath);

      double centerX = 0.5;
      double centerY = 0.5;
      double radius = 0.4;

      if (mainTarget != null) {
        centerX = mainTarget.centerX;
        centerY = mainTarget.centerY;
        radius = mainTarget.radius;
      }

      final impacts = _imageProcessingService.detectImpacts(imagePath);

      final detectedImpacts = impacts.map((impact) {
        final score = targetType == TargetType.concentric
            ? _calculateConcentricScore(
          impact.x,
          impact.y,
          centerX,
          centerY,
          radius,
        )
            : _calculateSilhouetteScore(impact.x, impact.y, centerX, centerY);

        return DetectedImpactResult(
          x: impact.x,
          y: impact.y,
          radius: impact.radius,
          suggestedScore: score,
        );
      }).toList();

      return TargetDetectionResult(
        centerX: centerX,
        centerY: centerY,
        radius: radius,
        impacts: detectedImpacts,
      );
    } catch (e) {
      return TargetDetectionResult.error('Erreur de detection: $e');
    }
  }

  int _calculateConcentricScore(
      double impactX,
      double impactY,
      double centerX,
      double centerY,
      double targetRadius,
      ) {
    final dx = impactX - centerX;
    final dy = impactY - centerY;
    final distance = math.sqrt(dx * dx + dy * dy) / targetRadius;

    if (distance <= 0.1) return 10;
    if (distance <= 0.2) return 9;
    if (distance <= 0.3) return 8;
    if (distance <= 0.4) return 7;
    if (distance <= 0.5) return 6;
    if (distance <= 0.6) return 5;
    if (distance <= 0.7) return 4;
    if (distance <= 0.8) return 3;
    if (distance <= 0.9) return 2;
    if (distance <= 1.0) return 1;
    return 0;
  }

  int _calculateSilhouetteScore(
      double impactX,
      double impactY,
      double centerX,
      double centerY,
      ) {
    final dx = (impactX - centerX).abs();
    final dy = impactY - centerY;

    if (dx > 0.15) return 0;
    if (dy < -0.25) return 5;
    if (dy < 0.0) return 5;
    if (dy < 0.15) return 4;
    if (dy < 0.35) return 3;

    return 0;
  }

  List<DetectedImpactResult> detectImpactsOnly(
      String imagePath,
      TargetType targetType,
      double centerX,
      double centerY,
      double radius,
      int ringCount,
      ImpactDetectionSettings settings,
      ) {
    try {
      final impacts = _imageProcessingService.detectImpactsWithSettings(
        imagePath,
        settings,
      );

      return impacts.map((impact) {
        final score = targetType == TargetType.concentric
            ? _calculateConcentricScoreWithRings(
          impact.x,
          impact.y,
          centerX,
          centerY,
          radius,
          ringCount,
        )
            : _calculateSilhouetteScore(impact.x, impact.y, centerX, centerY);

        return DetectedImpactResult(
          x: impact.x,
          y: impact.y,
          radius: impact.radius,
          suggestedScore: score,
        );
      }).toList();
    } catch (e) {
      return [];
    }
  }

  int _calculateConcentricScoreWithRings(
      double impactX,
      double impactY,
      double centerX,
      double centerY,
      double targetRadius,
      int ringCount,
      ) {
    final dx = impactX - centerX;
    final dy = impactY - centerY;
    final distance = math.sqrt(dx * dx + dy * dy) / targetRadius;

    for (int i = 0; i < ringCount; i++) {
      final zoneRadius = (i + 1) / ringCount;
      if (distance <= zoneRadius) {
        return 10 - i;
      }
    }

    return 0;
  }

  ImpactCharacteristics? analyzeReferenceImpacts(
      String imagePath,
      List<ReferenceImpact> references,
      ) {
    return _imageProcessingService.analyzeReferenceImpacts(
      imagePath,
      references,
    );
  }

  List<DetectedImpactResult> detectImpactsFromReferences(
      String imagePath,
      TargetType targetType,
      double centerX,
      double centerY,
      double radius,
      int ringCount,
      ImpactCharacteristics characteristics, {
        double tolerance = 2.0,
      }) {
    try {
      final impacts = _imageProcessingService.detectImpactsFromReferences(
        imagePath,
        characteristics,
        tolerance: tolerance,
      );

      return impacts.map((impact) {
        final score = targetType == TargetType.concentric
            ? _calculateConcentricScoreWithRings(
          impact.x,
          impact.y,
          centerX,
          centerY,
          radius,
          ringCount,
        )
            : _calculateSilhouetteScore(impact.x, impact.y, centerX, centerY);

        return DetectedImpactResult(
          x: impact.x,
          y: impact.y,
          radius: impact.radius,
          suggestedScore: score,
        );
      }).toList();
    } catch (e) {
      return [];
    }
  }

  List<DetectedImpactResult> detectImpactsWithOpenCV(
      String imagePath,
      TargetType targetType,
      double centerX,
      double centerY,
      double radius,
      int ringCount, {
        OpenCVDetectionSettings? settings,
      }) {
    try {
      final impacts = _opencvService.detectImpacts(
        imagePath,
        settings: settings ?? const OpenCVDetectionSettings(),
      );

      return impacts.map((impact) {
        final score = targetType == TargetType.concentric
            ? _calculateConcentricScoreWithRings(
          impact.x,
          impact.y,
          centerX,
          centerY,
          radius,
          ringCount,
        )
            : _calculateSilhouetteScore(impact.x, impact.y, centerX, centerY);

        return DetectedImpactResult(
          x: impact.x,
          y: impact.y,
          radius: impact.radius,
          suggestedScore: score,
        );
      }).toList();
    } catch (e) {
      debugPrint('Erreur détection OpenCV: $e');
      return [];
    }
  }

  List<DetectedImpactResult> detectImpactsWithOpenCVFromReferences(
      String imagePath,
      TargetType targetType,
      double centerX,
      double centerY,
      double radius,
      int ringCount,
      List<ReferenceImpact> references, {
        double tolerance = 2.0,
      }) {
    try {
      final refPoints = references.map((r) => (x: r.x, y: r.y)).toList();

      final impacts = _opencvService.detectFromReferences(
        imagePath,
        refPoints,
        tolerance: tolerance,
      );

      return impacts.map((impact) {
        final score = targetType == TargetType.concentric
            ? _calculateConcentricScoreWithRings(
          impact.x,
          impact.y,
          centerX,
          centerY,
          radius,
          ringCount,
        )
            : _calculateSilhouetteScore(impact.x, impact.y, centerX, centerY);

        return DetectedImpactResult(
          x: impact.x,
          y: impact.y,
          radius: impact.radius,
          suggestedScore: score,
        );
      }).toList();
    } catch (e) {
      debugPrint('Erreur détection OpenCV depuis références: $e');
      return [];
    }
  }
}