import 'dart:math' as math;
import '../data/models/shot.dart';
import '../data/models/target_type.dart';
import '../core/constants/app_constants.dart';

class ScoreResult {
  final int totalScore;
  final int maxPossibleScore;
  final double percentage;
  final Map<int, int> scoreDistribution; // score -> count
  final int shotCount;

  ScoreResult({
    required this.totalScore,
    required this.maxPossibleScore,
    required this.percentage,
    required this.scoreDistribution,
    required this.shotCount,
  });
}

class ScoreCalculatorService {
  /// Calculate score for a single shot on a concentric target
  /// ringCount determines the number of scoring zones (default 10)
  /// Center is always 10 (bullseye), each ring decrements by 1
  /// Example: 5 rings = 10, 9, 8, 7, 6
  /// imageAspectRatio is width/height to account for non-square images
  int calculateConcentricScore({
    required double shotX,
    required double shotY,
    required double targetCenterX,
    required double targetCenterY,
    required double targetRadius,
    int ringCount = 10,
    double imageAspectRatio = 1.0,
    List<double>? ringRadii, // Optional individual ring radii multipliers
  }) {
    final dx = shotX - targetCenterX;
    final dy = shotY - targetCenterY;

    // Account for aspect ratio to match visual representation
    // The visual uses min(width, height) for the radius
    double normalizedDistance;
    if (imageAspectRatio >= 1.0) {
      // Landscape or square: scale x by aspectRatio
      normalizedDistance = math.sqrt(dx * dx * imageAspectRatio * imageAspectRatio + dy * dy) / targetRadius;
    } else {
      // Portrait: scale y by 1/aspectRatio
      normalizedDistance = math.sqrt(dx * dx + dy * dy / (imageAspectRatio * imageAspectRatio)) / targetRadius;
    }

    // Use custom ring radii if provided, otherwise use equal spacing
    if (ringRadii != null && ringRadii.length == ringCount) {
      for (int i = 0; i < ringCount; i++) {
        if (normalizedDistance <= ringRadii[i]) {
          // Center = 10, decrement by 1 for each ring
          return 10 - i;
        }
      }
    } else {
      // Generate dynamic zone radii based on ring count
      // Each zone has equal width
      for (int i = 0; i < ringCount; i++) {
        final zoneRadius = (i + 1) / ringCount;
        if (normalizedDistance <= zoneRadius) {
          // Center = 10, decrement by 1 for each ring
          return 10 - i;
        }
      }
    }

    return 0; // Outside target
  }

  /// Calculate score for a single shot on a silhouette target
  int calculateSilhouetteScore({
    required double shotX,
    required double shotY,
    required double targetCenterX,
    required double targetCenterY,
    required double targetWidth,
    required double targetHeight,
  }) {
    // Check if shot is within silhouette bounds
    final relativeX = (shotX - targetCenterX).abs() / (targetWidth / 2);
    final relativeY = (shotY - (targetCenterY - targetHeight / 2)) / targetHeight;

    // Outside horizontal bounds
    if (relativeX > 1.0) return 0;

    // Check vertical zones (from top of silhouette)
    if (relativeY < 0) return 0; // Above silhouette

    if (relativeY <= AppConstants.silhouetteZones['head']!) {
      // Head zone - 5 points, but narrower
      if (relativeX <= 0.4) return 5;
    } else if (relativeY <= AppConstants.silhouetteZones['center']!) {
      // Center mass - 5 points
      if (relativeX <= 0.6) return 5;
    } else if (relativeY <= AppConstants.silhouetteZones['body']!) {
      // Body - 4 points
      if (relativeX <= 0.7) return 4;
    } else if (relativeY <= AppConstants.silhouetteZones['lower']!) {
      // Lower body - 3 points
      if (relativeX <= 0.5) return 3;
    }

    return 0; // Outside target
  }

  /// Calculate scores for all shots
  ScoreResult calculateScores({
    required List<Shot> shots,
    required TargetType targetType,
    required double targetCenterX,
    required double targetCenterY,
    double? targetRadius, // For concentric
    double? targetWidth, // For silhouette
    double? targetHeight, // For silhouette
    int ringCount = 10, // For concentric
    double imageAspectRatio = 1.0, // For concentric
    List<double>? ringRadii, // For concentric with individual ring radii
  }) {
    final scoreDistribution = <int, int>{};
    int totalScore = 0;

    for (final shot in shots) {
      int score;

      if (targetType == TargetType.concentric) {
        score = calculateConcentricScore(
          shotX: shot.x,
          shotY: shot.y,
          targetCenterX: targetCenterX,
          targetCenterY: targetCenterY,
          targetRadius: targetRadius ?? 0.4,
          ringCount: ringCount,
          imageAspectRatio: imageAspectRatio,
          ringRadii: ringRadii,
        );
      } else {
        score = calculateSilhouetteScore(
          shotX: shot.x,
          shotY: shot.y,
          targetCenterX: targetCenterX,
          targetCenterY: targetCenterY,
          targetWidth: targetWidth ?? 0.3,
          targetHeight: targetHeight ?? 0.7,
        );
      }

      totalScore += score;
      scoreDistribution[score] = (scoreDistribution[score] ?? 0) + 1;
    }

    final maxScore = targetType == TargetType.concentric ? 10 : 5;
    final maxPossibleScore = shots.length * maxScore;
    final percentage = maxPossibleScore > 0
        ? (totalScore / maxPossibleScore) * 100
        : 0.0;

    return ScoreResult(
      totalScore: totalScore,
      maxPossibleScore: maxPossibleScore,
      percentage: percentage,
      scoreDistribution: scoreDistribution,
      shotCount: shots.length,
    );
  }

  /// Recalculate a shot's score
  Shot recalculateShot({
    required Shot shot,
    required TargetType targetType,
    required double targetCenterX,
    required double targetCenterY,
    double? targetRadius,
    double? targetWidth,
    double? targetHeight,
    int ringCount = 10,
    double imageAspectRatio = 1.0,
    List<double>? ringRadii,
  }) {
    int newScore;

    if (targetType == TargetType.concentric) {
      newScore = calculateConcentricScore(
        shotX: shot.x,
        shotY: shot.y,
        targetCenterX: targetCenterX,
        targetCenterY: targetCenterY,
        targetRadius: targetRadius ?? 0.4,
        ringCount: ringCount,
        imageAspectRatio: imageAspectRatio,
        ringRadii: ringRadii,
      );
    } else {
      newScore = calculateSilhouetteScore(
        shotX: shot.x,
        shotY: shot.y,
        targetCenterX: targetCenterX,
        targetCenterY: targetCenterY,
        targetWidth: targetWidth ?? 0.3,
        targetHeight: targetHeight ?? 0.7,
      );
    }

    return shot.copyWith(score: newScore);
  }

  /// Get score color index (0-9 for zone colors)
  int getScoreColorIndex(int score, TargetType targetType) {
    if (targetType == TargetType.concentric) {
      return (10 - score).clamp(0, 9);
    } else {
      // Map silhouette scores (0-5) to color indices
      return ((5 - score) * 2).clamp(0, 9);
    }
  }
}
