import 'dart:convert';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart' as http;

import 'package:nearle/views/helpers/constants/apiconstants.dart';
import 'package:nearle/providers/deliverylog/deliverylog_provider.dart';
import 'dart:io';
import 'package:minio/minio.dart';
import 'package:minio/io.dart';
import 'dart:math';
import 'package:nearle/utils/kalman_filter.dart';
import 'package:nearle/utils/mqtt_service.dart';

class DeliveriesController extends GetxController {
  NearleKalmanFilter? _kf;
  DateTime? _lastUpdateTime;

  final UpdateDeliveryProvider _updateProvider = UpdateDeliveryProvider();

  bool _isSuccess(Map<String, dynamic>? resp) {
    final status = resp?['status'];
    if (status is bool) return status;
    if (status is String) {
      final v = status.toLowerCase();
      return v == 'true' || v == 'success' || v == 'ok' || v == 'accepted';
    }
    return false;
  }

  // UI flags similar to xpressrider for parity
  final RxBool arrivedShimmer = false.obs;
  final RxBool deliveredShimmer = false.obs;
  final RxBool isPipEnabled = false.obs;

  // Distance tracking for PiP mode
  final RxDouble deliverableDistance = 0.0.obs; // Distance in meters

  // Times captured in ISO-like format (yyyy-MM-dd HH:mm:ss)
  String _arrivedTime = '';
  String _pickedTime = '';
  String _activeTime = '';
  String _deliveredTime = '';
  String _cancelledTime = '';

  // Cached last known lat/lng from device
  final RxString currentLat = '0'.obs;
  final RxString currentLng = '0'.obs;

  // Bonus Points Tracking
  final RxInt lastBonusPoints = 0.obs;

  // Trigger to refresh deliveries list across screens
  final RxInt refreshTrigger = 0.obs;
  
  void triggerRefresh() {
    refreshTrigger.value++;
  }

  // ---------- UPLOAD IMAGE TO DO SPACES ----------
  Future<String?> uploadProofImage(
    File imageFile,
    String folderName,
    int userId,
    int deliveryId,
  ) async {
    try {
      // final rng = Random(); // Unused
      const String region = "sgp1";
      const String accessKey = "DO00NQER7N2FRYZAB2HR";
      const String secretKey = "nMDewX25IBEu1FM5dakK+v28/WbW3TzBAwq913+dxP0";
      const String bucketName = "nearle";
      // folderName will be "picked" or "delivered"

      // File name
      final now = DateTime.now();
      final dateStr =
          "${now.year}${now.month.toString().padLeft(2, '0')}${now.day.toString().padLeft(2, '0')}";
      final timeStr =
          "${now.hour.toString().padLeft(2, '0')}${now.minute.toString().padLeft(2, '0')}${now.second.toString().padLeft(2, '0')}";
      final String fileName = '$folderName-$deliveryId-$dateStr-$timeStr.jpg';

      // Object path inside the bucket
      // final String objectPath = "$folderName/$fileName";
      final String objectPath = "support/$fileName";

      // CDN URL you want
      final String cdnUrl = "https://images.nearle.app/$objectPath";

      // Initialize Minio
      final minio = Minio(
        endPoint: "$region.digitaloceanspaces.com",
        accessKey: accessKey,
        secretKey: secretKey,
        region: region,
        useSSL: true,
      );

      debugPrint("Uploading Proof: $objectPath");

      // Upload to DO Spaces
      await minio.fPutObject(
        bucketName,
        objectPath,
        imageFile.path,
        metadata: {"Content-Type": "image/jpeg", "x-amz-acl": "public-read"},
      );

      debugPrint("Proof Uploaded Successfully: $cdnUrl");
      return cdnUrl;
    } catch (e) {
      debugPrint("Proof Upload error: $e");
      Get.snackbar("Error", "Image upload failed. Please try again.");
      return null;
    }
  }

  // ---------------- Location helpers ----------------
  Future<Map<String, String>> _ensureLatLng(String lat, String lng) async {
    String outLat = lat;
    String outLng = lng;

    try {
      final needsFetch =
          (lat == '0' || lat.isEmpty || lng == '0' || lng.isEmpty);
      
      // Fast path: if we have valid coordinates, use them immediately
      if (!needsFetch) return {'lat': outLat, 'lng': outLng};

      // Reuse recently cached coordinates first if fresh (e.g. within 30s)
      // For now just check if they exist to save time
      if (currentLat.value.isNotEmpty &&
          currentLat.value != '0' &&
          currentLng.value.isNotEmpty &&
          currentLng.value != '0') {
        return {'lat': currentLat.value, 'lng': currentLng.value};
      }

      final serviceEnabled = await Geolocator.isLocationServiceEnabled();
      if (!serviceEnabled) return {'lat': outLat, 'lng': outLng};

      LocationPermission permission = await Geolocator.checkPermission();
      if (permission == LocationPermission.denied) {
        permission = await Geolocator.requestPermission();
      }
      if (permission == LocationPermission.denied ||
          permission == LocationPermission.deniedForever) {
        return {'lat': outLat, 'lng': outLng};
      }

      Position? pos;

      // 1. Try Last Known Position (Instant)
      try {
        pos = await Geolocator.getLastKnownPosition();
      } catch (_) {}

      // 2. If no last known, try current with a single balanced timeout
      // Reduced complicated retry logic to one solid attempt
      if (pos == null) {
        try {
           pos = await Geolocator.getCurrentPosition(
            desiredAccuracy: LocationAccuracy.high, // Better accuracy for deliveries
            timeLimit: const Duration(seconds: 4),
          );
        } catch (_) {
          // Fallback to low accuracy if high fails quickly
           try {
             pos = await Geolocator.getCurrentPosition(
              desiredAccuracy: LocationAccuracy.low,
              timeLimit: const Duration(seconds: 2),
            );
           } catch (_) {}
        }
      }

      if (pos != null) {
        final now = DateTime.now();
        double outLatDouble = pos.latitude;
        double outLngDouble = pos.longitude;

        if (_kf == null) {
          _kf = NearleKalmanFilter(lat: outLatDouble, lng: outLngDouble);
        } else {
          final double dt = _lastUpdateTime != null
              ? now.difference(_lastUpdateTime!).inMilliseconds / 1000.0
              : 30.0;
          _kf!.predict(dt);
          _kf!.update(outLatDouble, outLngDouble);
          outLatDouble = _kf!.x[0];
          outLngDouble = _kf!.x[1];
        }
        _lastUpdateTime = now;

        outLat = outLatDouble.toStringAsFixed(6);
        outLng = outLngDouble.toStringAsFixed(6);
        currentLat.value = outLat;
        currentLng.value = outLng;
      }

      return {'lat': outLat, 'lng': outLng};
    } catch (_) {
      return {'lat': outLat, 'lng': outLng};
    }
  }

  // Picked
  Future<bool> updatePickedStatus({
    required int deliveryId,
    required int orderHeaderId,
    required int pickupLocationId,
    String ridersLat = '0',
    String ridersLng = '0',
    String pickupLat = '0',
    String pickupLng = '0',
    String address = '',
    String city = '',
    String state = '',
    String suburb = '',
    String postcode = '',
    String deliveryType = '',
    String notes = '',
    double actualKms = 0.0,
    String? proofImage, // New parameter
  }) async {
    try {
      final now = DateTime.now();
      _pickedTime = _formatDateTimeFull(now);

      final ll = await _ensureLatLng(ridersLat, ridersLng);
      final rLat = double.tryParse(ll['lat'] ?? '0') ?? 0.0;
      final rLng = double.tryParse(ll['lng'] ?? '0') ?? 0.0;
      final pLat = double.tryParse(pickupLat) ?? 0.0;
      final pLng = double.tryParse(pickupLng) ?? 0.0;

      // Geofence Check: Picked -> Pickup Location
      final inFence = await _checkGeofence(pLat, pLng, rLat, rLng, 'Picked');
      if (!inFence) return false;

      // Save pickup location ONLY if it hasn't been saved yet (first pickup only)
      final existingPickup = await _getPickupLocation();
      if (existingPickup['lat']!.isEmpty &&
          pickupLat != '0' &&
          pickupLng != '0') {
        await _savePickupLocation(pickupLat, pickupLng);
      } else {
        debugPrint('[PICKED] Pickup location already exists, not overwriting');
      }

      // Calculate distance from rider location to pickup location if coordinates are available
      double calculatedKms = actualKms;
      if (actualKms == 0.0) {
        try {
          final riderLat = double.tryParse(ll['lat'] ?? '0') ?? 0.0;
          final riderLng = double.tryParse(ll['lng'] ?? '0') ?? 0.0;
          final pickLat = double.tryParse(pickupLat) ?? 0.0;
          final pickLng = double.tryParse(pickupLng) ?? 0.0;

          if (riderLat != 0 && riderLng != 0 && pickLat != 0 && pickLng != 0) {
            final distanceMeters = Geolocator.distanceBetween(
              riderLat,
              riderLng,
              pickLat,
              pickLng,
            );
            calculatedKms = distanceMeters / 1000.0;
            debugPrint(
              '[PICKED] Calculated distance: ${calculatedKms.toStringAsFixed(2)} km from rider to pickup',
            );
          }
        } catch (e) {
          debugPrint('[PICKED] Error calculating distance: $e');
        }
      }

      final payload = <String, dynamic>{
        'deliveryid': deliveryId,
        'orderheaderid': orderHeaderId,
        'pickuplocationid': pickupLocationId,
        'orderstatus': 'picked',
        'pickuptime': _pickedTime,
        'riderslat': ll['lat'],
        'riderslon': ll['lng'],
        'deliverylat': '',
        'deliverylong': '',
        'actualkms': calculatedKms.toStringAsFixed(2),
        'deliveryamt': 0.0,
        'deliverytype': deliveryType,
        'address': address,
        'city': city,
        'state': state,
        'suburb': suburb,
        'postcode': postcode,
        'notes': notes,
        'pickupimage': proofImage ?? '', // Standard key
        'proofimage': proofImage ?? '', // Fallback/Alternative key just in case
      };

      debugPrint('[PICKED] Payload: $payload');

      final url = _resolveUpdateUrl();
      final resp = await _updateProvider.updatePickedDelivery(payload, url);
      final ok = _isSuccess(resp);

      if (!ok) {
        debugPrint('[UPDATE][PICKED][FAILED] resp=${jsonEncode(resp)}');
      } else {
        // --- MQTT LOGIC ---
        NearleMqttService().publishLog('delivery_picked', payload);
      }
      return ok;
    } catch (e) {
      debugPrint('[UPDATE][PICKED][ERROR] $e');
      return false;
    }
  }

