enum ObstacleDirection { left, center, right }

class BoundingBox {
  final double left;
  final double top;
  final double right;
  final double bottom;

  const BoundingBox({
    required this.left,
    required this.top,
    required this.right,
    required this.bottom,
  });

  double get width => right - left;
  double get height => bottom - top;
  double get centerX => left + width / 2;
}

class DetectionResult {
  final String label;
  final double confidence;
  final ObstacleDirection direction;
  final String estimatedDistance;

  const DetectionResult({
    required this.label,
    required this.confidence,
    required this.direction,
    required this.estimatedDistance,
  });

  String get directionName => direction.name.toUpperCase();

  String get spokenId {
    final area = switch (direction) {
      ObstacleDirection.left => 'kiri',
      ObstacleDirection.center => 'tengah',
      ObstacleDirection.right => 'kanan',
    };
    return 'Hati-hati, $label di $area. Jarak $estimatedDistance.';
  }
}

class ObstacleAnalyzer {
  static const double frameWidth = 640.0;
  static const double frameHeight = 480.0;

  ObstacleDirection analyzeDirection(BoundingBox box) {
    final cx = box.centerX;
    if (cx < frameWidth * 0.33) return ObstacleDirection.left;
    if (cx > frameWidth * 0.67) return ObstacleDirection.right;
    return ObstacleDirection.center;
  }

  String estimateDistance(BoundingBox box) {
    final ratio = box.height / frameHeight;
    if (ratio > 0.60) return 'Very Close (< 1m)';
    if (ratio > 0.35) return 'Close (1-2m)';
    if (ratio > 0.15) return 'Medium (2-4m)';
    return 'Far (> 4m)';
  }

  String buildTtsMessage(DetectionResult result) {
    final directionLabel = switch (result.direction) {
      ObstacleDirection.left => 'kiri',
      ObstacleDirection.center => 'depan',
      ObstacleDirection.right => 'kanan',
    };
    return 'Hati-hati, ${result.label} di $directionLabel. '
        'Jarak ${result.estimatedDistance}.';
  }

  DetectionResult? prioritize(List<DetectionResult> detections) {
    if (detections.isEmpty) return null;
    const order = [
      'Very Close (< 1m)',
      'Very Close',
      'Close (1-2m)',
      'Close',
      'Medium (2-4m)',
      'Medium',
      'Far (> 4m)',
      'Far',
    ];
    final sorted = List<DetectionResult>.of(detections);
    sorted.sort((a, b) {
      final ai = order.indexOf(a.estimatedDistance);
      final bi = order.indexOf(b.estimatedDistance);
      final aRank = ai == -1 ? order.length : ai;
      final bRank = bi == -1 ? order.length : bi;
      return aRank.compareTo(bRank);
    });
    return sorted.first;
  }

  List<DetectionResult> filterByConfidence(
    List<DetectionResult> detections,
    double threshold,
  ) {
    return detections.where((d) => d.confidence >= threshold).toList();
  }

  DetectionResult analyzeFallback({
    String label = 'person',
    double confidence = 0.86,
  }) {
    return DetectionResult(
      label: label,
      confidence: confidence,
      direction: ObstacleDirection.center,
      estimatedDistance: 'Close (1-2m)',
    );
  }
}
