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;

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
  TargetDetectionResult detectTarget(String imagePath, TargetType targetType) {
    try {
      // Detect main target
      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;
      }

      // Detect impacts
      final impacts = _imageProcessingService.detectImpacts(imagePath);

      // Convert impacts to relative coordinates and calculate scores
      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,
  ) {
    // Calculate distance from center (normalized to target radius)
    final dx = impactX - centerX;
    final dy = impactY - centerY;
    final distance = math.sqrt(dx * dx + dy * dy) / targetRadius;

    // Score zones (10 zones)
    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; // Outside target
  }

  int _calculateSilhouetteScore(
    double impactX,
    double impactY,
    double centerX,
    double centerY,
  ) {
    // For silhouettes, scoring is typically based on zones
    // Head and center mass = 5, body = 4, lower = 3

    final dx = (impactX - centerX).abs();
    final dy = impactY - centerY;

    // Check if within silhouette bounds (approximate)
    if (dx > 0.15) return 0; // Too far left/right

    // Vertical zones
    if (dy < -0.25) return 5; // Head zone (top)
    if (dy < 0.0) return 5; // Center mass (upper body)
    if (dy < 0.15) return 4; // Body
    if (dy < 0.35) return 3; // Lower body

    return 0; // Outside target
  }

  /// Detect only impacts with custom settings (doesn't affect target position)
  List<DetectedImpactResult> detectImpactsOnly(
    String imagePath,
    TargetType targetType,
    double centerX,
    double centerY,
    double radius,
    int ringCount,
    ImpactDetectionSettings settings,
  ) {
    try {
      // Detect impacts with custom settings
      final impacts = _imageProcessingService.detectImpactsWithSettings(
        imagePath,
        settings,
      );

      // Convert impacts to relative coordinates and calculate scores
      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,
  ) {
    // Calculate distance from center (normalized to target radius)
    final dx = impactX - centerX;
    final dy = impactY - centerY;
    final distance = math.sqrt(dx * dx + dy * dy) / targetRadius;

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

    return 0; // Outside target
  }

  /// Analyze reference impacts to learn their characteristics
  ImpactCharacteristics? analyzeReferenceImpacts(
    String imagePath,
    List<ReferenceImpact> references,
  ) {
    return _imageProcessingService.analyzeReferenceImpacts(
      imagePath,
      references,
    );
  }

  /// Detect impacts based on reference characteristics (calibrated detection)
  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 [];
    }
  }

  /// Détecte les impacts en utilisant OpenCV (Hough Circles + Contours)
  ///
  /// Cette méthode utilise les algorithmes OpenCV pour une détection plus robuste:
  /// - Transformation de Hough pour détecter les cercles
  /// - Analyse de contours avec filtrage par circularité
  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 [];
    }
  }

  /// Détecte les impacts avec OpenCV en utilisant des références
  ///
  /// Analyse les impacts de référence pour apprendre leurs caractéristiques
  /// puis détecte les impacts similaires dans l'image.
  List<DetectedImpactResult> detectImpactsWithOpenCVFromReferences(
    String imagePath,
    TargetType targetType,
    double centerX,
    double centerY,
    double radius,
    int ringCount,
    List<ReferenceImpact> references, {
    double tolerance = 2.0,
  }) {
    try {
      // Convertir les références au format OpenCV
      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 [];
    }
  }

}