  // Delivered
  Future<bool> updateDeliveredStatus({
    required int deliveryId,
    required int orderHeaderId,
    String ridersLat = '0',
    String ridersLng = '0',
    int deliveryLocationId = 0,
    int smsDelivery = 0,
    String pickupLat = '0',
    String pickupLng = '0',
    String deliveryLat = '0',
    String deliveryLng = '0',
    String notes = '',
    double deliveryAmount = 0.0,
    double actualKms = 0.0,
    double collectionAmount = 0.0,
    double collectedAmount = 0.0,
    int collectionStatus = 0,
    bool wasSkipped = false,
    String orderId = '', // New parameter for timer/bonus logic
    String? proofImage, // New parameter
  }) async {
    try {
      deliveredShimmer.value = true;
      // ✅ PARALLEL OPTIMIZATION: Start fetching Prefs immediately
      final prefsFuture = SharedPreferences.getInstance();
      
      final now = DateTime.now();
      _deliveredTime = _formatDateTimeFull(now);

      final ll = await _ensureLatLng(ridersLat, ridersLng);
      final rLat = double.tryParse(ll['lat'] ?? '0') ?? 0.0;
      final rLng = double.tryParse(ll['lng'] ?? '0') ?? 0.0;
      final dLat = double.tryParse(deliveryLat) ?? 0.0;
      final dLng = double.tryParse(deliveryLng) ?? 0.0;

      // Geofence Check: Delivered -> Delivery Location
      final inFence = await _checkGeofence(dLat, dLng, rLat, rLng, 'Delivered');
      if (!inFence) {
        deliveredShimmer.value = false;
        return false;
      }

      debugPrint(
        '[DELIVERED] Rider GPS location: lat=${ll['lat']}, lng=${ll['lng']}',
      );

      // Wait for prefs to be ready
      final prefs = await prefsFuture;

      // ---------------- RIDER TIME (minutes) ----------------
      int riderTimeMinutes = 0;
      try {
        final key = 'ridertime_start_$deliveryId';
        final startStr = prefs.getString(key);
        if (startStr != null && startStr.isNotEmpty) {
          final start = DateTime.tryParse(startStr);
          if (start != null) {
            final diff = now.difference(start).inSeconds / 60.0;
            if (diff > 0) {
              // Round: 2.7 -> 3, 2.5 -> 3, 2.4 -> 2, etc.
              riderTimeMinutes = diff.round();
            }
          }
        }
        debugPrint('[DELIVERED] riderTimeMinutes=$riderTimeMinutes');
      } catch (e) {
        debugPrint('[DELIVERED] Error computing ridertime: $e');
      }

      // CRITICAL: Always calculate riderkms - even for small distances (10 meters = 0.01 km)
      // This ensures riderkms is ALWAYS passed correctly, never null
      double calculatedRiderKms = 0.0;
      final riderLat = double.tryParse(ll['lat'] ?? '0') ?? 0.0;
      final riderLng = double.tryParse(ll['lng'] ?? '0') ?? 0.0;

      // PRIORITY 1: Use cumulative distance tracked from active to delivered (most accurate)
      // This is the actual distance the rider traveled, calculated from GPS points every 30 seconds
      try {
        final cumulativeKmStr =
            prefs.getString('delivery_tracking_${deliveryId}_cumulativeKm') ??
            '';
        if (cumulativeKmStr.isNotEmpty) {
          final cumulativeKm = double.tryParse(cumulativeKmStr) ?? 0.0;
          if (cumulativeKm > 0) {
            calculatedRiderKms = cumulativeKm;
            debugPrint(
              '[DELIVERED] ✅ Using CUMULATIVE tracked distance: ${calculatedRiderKms.toStringAsFixed(4)} km',
            );
            debugPrint(
              '[DELIVERED] 📊 This is the actual distance traveled from active to delivered',
            );
          }
        }
      } catch (e) {
        debugPrint('[DELIVERED] Error getting cumulative distance: $e');
      }

      // PRIORITY 2: Only use actualKms if cumulative distance not available and it's greater than 0
      if (calculatedRiderKms == 0.0 &&
          actualKms > 0.0 &&
          riderLat != 0 &&
          riderLng != 0) {
        calculatedRiderKms = actualKms;
        debugPrint(
          '[DELIVERED] Using provided actualKms: ${calculatedRiderKms.toStringAsFixed(2)} km',
        );
      }

      // Always try to calculate distance if we have valid rider coordinates
      // CRITICAL: Use Google Maps Directions API for accurate ROAD distance (not straight-line)
      if (calculatedRiderKms == 0.0 && riderLat != 0 && riderLng != 0) {
        try {
          // Method 1: Try Start Location (Anti-Cheat) - most accurate
          final startLoc = await _getDeliveryStartLocation(deliveryId);
          final startLatStr = startLoc['lat'] ?? '';
          final startLngStr = startLoc['lng'] ?? '';

          if (startLatStr.isNotEmpty && startLngStr.isNotEmpty) {
            final startLat = double.tryParse(startLatStr) ?? 0.0;
            final startLng = double.tryParse(startLngStr) ?? 0.0;
            if (startLat != 0 && startLng != 0) {
              // Try Google Maps route distance first (accurate road distance)
              final routeKm = await _getRouteDistanceKm(
                startLat,
                startLng,
                riderLat,
                riderLng,
              );
              if (routeKm != null && routeKm > 0) {
                calculatedRiderKms = routeKm;
                debugPrint(
                  '[DELIVERED] ✅ Route distance (Start Location): ${calculatedRiderKms.toStringAsFixed(4)} km',
                );
              } else {
                // Fallback to straight-line if API fails
                final distanceMeters = Geolocator.distanceBetween(
                  startLat,
                  startLng,
                  riderLat,
                  riderLng,
                );
                calculatedRiderKms = distanceMeters / 1000.0;
                debugPrint(
                  '[DELIVERED] ⚠️ Fallback straight-line (Start Location): ${distanceMeters.toStringAsFixed(2)} meters = ${calculatedRiderKms.toStringAsFixed(4)} km',
                );
              }
            }
          }

          // Method 2: If start location failed, try provided pickup location
          if (calculatedRiderKms == 0.0 &&
              pickupLat != '0' &&
              pickupLng != '0') {
            final pickLat = double.tryParse(pickupLat) ?? 0.0;
            final pickLng = double.tryParse(pickupLng) ?? 0.0;
            if (pickLat != 0 && pickLng != 0) {
              // Try Google Maps route distance first
              final routeKm = await _getRouteDistanceKm(
                pickLat,
                pickLng,
                riderLat,
                riderLng,
              );
              if (routeKm != null && routeKm > 0) {
                calculatedRiderKms = routeKm;
                debugPrint(
                  '[DELIVERED] ✅ Route distance (Pickup Location): ${calculatedRiderKms.toStringAsFixed(4)} km',
                );
              } else {
                // Fallback to straight-line
                final distanceMeters = Geolocator.distanceBetween(
                  pickLat,
                  pickLng,
                  riderLat,
                  riderLng,
                );
                calculatedRiderKms = distanceMeters / 1000.0;
                debugPrint(
                  '[DELIVERED] ⚠️ Fallback straight-line (Pickup Location): ${distanceMeters.toStringAsFixed(2)} meters = ${calculatedRiderKms.toStringAsFixed(4)} km',
                );
              }
            }
          }

          // Method 3: Fallback to saved pickup location
          if (calculatedRiderKms == 0.0) {
            final pickupLoc = await _getPickupLocation();
            final pickLat = double.tryParse(pickupLoc['lat'] ?? '') ?? 0.0;
            final pickLng = double.tryParse(pickupLoc['lng'] ?? '') ?? 0.0;
            if (pickLat != 0 && pickLng != 0) {
              // Try Google Maps route distance first
              final routeKm = await _getRouteDistanceKm(
                pickLat,
                pickLng,
                riderLat,
                riderLng,
              );
              if (routeKm != null && routeKm > 0) {
                calculatedRiderKms = routeKm;
                debugPrint(
                  '[DELIVERED] ✅ Route distance (Saved Pickup): ${calculatedRiderKms.toStringAsFixed(4)} km',
                );
              } else {
                // Fallback to straight-line
                final distanceMeters = Geolocator.distanceBetween(
                  pickLat,
                  pickLng,
                  riderLat,
                  riderLng,
                );
                calculatedRiderKms = distanceMeters / 1000.0;
                debugPrint(
                  '[DELIVERED] ⚠️ Fallback straight-line (Saved Pickup): ${distanceMeters.toStringAsFixed(2)} meters = ${calculatedRiderKms.toStringAsFixed(4)} km',
                );
              }
            }
          }

          // Method 4: Final fallback - calculate from delivery location to current location
          if (calculatedRiderKms == 0.0 &&
              deliveryLat != '0' &&
              deliveryLng != '0') {
            final dLat = double.tryParse(deliveryLat) ?? 0.0;
            final dLng = double.tryParse(deliveryLng) ?? 0.0;
            if (dLat != 0 && dLng != 0) {
              // Try Google Maps route distance first
              final routeKm = await _getRouteDistanceKm(
                dLat,
                dLng,
                riderLat,
                riderLng,
              );
              if (routeKm != null && routeKm > 0) {
                calculatedRiderKms = routeKm;
                debugPrint(
                  '[DELIVERED] ✅ Route distance (Delivery Location): ${calculatedRiderKms.toStringAsFixed(4)} km',
                );
              } else {
                // Fallback to straight-line
                final distanceMeters = Geolocator.distanceBetween(
                  dLat,
                  dLng,
                  riderLat,
                  riderLng,
                );
                calculatedRiderKms = distanceMeters / 1000.0;
                debugPrint(
                  '[DELIVERED] ⚠️ Fallback straight-line (Delivery Location): ${distanceMeters.toStringAsFixed(2)} meters = ${calculatedRiderKms.toStringAsFixed(4)} km',
                );
              }
            }
          }
        } catch (e) {
          debugPrint('[DELIVERED] Error calculating distance: $e');
        }
      }

      // CRITICAL: Ensure riderkms is never 0 or null - use minimum value if calculation failed
      // Even 10 meters should be shown (0.01 km)
      if (calculatedRiderKms == 0.0) {
        debugPrint(
          '[DELIVERED] ⚠️ WARNING: Could not calculate distance, using minimum 0.01 km (10 meters)',
        );
        calculatedRiderKms = 0.01; // Minimum 10 meters
      }

      debugPrint(
        '[DELIVERED] Final riderkms: ${calculatedRiderKms.toStringAsFixed(4)} km',
      );

      // ---- BONUS POINTS LOGIC ----
      int bonusPoints = 0;
      if (orderId.isNotEmpty) {
        try {
          final endKey = 'eta_endtime_$orderId';
          final endSeconds = prefs.getInt(endKey);

          if (endSeconds != null) {
            final currentSeconds =
                DateTime.now().millisecondsSinceEpoch ~/ 1000;
            // If delivered before or at the end time, give bonus points
            if (currentSeconds <= endSeconds) {
              // Bonus points = riderkms rounded
              bonusPoints = calculatedRiderKms.round();
              debugPrint(
                '[DELIVERED] 🏆 ON TIME! Bonus Points: $bonusPoints (RiderKms: $calculatedRiderKms)',
              );
            } else {
              debugPrint(
                '[DELIVERED] ⏳ LATE! No Bonus Points. (Deadline: $endSeconds, Now: $currentSeconds)',
              );
            }
          } else {
            debugPrint('[DELIVERED] No ETA timer found for bonus logic.');
          }
        } catch (e) {
          debugPrint('[DELIVERED] Error calculating bonus points: $e');
        }
      }

      final double riderChargeRate = await _getRiderChargeRate();
      
      // ---- SKIP PENALTY CHECK ----
      // If the rider has exceeded skip limits in the last 3 hours, forfeit bonus points
      final skipStatus = await checkSkipStatus();
      if (skipStatus['penalty'] == true) {
        if (bonusPoints > 0) {
          debugPrint('[DELIVERED] ⚠️ Bonus points ($bonusPoints) forfeited due to skip penalty.');
          bonusPoints = 0;
        }
      }

      final double riderChargesAmountRaw =
          riderChargeRate > 0 && calculatedRiderKms > 0
          ? calculatedRiderKms * riderChargeRate
          : 0.0;
      // Ensure ridercharges is sent with 2 decimal places (Decimal(10,2) style)
      final double riderChargesAmount = double.parse(
        riderChargesAmountRaw.toStringAsFixed(2),
      );

      // CRITICAL: Always set deliverytime to current time right before creating payload
      // This ensures deliverytime is ALWAYS passed when confirming delivery, no matter what
      // Setting it here (right before payload) ensures it's the exact time of confirmation
      final currentTime = DateTime.now();
      final currentDeliveryTime = _formatDateTimeFull(currentTime);
      _deliveredTime = currentDeliveryTime; // Update the instance variable too

      debugPrint('[DELIVERED] Setting deliverytime to: $currentDeliveryTime');

      // CRITICAL: Ensure riderkms is always a valid string, never null or empty
      final riderKmsString = calculatedRiderKms > 0
          ? calculatedRiderKms.toStringAsFixed(
              4,
            ) // Use 4 decimals to show even 10 meters (0.0100 km)
          : '0.0100'; // Fallback minimum (10 meters)

      // CRITICAL: Validate coordinates before creating payload - NEVER send '0' or null
      final ridersLatStr = ll['lat'] ?? '0';
      final ridersLngStr = ll['lng'] ?? '0';
      final ridersLatDouble = double.tryParse(ridersLatStr) ?? 0.0;
      final ridersLngDouble = double.tryParse(ridersLngStr) ?? 0.0;

      if (ridersLatDouble == 0 ||
          ridersLngDouble == 0 ||
          ridersLatDouble.abs() > 90 ||
          ridersLngDouble.abs() > 180) {
        debugPrint(
          '[DELIVERED] ❌ ERROR: Invalid rider coordinates (lat=$ridersLatStr, lng=$ridersLngStr) - cannot proceed',
        );
        deliveredShimmer.value = false;
        return false; // Don't send invalid coordinates
      }

      // Validate delivery coordinates
      final deliveryLatDouble = double.tryParse(deliveryLat) ?? 0.0;
      final deliveryLngDouble = double.tryParse(deliveryLng) ?? 0.0;
      if (deliveryLatDouble == 0 ||
          deliveryLngDouble == 0 ||
          deliveryLatDouble.abs() > 90 ||
          deliveryLngDouble.abs() > 180) {
        debugPrint(
          '[DELIVERED] ❌ ERROR: Invalid delivery coordinates (lat=$deliveryLat, lng=$deliveryLng) - cannot proceed',
        );
        deliveredShimmer.value = false;
        return false; // Don't send invalid coordinates
      }

      debugPrint(
        '[DELIVERED] ✅ Validated coordinates - Rider: ($ridersLatStr, $ridersLngStr), Delivery: ($deliveryLat, $deliveryLng)',
      );

      final payload = <String, dynamic>{
        'deliveryid': deliveryId,
        'orderheaderid': orderHeaderId,
        'deliverylocationid': deliveryLocationId,
        'orderstatus': 'delivered',
        'deliveredtime': currentDeliveryTime,
        'deliverytime':
            currentDeliveryTime, // ALWAYS pass current time - required field, set right before payload
        'smsdelivery': smsDelivery,
        'riderslat': ridersLatStr, // Guaranteed non-zero and valid
        'riderslon': ridersLngStr, // Guaranteed non-zero and valid
        'raw_latitude': ll['raw_lat'] ?? '0',
        'raw_longitude': ll['raw_lng'] ?? '0',
        'velocity_lat': ll['velocity_lat'] ?? '0',
        'velocity_lng': ll['velocity_lng'] ?? '0',
        'speed': ll['speed'] ?? '0',
        'heading': ll['heading'] ?? '0',
        'pickuplat': pickupLat,
        'pickuplong': pickupLng,
        'deliverylat': deliveryLat, // Guaranteed non-zero and valid
        'deliverylong': deliveryLng, // Guaranteed non-zero and valid
        'riderkms':
            riderKmsString, // ALWAYS pass riderkms - never null, shows even 10 meters (0.0100 km)
        'ridercharges': riderChargesAmount,
        'deliveryamt': deliveryAmount,
        'collectionamt': collectionAmount,
        'collectedamt': collectedAmount,
        'collectionstatus': collectionStatus,
        'ridertime': riderTimeMinutes,
        'notes': notes,
        'wasskipped': wasSkipped,
        'bonuspts': bonusPoints, // ✅ ADDED BONUS POINTS
        'dropimage': proofImage ?? '', // Include in payload
      };


      // Update observable for UI
      lastBonusPoints.value = bonusPoints;

      debugPrint(
        '[DELIVERED] Payload includes deliverytime: $currentDeliveryTime',
      );
      debugPrint('[DELIVERED] Payload includes riderkms: $riderKmsString km');
      debugPrint('[DELIVERED] Payload includes bonuspts: $bonusPoints');

      final url = _resolveUpdateUrl();
      final resp = await _updateProvider.updateDelivery(payload, url);
      final ok = _isSuccess(resp);

      // Save current RIDER GPS location as last delivery location for next delivery
      if (ok) {
        final actualLat = ll['lat'] ?? '0';
        final actualLng = ll['lng'] ?? '0';
        if (actualLat != '0' && actualLng != '0') {
          await _saveLastDeliveryLocation(actualLat, actualLng);
        }

        // CRITICAL: Clean up cumulative distance tracking after delivery is completed
        try {
          final cumulativeKm =
              prefs.getString('delivery_tracking_${deliveryId}_cumulativeKm') ??
              '';
          if (cumulativeKm.isNotEmpty) {
            debugPrint(
              '[DELIVERED] 📊 Final cumulative distance used: $cumulativeKm km',
            );
          }
          // Clean up tracking data
          await prefs.remove('delivery_tracking_${deliveryId}_lastLat');
          await prefs.remove('delivery_tracking_${deliveryId}_lastLng');
          await prefs.remove('delivery_tracking_${deliveryId}_cumulativeKm');
          await prefs.remove('delivery_tracking_${deliveryId}_lastUpdateMs');
          await prefs.remove('ridertime_start_$deliveryId');
          await prefs.remove('delivery_proximity_alerted_$deliveryId');

          final activeTracking = prefs.getStringList('active_tracking_delivery_ids') ?? [];
          if (activeTracking.contains(deliveryId.toString())) {
             activeTracking.remove(deliveryId.toString());
             await prefs.setStringList('active_tracking_delivery_ids', activeTracking);
          }

          // ✅ Clean up ETA timer
          if (orderId.isNotEmpty) {
            await prefs.remove('eta_endtime_$orderId');
          }
          debugPrint(
            '[DELIVERED] 🧹 Cleaned up distance tracking for deliveryId: $deliveryId',
          );
        } catch (e) {
          debugPrint('[DELIVERED] Error cleaning up tracking: $e');
        }
      }

      // Reset foreground delivery notification flag
      await setNotificationSent(false);
      deliveredShimmer.value = false;

      if (!ok) {
        debugPrint('[UPDATE][DELIVERED][FAILED] resp=${jsonEncode(resp)}');
      } else {
        // --- MQTT LOGIC ---
        NearleMqttService().publishLog('delivery_completed', payload);
      }
      return ok;
    } catch (e) {
      debugPrint('[UPDATE][DELIVERED][ERROR] $e');
      deliveredShimmer.value = false;
      return false;
    }
  }

  // ---------------- Date/Time helpers ----------------
  String _two(int n) => n.toString().padLeft(2, '0');

  String _formatDateTimeFull(DateTime dt) {
    final y = dt.year.toString();
    final m = _two(dt.month);
    final d = _two(dt.day);
    final hh = _two(dt.hour);
    final mm = _two(dt.minute);
    final ss = _two(dt.second);
    return "$y-$m-$d $hh:$mm:$ss";
  }

  String _resolveUpdateUrl() {
    return ApiConstants.mainRoute == 'live'
        ? ApiConstants.updateDeliveryLive
        : ApiConstants.updateDeliveryDev;
  }

  // ---------------- SharedPrefs helpers ----------------
  Future<void> setNotificationSent(bool value) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setBool('notificationSent', value);
    } catch (_) {}
  }

  Future<int> getLogSecondsOrDefault([int fallback = 60]) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      return prefs.getInt('logSeconds') ?? fallback;
    } catch (_) {
      return fallback;
    }
  }

  Future<void> persistDeliveryLogSnapshot(
    List<Map<String, dynamic>> logs,
  ) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('deliveryLog', jsonEncode(logs));
    } catch (_) {}
  }

  // ---------------- Geofencing helpers ----------------
  Future<int> _getDeliveryRadius() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      return prefs.getInt('deliveryradius') ?? 100;
    } catch (_) {
      return 100;
    }
  }

  // Helper method to show snackbar reliably in both debug and release builds
  void _showErrorSnackbar(
    String title,
    String message, {
    Color? bgColor,
    int seconds = 4,
    int retryCount = 0,
  }) {
    // Prevent infinite recursion
    if (retryCount > 3) {
      print('[GEOFENCE] Max retries reached for snackbar. Title: $title');
      return;
    }

    try {
      final context = Get.key.currentContext;

      // Get bottom safe area padding
      final double bottomSafePadding = context != null
          ? MediaQuery.of(context).padding.bottom
          : 0;

      Get.snackbar(
        title,
        message,
        backgroundColor: bgColor ?? Colors.red,
        colorText: Colors.white,
        duration: Duration(seconds: seconds),
        snackPosition: SnackPosition.BOTTOM,
        isDismissible: true,
        shouldIconPulse: true,
        margin: EdgeInsets.fromLTRB(
          12,
          0,
          12,
          12 + bottomSafePadding, //  SafeArea bottom
        ),
        borderRadius: 10,
        maxWidth: 400,
        forwardAnimationCurve: Curves.easeOutBack,
        reverseAnimationCurve: Curves.easeInBack,
      );
    } catch (e) {
      print(
        '[GEOFENCE] Error showing snackbar (attempt ${retryCount + 1}): $e',
      );
      print(
        '[GEOFENCE] GetX context available: ${Get.key.currentContext != null}',
      );

      Future.delayed(const Duration(milliseconds: 400), () {
        try {
          Get.snackbar(
            title,
            message,
            backgroundColor: bgColor ?? Colors.red,
            colorText: Colors.white,
            snackPosition: SnackPosition.BOTTOM,
          );
        } catch (e2) {
          print('[GEOFENCE] Retry snackbar failed: $e2');

          if (retryCount < 2) {
            Future.delayed(const Duration(milliseconds: 300), () {
              _showErrorSnackbar(
                title,
                message,
                bgColor: bgColor,
                seconds: seconds,
                retryCount: retryCount + 1,
              );
            });
          }
        }
      });
    }
  }

  Future<bool> _checkGeofence(
    double targetLat,
    double targetLng,
    double currentLat,
    double currentLng,
    String action,
  ) async {
    // Validate coordinates - ensure they are valid GPS coordinates
    final bool hasValidTarget =
        targetLat != 0 &&
        targetLng != 0 &&
        targetLat.abs() <= 90 &&
        targetLng.abs() <= 180;
    final bool hasValidCurrent =
        currentLat != 0 &&
        currentLng != 0 &&
        currentLat.abs() <= 90 &&
        currentLng.abs() <= 180;

    if (!hasValidTarget || !hasValidCurrent) {
      // If coordinates are missing or invalid, show error and block
      if (kDebugMode) {
        debugPrint(
          '[GEOFENCE] Missing or invalid coordinates for $action. Target: ($targetLat, $targetLng), Current: ($currentLat, $currentLng)',
        );
      }

      // Show warning snackbar using helper method
      _showErrorSnackbar(
        'Location Warning',
        'Missing coordinates. Proceeding with update.',
        bgColor: Colors.orange,
        seconds: 3,
      );
      return true; // Allow update to proceed despite missing coords
    }

    try {
      final radius = await _getDeliveryRadius();
      final distance = Geolocator.distanceBetween(
        targetLat,
        targetLng,
        currentLat,
        currentLng,
      );
      final distanceKm = distance / 1000.0;
      final radiusKm = radius / 1000.0;
      final distanceMeters = distance;

      if (kDebugMode) {
        debugPrint(
          '[GEOFENCE] Action: $action | Target: ($targetLat, $targetLng) | Current: ($currentLat, $currentLng) | Distance: ${distanceMeters.toStringAsFixed(1)}m (${distanceKm.toStringAsFixed(3)}km) | Radius: ${radius}m (${radiusKm.toStringAsFixed(3)}km)',
        );
      }

      if (distance > radius) {
        // Show error snackbar with clear message using helper method
        _showErrorSnackbar(
          'Location Error',
          'You are too far from the location to mark as $action.\nDistance: ${distanceMeters.toStringAsFixed(0)} m (${distanceKm.toStringAsFixed(2)} km)\nRequired: Within $radius m',
          seconds: 5,
        );
        return false;
      }
      return true;
    } catch (e) {
      if (kDebugMode) {
        debugPrint('[GEOFENCE] Error calculating distance: $e');
      }
      // On error, show warning but allow (fail-safe)
      _showErrorSnackbar(
        'Location Warning',
        'Unable to verify distance. Proceeding with caution.',
        bgColor: Colors.orange,
        seconds: 3,
      );
      return true; // Allow on error (fail-safe)
    }
  }

  // Get last delivery location (for calculating riderkms between deliveries)
  Future<Map<String, String>> _getLastDeliveryLocation() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final lat = prefs.getString('lastDeliveryLat') ?? '';
      final lng = prefs.getString('lastDeliveryLng') ?? '';
      if (lat.isNotEmpty && lng.isNotEmpty) {
        return {'lat': lat, 'lng': lng};
      }
    } catch (_) {}
    return {'lat': '', 'lng': ''};
  }

  // Save last delivery location (rider's actual GPS location, not destination)
  Future<void> _saveLastDeliveryLocation(String lat, String lng) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('lastDeliveryLat', lat);
      await prefs.setString('lastDeliveryLng', lng);
      debugPrint('[TRACKING] Saved last delivery location: lat=$lat, lng=$lng');
    } catch (e) {
      debugPrint('[TRACKING] Error saving last delivery location: $e');
    }
  }

  // Save start location for a specific delivery (when "Start Navigation" is slid)
  Future<void> _saveDeliveryStartLocation(
    int deliveryId,
    String lat,
    String lng,
  ) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('delivery_start_${deliveryId}_lat', lat);
      await prefs.setString('delivery_start_${deliveryId}_lng', lng);
      debugPrint(
        '[TRACKING] Saved start location for delivery $deliveryId: lat=$lat, lng=$lng',
      );
    } catch (e) {
      debugPrint('[TRACKING] Error saving delivery start location: $e');
    }
  }

  // Get start location for a specific delivery
  Future<Map<String, String>> _getDeliveryStartLocation(int deliveryId) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final lat = prefs.getString('delivery_start_${deliveryId}_lat') ?? '';
      final lng = prefs.getString('delivery_start_${deliveryId}_lng') ?? '';
      if (lat.isNotEmpty && lng.isNotEmpty) {
        return {'lat': lat, 'lng': lng};
      }
    } catch (_) {}
    return {'lat': '', 'lng': ''};
  }

  // Get pickup location (for first delivery riderkms calculation)
  Future<Map<String, String>> _getPickupLocation() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final lat = prefs.getString('pickupLat') ?? '';
      final lng = prefs.getString('pickupLng') ?? '';
      if (lat.isNotEmpty && lng.isNotEmpty) {
        return {'lat': lat, 'lng': lng};
      }
    } catch (_) {}
    return {'lat': '', 'lng': ''};
  }

  // Google Maps API Key
  static const String _googleMapsApiKey =
      'AIzaSyBhkGfnq27sN0wV5y_S-M2KojpFTk_by-Q';

  // Get route distance from Google Maps Directions API (accurate road distance)
  // Returns distance in kilometers, or null if API fails
  Future<double?> _getRouteDistanceKm(
    double originLat,
    double originLng,
    double destLat,
    double destLng,
  ) async {
    try {
      // Validate coordinates
      if (originLat == 0 ||
          originLng == 0 ||
          destLat == 0 ||
          destLng == 0 ||
          originLat.abs() > 90 ||
          originLng.abs() > 180 ||
          destLat.abs() > 90 ||
          destLng.abs() > 180) {
        debugPrint('[ROUTE] Invalid coordinates for route calculation');
        return null;
      }

      // Google Maps Directions API endpoint
      final url = Uri.parse(
        'https://maps.googleapis.com/maps/api/directions/json'
        '?origin=$originLat,$originLng'
        '&destination=$destLat,$destLng'
        '&key=$_googleMapsApiKey'
        '&units=metric'
        '&mode=driving', // Use driving mode for accurate road distance
      );

      debugPrint(
        '[ROUTE] Requesting route distance from ($originLat, $originLng) to ($destLat, $destLng)',
      );

      final response = await http
          .get(url)
          .timeout(
            const Duration(seconds: 5),
            onTimeout: () {
              debugPrint('[ROUTE] API timeout');
              return http.Response('', 408);
            },
          );

      if (response.statusCode == 200) {
        final data = json.decode(response.body);

        if (data['status'] == 'OK' &&
            data['routes'] != null &&
            (data['routes'] as List).isNotEmpty) {
          final route = (data['routes'] as List).first;
          final legs = route['legs'] as List?;

          if (legs != null && legs.isNotEmpty) {
            double totalDistanceMeters = 0.0;
            for (final leg in legs) {
              final distance = leg['distance'] as Map<String, dynamic>?;
              if (distance != null && distance['value'] != null) {
                totalDistanceMeters += (distance['value'] as num).toDouble();
              }
            }

            final distanceKm = totalDistanceMeters / 1000.0;
            debugPrint(
              '[ROUTE] ✅ Route distance: ${distanceKm.toStringAsFixed(4)} km (${totalDistanceMeters.toStringAsFixed(0)} meters)',
            );
            return distanceKm;
          }
        } else {
          debugPrint('[ROUTE] ⚠️ API returned status: ${data['status']}');
        }
      } else {
        debugPrint('[ROUTE] ⚠️ API error: ${response.statusCode}');
      }
    } catch (e) {
      debugPrint('[ROUTE] ❌ Error getting route distance: $e');
    }

    return null; // Return null if API fails - will fallback to straight-line
  }

  // Save pickup location
  Future<void> _savePickupLocation(String lat, String lng) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.setString('pickupLat', lat);
      await prefs.setString('pickupLng', lng);
      debugPrint('[TRACKING] Saved pickup location: lat=$lat, lng=$lng');
    } catch (e) {
      debugPrint('[TRACKING] Error saving pickup location: $e');
    }
  }

  Future<double> _getRiderChargeRate() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      // Prefer new fuelcharge key; fall back to legacy firstmilecharge if needed
      Object? raw = prefs.get('fuelcharge');
      raw ??= prefs.get('firstmilecharge');
      if (raw is double) return raw;
      if (raw is int) return raw.toDouble();
      if (raw is String) {
        return double.tryParse(raw) ?? 0.0;
      }
    } catch (_) {}
    return 0.0;
  }

  // Clear delivery tracking (when all deliveries are done or starting fresh)
  Future<void> clearDeliveryTracking() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      await prefs.remove('lastDeliveryLat');
      await prefs.remove('lastDeliveryLng');
      await prefs.remove('pickupLat');
      await prefs.remove('pickupLng');
      debugPrint('[TRACKING] Cleared all delivery tracking data');
    } catch (e) {
      debugPrint('[TRACKING] Error clearing delivery tracking: $e');
    }
  }

  // ---------------- Status Updates ----------------

  // Accepted
  Future<bool> updateAcceptedStatus({
    required int deliveryId,
    required int orderHeaderId,
    String ridersLat = '0',
    String ridersLng = '0',
    String notes = '',
  }) async {
    try {
      final ll = await _ensureLatLng(ridersLat, ridersLng);
      final acceptedTime = _formatDateTimeFull(DateTime.now());
      final payload = <String, dynamic>{
        'deliveryid': deliveryId,
        'orderheaderid': orderHeaderId,
        'orderstatus': 'accepted',
        'acceptedtime': acceptedTime,
        'riderslat': ll['lat'],
        'riderslon': ll['lng'],
        'raw_latitude': ll['raw_lat'] ?? '0',
        'raw_longitude': ll['raw_lng'] ?? '0',
        'velocity_lat': ll['velocity_lat'] ?? '0',
        'velocity_lng': ll['velocity_lng'] ?? '0',
        'speed': ll['speed'] ?? '0',
        'heading': ll['heading'] ?? '0',
        'deliverylat': '',
        'deliverylong': '',
        'actualkms': '',
        'deliveryamt': 0.0,
        'notes': notes,
      };

      final url = _resolveUpdateUrl();
      final resp = await _updateProvider.updateDelivery(payload, url);
      final ok = _isSuccess(resp);
      if (!ok) {
        // Log entire response for debugging
        try {
          debugPrint('[UPDATE][ACCEPTED][FAILED] resp=${jsonEncode(resp)}');
        } catch (_) {}
      }
      return ok;
    } catch (e) {
      debugPrint('[UPDATE][ACCEPTED][ERROR] $e');
      return false;
    }
  }

  // Active (start navigation)
  Future<bool> updateActiveStatus({
    required int deliveryId,
    required int orderHeaderId,
    String ridersLat = '0',
    String ridersLng = '0',
    String notes = '',
    String orderId = '',
  }) async {
    try {
      final now = DateTime.now();
      _activeTime = _formatDateTimeFull(now);
      final prefs = await SharedPreferences.getInstance();
      String startTime = prefs.getString('delivery_starttime_$deliveryId') ?? '';
      
      if (startTime.isEmpty) {
        startTime = _formatDateTimeFull(now);
      } else {
         debugPrint('[UPDATE][ACTIVE] Using preserved starttime: $startTime');
      }

      // Ensure we never send 0/0 for rider location.
      Map<String, String> ll = await _ensureLatLng(ridersLat, ridersLng);
      if ((ll['lat'] ?? '0') == '0' || (ll['lng'] ?? '0') == '0') {
        try {
          final last = await Geolocator.getLastKnownPosition();
          if (last != null) {
            ll = {
              'lat': last.latitude.toStringAsFixed(6),
              'lng': last.longitude.toStringAsFixed(6),
            };
          }
        } catch (_) {}
      }
      if ((ll['lat'] ?? '0') == '0' || (ll['lng'] ?? '0') == '0') {
        try {
          final pos = await Geolocator.getCurrentPosition(
            desiredAccuracy: LocationAccuracy.low,
            timeLimit: const Duration(seconds: 3),
          );
          ll = {
            'lat': pos.latitude.toStringAsFixed(6),
            'lng': pos.longitude.toStringAsFixed(6),
          };
        } catch (_) {}
      }

      // If we still don't have a valid location, bail out to avoid sending 0/0.
      if ((ll['lat'] ?? '0') == '0' || (ll['lng'] ?? '0') == '0') {
        debugPrint('[UPDATE][ACTIVE] No valid rider coordinates; aborting');
        return false;
      }
      final payload = <String, dynamic>{
        'deliveryid': deliveryId,
        'orderheaderid': orderHeaderId,
        'orderstatus': 'active',
        'activetime': _activeTime,
        'starttime': startTime, // Add starttime when status changes to active
        'activelat': ll['lat'],
        'activelon': ll['lng'],
        'riderslat': ll['lat'],
        'riderslon': ll['lng'],
        'raw_latitude': ll['raw_lat'] ?? '0',
        'raw_longitude': ll['raw_lng'] ?? '0',
        'velocity_lat': ll['velocity_lat'] ?? '0',
        'velocity_lng': ll['velocity_lng'] ?? '0',
        'speed': ll['speed'] ?? '0',
        'heading': ll['heading'] ?? '0',
        'deliverylat': '',
        'deliverylong': '',
        'actualkms': '',
        'deliveryamt': 0.0,
        'notes': notes,
      };

      final url = _resolveUpdateUrl();
      final resp = await _updateProvider.updateActiveDelivery(payload, url);
      final ok = _isSuccess(resp);

      if (ok) {
        final lat = ll['lat'] ?? '0';
        final lng = ll['lng'] ?? '0';
        if (lat != '0' && lng != '0') {
          await _saveDeliveryStartLocation(deliveryId, lat, lng);
        }

        // Save starttime to SharedPreferences for delivery logs (using deliveryId as key)
        // This will be retrieved when creating delivery log payloads
        try {
          final prefs = await SharedPreferences.getInstance();
          await prefs.setString('delivery_starttime_$deliveryId', startTime);
          if (orderId.isNotEmpty) {
            await prefs.setString('current_riding_order_id', orderId);
          }
          await prefs.setString('current_riding_delivery_id', deliveryId.toString());
          debugPrint(
            '[UPDATE][ACTIVE] Saved starttime: $startTime for deliveryId: $deliveryId',
          );

          // CRITICAL: Initialize cumulative distance tracking when delivery becomes active
          // Reset any previous tracking data and set initial location
          await prefs.setString('delivery_tracking_${deliveryId}_lastLat', lat);
          await prefs.setString('delivery_tracking_${deliveryId}_lastLng', lng);
          await prefs.setString(
            'delivery_tracking_${deliveryId}_cumulativeKm',
            '0.0',
          );
          await prefs.setInt(
            'delivery_tracking_${deliveryId}_lastUpdateMs',
            DateTime.now().millisecondsSinceEpoch,
          );
          
          final activeTracking = prefs.getStringList('active_tracking_delivery_ids') ?? [];
          if (!activeTracking.contains(deliveryId.toString())) {
             activeTracking.add(deliveryId.toString());
             await prefs.setStringList('active_tracking_delivery_ids', activeTracking);
          }

          debugPrint(
            '[UPDATE][ACTIVE] 🚀 Initialized distance tracking for deliveryId: $deliveryId at ($lat, $lng)',
          );
        } catch (e) {
          debugPrint('[UPDATE][ACTIVE] Error saving starttime: $e');
        }
      } else {
        debugPrint('[UPDATE][ACTIVE][FAILED] resp=${jsonEncode(resp)}');
      }

      return ok;
    } catch (e) {
      debugPrint('[UPDATE][ACTIVE][ERROR] $e');
      return false;
    }
  }

  // Arrived
  Future<bool> updateArrivedStatus({
    required int deliveryId,
    required int orderHeaderId,
    String ridersLat = '0',
    String ridersLng = '0',
    String pickupLat = '0',
    String pickupLng = '0',
    String notes = '',
  }) async {
    // START LOADING IMMEDIATELY for better UX
    arrivedShimmer.value = true;
    try {
      final ll = await _ensureLatLng(ridersLat, ridersLng);
      final rLat = double.tryParse(ll['lat'] ?? '0') ?? 0.0;
      final rLng = double.tryParse(ll['lng'] ?? '0') ?? 0.0;
      final pLat = double.tryParse(pickupLat) ?? 0.0;
      final pLng = double.tryParse(pickupLng) ?? 0.0;

      // Geofence Check: Arrived -> Pickup Location
      final inFence = await _checkGeofence(pLat, pLng, rLat, rLng, 'Arrived');
      if (!inFence) {
         arrivedShimmer.value = false;
         return false;
      }
      
      final now = DateTime.now();
      _arrivedTime = _formatDateTimeFull(now);

      final payload = <String, dynamic>{
        'deliveryid': deliveryId,
        'orderheaderid': orderHeaderId,
        'orderstatus': 'arrived',
        'arrivaltime': _arrivedTime,
        'riderslat': ll['lat'],
        'riderslon': ll['lng'],
        'raw_latitude': ll['raw_lat'] ?? '0',
        'raw_longitude': ll['raw_lng'] ?? '0',
        'velocity_lat': ll['velocity_lat'] ?? '0',
        'velocity_lng': ll['velocity_lng'] ?? '0',
        'speed': ll['speed'] ?? '0',
        'heading': ll['heading'] ?? '0',
        'deliverylat': '',
        'deliverylong': '',
        'actualkms': '',
        'deliveryamt': 0.0,
        'notes': notes,
      };

      final url = _resolveUpdateUrl();
      final resp = await _updateProvider.updateArrivedDelivery(payload, url);
      final ok = _isSuccess(resp);
      
      // Stop shimmer immediately after response
      arrivedShimmer.value = false;

      if (!ok) {
        debugPrint('[UPDATE][ARRIVED][FAILED] resp=${jsonEncode(resp)}');
      }
      return ok;
    } catch (e) {
      debugPrint('[UPDATE][ARRIVED][ERROR] $e');
      arrivedShimmer.value = false;
      return false;
    }
  }

  // Rejected (remove from queue)
  Future<bool> updateRejectedStatus({
    required int deliveryId,
    required int orderHeaderId,
    String ridersLat = '0',
    String ridersLng = '0',
    String notes = '',
  }) async {
    try {
      final ll = await _ensureLatLng(ridersLat, ridersLng);
      final payload = <String, dynamic>{
        'deliveryid': deliveryId,
        'orderheaderid': orderHeaderId,
        'orderstatus': 'rejected',
        'riderslat': ll['lat'],
        'riderslon': ll['lng'],
        'raw_latitude': ll['raw_lat'] ?? '0',
        'raw_longitude': ll['raw_lng'] ?? '0',
        'velocity_lat': ll['velocity_lat'] ?? '0',
        'velocity_lng': ll['velocity_lng'] ?? '0',
        'speed': ll['speed'] ?? '0',
        'heading': ll['heading'] ?? '0',
        'deliverylat': '',
        'deliverylong': '',
        'actualkms': '',
        'deliveryamt': 0.0,
        'notes': notes,
      };

      final url = _resolveUpdateUrl();
      final resp = await _updateProvider.updateDelivery(payload, url);
      final ok = _isSuccess(resp);

      if (!ok) {
        debugPrint('[UPDATE][REJECTED][FAILED] resp=${jsonEncode(resp)}');
      }
      return ok;
    } catch (e) {
      debugPrint('[UPDATE][REJECTED][ERROR] $e');
      return false;
    }
  }

  // Cancelled (delivery cancelled by rider)
  Future<bool> updateCancelledStatus({
    required int deliveryId,
    required int orderHeaderId,
    String ridersLat = '0',
    String ridersLng = '0',
    String pickupLat = '0',
    String pickupLng = '0',
    String deliveryLat = '0',
    String deliveryLng = '0',
    String notes = '',
    double actualKms = 0.0,
    bool wasSkipped = false,
  }) async {
    try {
      final now = DateTime.now();
      _cancelledTime = _formatDateTimeFull(now);

      final ll = await _ensureLatLng(ridersLat, ridersLng);

      final riderLat = double.tryParse(ll['lat'] ?? '0') ?? 0.0;
      final riderLng = double.tryParse(ll['lng'] ?? '0') ?? 0.0;
      final dLat = double.tryParse(deliveryLat) ?? 0.0;
      final dLng = double.tryParse(deliveryLng) ?? 0.0;

      // ✅ SPECIAL CASE: Skip geofence check for "Incorrect Location" cancellation
      // When user cancels due to incorrect location, we don't verify range
      final bool isIncorrectLocation = notes.toLowerCase().contains(
        'incorrect location',
      );

      if (isIncorrectLocation) {
        debugPrint(
          '[CANCELLED] ⚠️ Skipping geofence check - Reason: Incorrect Location',
        );
      } else {
        // Geofence Check: Cancelled -> Delivery Location (for all other reasons)
        final inFence = await _checkGeofence(
          dLat,
          dLng,
          riderLat,
          riderLng,
          'Cancelled',
        );
        if (!inFence) {
          debugPrint(
            '[CANCELLED] ❌ Geofence check failed - Rider not in range',
          );
          return false;
        }
      }

      // PRIORITY 1: Use cumulative distance tracked from active to cancelled (most accurate)
      // This is the actual distance the rider traveled, calculated from GPS points every 30 seconds
      double calculatedKms = 0.0;
      try {
        final prefs = await SharedPreferences.getInstance();
        final cumulativeKmStr =
            prefs.getString('delivery_tracking_${deliveryId}_cumulativeKm') ??
            '';
        if (cumulativeKmStr.isNotEmpty) {
          final cumulativeKm = double.tryParse(cumulativeKmStr) ?? 0.0;
          if (cumulativeKm > 0) {
            calculatedKms = cumulativeKm;
            debugPrint(
              '[CANCELLED] ✅ Using CUMULATIVE tracked distance: ${calculatedKms.toStringAsFixed(4)} km',
            );
            debugPrint(
              '[CANCELLED] 📊 This is the actual distance traveled from active to cancelled',
            );
          }
        }
      } catch (e) {
        debugPrint('[CANCELLED] Error getting cumulative distance: $e');
      }

      // PRIORITY 2: Fallback to actualKms if cumulative not available
      if (calculatedKms == 0.0) {
        calculatedKms = actualKms;
      }

      if (wasSkipped && calculatedKms == 0.0) {
        try {
          final lastDelivery = await _getLastDeliveryLocation();
          final lastLat = double.tryParse(lastDelivery['lat'] ?? '') ?? 0.0;
          final lastLng = double.tryParse(lastDelivery['lng'] ?? '') ?? 0.0;
          if (lastLat != 0 && lastLng != 0 && riderLat != 0 && riderLng != 0) {
            final distanceMeters = Geolocator.distanceBetween(
              lastLat,
              lastLng,
              riderLat,
              riderLng,
            );
            calculatedKms = distanceMeters / 1000.0;
            debugPrint(
              '[CANCELLED] Skipped delivery - rider travelled ${calculatedKms.toStringAsFixed(2)} km from last stop to cancellation point',
            );
          }
        } catch (e) {
          debugPrint('[CANCELLED] Error calculating skipped distance: $e');
        }
      }

      if (calculatedKms == 0.0) {
        try {
          // ANTI-CHEAT: Try to get the locked "Start Location" for this delivery
          final startLoc = await _getDeliveryStartLocation(deliveryId);
          final startLatStr = startLoc['lat'] ?? '';
          final startLngStr = startLoc['lng'] ?? '';

          if (startLatStr.isNotEmpty && startLngStr.isNotEmpty) {
            final startLat = double.tryParse(startLatStr) ?? 0.0;
            final startLng = double.tryParse(startLngStr) ?? 0.0;
            if (startLat != 0 &&
                startLng != 0 &&
                riderLat != 0 &&
                riderLng != 0) {
              // Try Google Maps route distance first (accurate road distance)
              final routeKm = await _getRouteDistanceKm(
                startLat,
                startLng,
                riderLat,
                riderLng,
              );
              if (routeKm != null && routeKm > 0) {
                calculatedKms = routeKm;
                debugPrint(
                  '[CANCELLED] ✅ Route distance (Anti-Cheat): ${calculatedKms.toStringAsFixed(4)} km from Start Location to cancellation point',
                );
              } else {
                // Fallback to straight-line if API fails
                final distanceMeters = Geolocator.distanceBetween(
                  startLat,
                  startLng,
                  riderLat,
                  riderLng,
                );
                calculatedKms = distanceMeters / 1000.0;
                debugPrint(
                  '[CANCELLED] ⚠️ Fallback straight-line (Anti-Cheat): ${calculatedKms.toStringAsFixed(4)} km',
                );
              }
            }
          } else {
            // Fallback to Pickup -> Current
            final pickLat = double.tryParse(pickupLat) ?? 0.0;
            final pickLng = double.tryParse(pickupLng) ?? 0.0;
            if (pickLat != 0 &&
                pickLng != 0 &&
                riderLat != 0 &&
                riderLng != 0) {
              // Try Google Maps route distance first
              final routeKm = await _getRouteDistanceKm(
                pickLat,
                pickLng,
                riderLat,
                riderLng,
              );
              if (routeKm != null && routeKm > 0) {
                calculatedKms = routeKm;
                debugPrint(
                  '[CANCELLED] ✅ Route distance: ${calculatedKms.toStringAsFixed(4)} km from pickup to cancellation point',
                );
              } else {
                // Fallback to straight-line if API fails
                final distanceMeters = Geolocator.distanceBetween(
                  pickLat,
                  pickLng,
                  riderLat,
                  riderLng,
                );
                calculatedKms = distanceMeters / 1000.0;
                debugPrint(
                  '[CANCELLED] ⚠️ Fallback straight-line: ${calculatedKms.toStringAsFixed(4)} km',
                );
              }
            }
          }
        } catch (e) {
          debugPrint('[CANCELLED] Error calculating distance: $e');
        }
      }

      final double riderChargeRate = await _getRiderChargeRate();
      final double riderChargesAmountRaw =
          riderChargeRate > 0 && calculatedKms > 0
          ? calculatedKms * riderChargeRate
          : 0.0;
      final double riderChargesAmount = double.parse(
        riderChargesAmountRaw.toStringAsFixed(2),
      );
      if (riderChargeRate > 0) {
        debugPrint(
          '[CANCELLED] Applying rider charge rate $riderChargeRate => ridercharges ${riderChargesAmount.toStringAsFixed(2)}',
        );
      }

      // CRITICAL: Clean up cumulative distance tracking after cancellation
      try {
        final prefs = await SharedPreferences.getInstance();
        final cumulativeKm =
            prefs.getString('delivery_tracking_${deliveryId}_cumulativeKm') ??
            '';
        if (cumulativeKm.isNotEmpty) {
          debugPrint(
            '[CANCELLED] 📊 Final cumulative distance used: $cumulativeKm km',
          );
        }
        // Clean up tracking data
        await prefs.remove('delivery_tracking_${deliveryId}_lastLat');
        await prefs.remove('delivery_tracking_${deliveryId}_lastLng');
        await prefs.remove('delivery_tracking_${deliveryId}_cumulativeKm');
        await prefs.remove('delivery_tracking_${deliveryId}_lastUpdateMs');
        await prefs.remove('delivery_proximity_alerted_$deliveryId');

        final activeTracking = prefs.getStringList('active_tracking_delivery_ids') ?? [];
        if (activeTracking.contains(deliveryId.toString())) {
           activeTracking.remove(deliveryId.toString());
           await prefs.setStringList('active_tracking_delivery_ids', activeTracking);
        }

        debugPrint(
          '[CANCELLED] 🧹 Cleaned up distance tracking for deliveryId: $deliveryId',
        );
      } catch (e) {
        debugPrint('[CANCELLED] Error cleaning up tracking: $e');
      }

      // CRITICAL: Validate coordinates before creating payload - NEVER send '0' or null
      final ridersLatStr = ll['lat'] ?? '0';
      final ridersLngStr = ll['lng'] ?? '0';
      final ridersLatDouble = double.tryParse(ridersLatStr) ?? 0.0;
      final ridersLngDouble = double.tryParse(ridersLngStr) ?? 0.0;

      if (ridersLatDouble == 0 ||
          ridersLngDouble == 0 ||
          ridersLatDouble.abs() > 90 ||
          ridersLngDouble.abs() > 180) {
        debugPrint(
          '[CANCELLED] ❌ ERROR: Invalid rider coordinates (lat=$ridersLatStr, lng=$ridersLngStr) - cannot proceed',
        );
        return false; // Don't send invalid coordinates
      }

      debugPrint(
        '[CANCELLED] ✅ Validated coordinates - Rider: ($ridersLatStr, $ridersLngStr)',
      );

      final payload = <String, dynamic>{
        'deliveryid': deliveryId,
        'orderheaderid': orderHeaderId,
        'orderstatus': 'cancelled',
        'canceltime': _cancelledTime,
        'riderslat': ridersLatStr, // Guaranteed non-zero and valid
        'riderslon': ridersLngStr, // Guaranteed non-zero and valid
        'raw_latitude': ll['raw_lat'] ?? '0',
        'raw_longitude': ll['raw_lng'] ?? '0',
        'velocity_lat': ll['velocity_lat'] ?? '0',
        'velocity_lng': ll['velocity_lng'] ?? '0',
        'speed': ll['speed'] ?? '0',
        'heading': ll['heading'] ?? '0',
        'deliverylat': '',
        'deliverylong': '',
        'riderkms': calculatedKms.toStringAsFixed(2),
        'ridercharges': riderChargesAmount,
        'deliveryamt': 0.0,
        'notes': notes,
      };

      final url = _resolveUpdateUrl();
      final resp = await _updateProvider.updateDelivery(payload, url);
      final ok = _isSuccess(resp);

      if (ok) {
        // Update last delivery location to current location so next order starts here
        final actualLat = ll['lat'] ?? '0';
        final actualLng = ll['lng'] ?? '0';
        if (actualLat != '0' && actualLng != '0') {
          await _saveLastDeliveryLocation(actualLat, actualLng);
        }
      }

      if (!ok) {
        debugPrint('[UPDATE][CANCELLED][FAILED] resp=${jsonEncode(resp)}');
      }
      return ok;
    } catch (e) {
      debugPrint('[UPDATE][CANCELLED][ERROR] $e');
      return false;
    }
  }

  // Skipped (delivery skipped by rider)
  Future<bool> updateSkippedStatus({
    required int deliveryId,
    required int orderHeaderId,
    String ridersLat = '0',
    String ridersLng = '0',
    String notes = '',
  }) async {
    try {
      final ll = await _ensureLatLng(ridersLat, ridersLng);
      final skippedTime = _formatDateTimeFull(DateTime.now());
      final payload = <String, dynamic>{
        'deliveryid': deliveryId,
        'orderheaderid': orderHeaderId,
        'orderstatus': 'skipped',
        'skippedtime': skippedTime,
        'riderslat': ll['lat'],
        'riderslon': ll['lng'],
        'deliverylat': '',
        'deliverylong': '',
        'actualkms': '',
        'deliveryamt': 0.0,
        'notes': notes,
      };

      // Calculate distance for skipped order (Anti-Cheat)
      double calculatedKms = 0.0;
      try {
        final riderLat = double.tryParse(ll['lat'] ?? '0') ?? 0.0;
        final riderLng = double.tryParse(ll['lng'] ?? '0') ?? 0.0;

        if (riderLat != 0 && riderLng != 0) {
          // Try Start Location first
          final startLoc = await _getDeliveryStartLocation(deliveryId);
          final startLatStr = startLoc['lat'] ?? '';
          final startLngStr = startLoc['lng'] ?? '';

          if (startLatStr.isNotEmpty && startLngStr.isNotEmpty) {
            final startLat = double.tryParse(startLatStr) ?? 0.0;
            final startLng = double.tryParse(startLngStr) ?? 0.0;
            if (startLat != 0 && startLng != 0) {
              // Try Google Maps route distance first (accurate road distance)
              final routeKm = await _getRouteDistanceKm(
                startLat,
                startLng,
                riderLat,
                riderLng,
              );
              if (routeKm != null && routeKm > 0) {
                calculatedKms = routeKm;
                debugPrint(
                  '[SKIPPED] ✅ Route distance (Start Location): ${calculatedKms.toStringAsFixed(4)} km',
                );
              } else {
                // Fallback to straight-line if API fails
                final distanceMeters = Geolocator.distanceBetween(
                  startLat,
                  startLng,
                  riderLat,
                  riderLng,
                );
                calculatedKms = distanceMeters / 1000.0;
                debugPrint(
                  '[SKIPPED] ⚠️ Fallback straight-line (Start Location): ${calculatedKms.toStringAsFixed(4)} km',
                );
              }
            }
          } else {
            // Fallback to Last Delivery Location
            final lastDelivery = await _getLastDeliveryLocation();
            final lastLat = double.tryParse(lastDelivery['lat'] ?? '') ?? 0.0;
            final lastLng = double.tryParse(lastDelivery['lng'] ?? '') ?? 0.0;
            if (lastLat != 0 && lastLng != 0) {
              // Try Google Maps route distance first
              final routeKm = await _getRouteDistanceKm(
                lastLat,
                lastLng,
                riderLat,
                riderLng,
              );
              if (routeKm != null && routeKm > 0) {
                calculatedKms = routeKm;
                debugPrint(
                  '[SKIPPED] ✅ Route distance (Last Delivery): ${calculatedKms.toStringAsFixed(4)} km',
                );
              } else {
                // Fallback to straight-line if API fails
                final distanceMeters = Geolocator.distanceBetween(
                  lastLat,
                  lastLng,
                  riderLat,
                  riderLng,
                );
                calculatedKms = distanceMeters / 1000.0;
                debugPrint(
                  '[SKIPPED] ⚠️ Fallback straight-line (Last Delivery): ${calculatedKms.toStringAsFixed(4)} km',
                );
              }
            }
          }
        }
      } catch (e) {
        debugPrint('[SKIPPED] Error calculating distance: $e');
      }

      if (calculatedKms > 0) {
        payload['riderkms'] = calculatedKms.toStringAsFixed(2);
        debugPrint('[SKIPPED] Calculated riderkms: ${payload['riderkms']}');
      }

      final url = _resolveUpdateUrl();
      final resp = await _updateProvider.updateDelivery(payload, url);
      final ok = _isSuccess(resp);

      if (ok) {
        // Update last delivery location to current location so next order starts here
        final actualLat = ll['lat'] ?? '0';
        final actualLng = ll['lng'] ?? '0';
        if (actualLat != '0' && actualLng != '0') {
          await _saveLastDeliveryLocation(actualLat, actualLng);
        }
      }

      if (!ok) {
        debugPrint('[UPDATE][SKIPPED][FAILED] resp=${jsonEncode(resp)}');
      }
      return ok;
    } catch (e) {
      debugPrint('[UPDATE][SKIPPED][ERROR] $e');
      return false;
    }
  }

  /// Check if there are active deliveries
  /// Returns true ONLY if has_live_deliveries is true (verified by API)
  /// This ensures PiP is only enabled when there are actually active deliveries
  Future<bool> hasActiveDeliveries() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final hasLive = prefs.getBool('has_live_deliveries') ?? false;

      // ✅ CRITICAL: Only return true if has_live_deliveries is true
      // Don't rely on active_delivery_order_id alone - it may be stale
      // The API sets has_live_deliveries based on actual order status = "active"
      if (!hasLive) {
        // Clear stale active_delivery_order_id if has_live_deliveries is false
        final activeOrderId = prefs.getString('active_delivery_order_id');
        if (activeOrderId != null && activeOrderId.isNotEmpty) {
          debugPrint(
            '[DELIVERIES_CONTROLLER] Clearing stale active_delivery_order_id: $activeOrderId (no active deliveries)',
          );
          await prefs.remove('active_delivery_order_id');
        }
      }

      debugPrint(
        '[DELIVERIES_CONTROLLER] hasActiveDeliveries: $hasLive (has_live_deliveries from API)',
      );
      return hasLive;
    } catch (e) {
      debugPrint(
        '[DELIVERIES_CONTROLLER] Error checking active deliveries: $e',
      );
      return false;
    }
  }

  // ---------------- Skip Penalty Logic ----------------

  /// Checks the current skip count and penalty status within the 3-hour window.
  /// Resets the window if 3 hours have passed since the first skip.
  Future<Map<String, dynamic>> checkSkipStatus() async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final now = DateTime.now();
      final startStr = prefs.getString('skip_window_start');
      int count = prefs.getInt('skip_count') ?? 0;
      bool penalty = prefs.getBool('skip_penalty_active') ?? false;

      if (startStr != null) {
        final start = DateTime.tryParse(startStr);
        if (start != null) {
          final diff = now.difference(start);
          if (diff.inHours >= 3) {
            debugPrint('[SKIP_LOGIC] 3-hour window expired (elapsed: ${diff.inMinutes}m). Resetting skips.');
            // Reset window
            count = 0;
            penalty = false;
            await prefs.remove('skip_window_start');
            await prefs.remove('skip_count');
            await prefs.remove('skip_penalty_active');
          }
        } else {
           // Invalid date string, reset
           await prefs.remove('skip_window_start');
        }
      }

      return {'count': count, 'penalty': penalty};
    } catch (e) {
      debugPrint('[SKIP_LOGIC] Error checking status: $e');
      return {'count': 0, 'penalty': false};
    }
  }

  /// Registers a skip action.
  /// Increments skip count and sets penalty if specified.
  /// Starts 3-hour window if not already active.
  Future<void> registerSkip({bool applyPenalty = false}) async {
    try {
      final prefs = await SharedPreferences.getInstance();
      final now = DateTime.now();
      
      // Ensure we are working with fresh/current state
      await checkSkipStatus(); 

      // Re-fetch after potential reset
      int count = prefs.getInt('skip_count') ?? 0;
      String? startStr = prefs.getString('skip_window_start');

      if (startStr == null) {
        // Start new window
        await prefs.setString('skip_window_start', now.toIso8601String());
        debugPrint('[SKIP_LOGIC] Starting new 3-hour skip window.');
      }

      count++;
      await prefs.setInt('skip_count', count);
      
      if (applyPenalty) {
        await prefs.setBool('skip_penalty_active', true);
        debugPrint('[SKIP_LOGIC] Penalty activated for this session.');
      }

      debugPrint('[SKIP_LOGIC] Skip registered. Count: $count, Penalty: $applyPenalty');
    } catch (e) {
      debugPrint('[SKIP_LOGIC] Error registering skip: $e');
    }
  }
}

