import 'dart:async';
import 'dart:convert';
import 'dart:ui';
import 'dart:math' show cos, max, min, pi, pow, sin, atan2;
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import 'package:http/http.dart' as http;
import 'package:intl/intl.dart';
import '../../../constant/api_key.dart';
import '../../../services/offline_map_service.dart';
import '../../../models/model/painter_copoun.dart';
import '../../../views/widgets/mycircular.dart';
import '../deep_link_controller.dart';

import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../constant/colors.dart';
import '../../../constant/style.dart';
import '../../../constant/country_polygons.dart';
import '../../../env/env.dart';
import '../../../main.dart'; // contains global 'box', 'sql'
import '../../../print.dart';
import '../../../services/pip_service.dart';
import '../../../services/ride_live_notification.dart';
import '../../../views/home/map_page_passenger.dart';
import '../../../views/Rate/rate_captain.dart';
import '../../../views/Rate/rating_driver_bottom.dart';
import '../../../views/widgets/mydialoug.dart';
import '../../../views/widgets/elevated_btn.dart';
import '../../../views/home/map_widget.dart/car_details_widget_to_go.dart';
import '../../../views/home/map_widget.dart/select_driver_mishwari.dart';
import '../../functions/crud.dart';
import '../../functions/launch.dart';
import '../../payment/payment_controller.dart';
import '../points_for_rider_controller.dart';
import 'map_engine_controller.dart';
import 'location_search_controller.dart';
import 'nearby_drivers_controller.dart';
import 'ui_interactions_controller.dart';
import 'map_socket_controller.dart';
import '../decode_polyline_isolate.dart';
import '../ios_live_activity_service.dart';
import '../../firebase/local_notification.dart';
import '../../firebase/notification_service.dart';
import '../../functions/audio_record1.dart';
import '../../functions/package_info.dart';
import '../../functions/secure_storage.dart';
import '../vip_waitting_page.dart';
import '../device_performance.dart';
import 'ride_state.dart';
import '../../../views/widgets/error_snakbar.dart';
import 'package:flutter_confetti/flutter_confetti.dart' hide Circle;
import 'package:crypto/crypto.dart';

class RideLifecycleController extends GetxController {
  // --- Missing variables from monolithic controller ---
  String currentRideId = '';
  bool isDrawingRoute = false;
  bool isAnotherOreder = false;
  bool isWhatsAppOrder = false;
  LatLng startLocation = const LatLng(32, 35);
  LatLng endLocation = const LatLng(32, 35);
  String dynamicApiUrl = 'https://routec.intaleq.xyz/route';
  String? cardNumber;
  bool isBeginRideFromDriverRunning = false;
  bool isDriversTokensSend = false;
  Map<String, dynamic> rideData = {};
  Map<String, dynamic> dInfo = {};
  List<LatLng> datadriverCarsLocationToPassengerAfterApplied = [];
  double distanceOfTrip = 0.0;
  double apiDistanceMeters = 0.0;
  double tax = 0.0;
  int selectedPassengerCount = 1;
  final GlobalKey<FormState> increaseFeeFormKey = GlobalKey<FormState>();
  final GlobalKey<FormState> messagesFormKey = GlobalKey<FormState>();
  final GlobalKey<FormState> promoFormKey = GlobalKey<FormState>();
  String walletStr = '0';
  double walletVal = 0.0;
  bool rideConfirm = false;
  LatLng driverLocationToPassenger = const LatLng(32, 35);
  final TextEditingController messageToDriver = TextEditingController();
  int carsOrder = 0;

  Rx<RideState> currentRideState = RideState.noRide.obs;
  String statusRide = 'wait';
  String statusRideVip = 'wait';
  bool statusRideFromStart = false;

  double distance = 0;
  double duration = 0;
  int durationToRide = 0;
  int remainingTime = 25;
  int remainingTimeToPassengerFromDriverAfterApplied = 60;
  int remainingTimeDriverWaitPassenger5Minute = 60;
  int timeToPassengerFromDriverAfterApplied = 0;
  Timer? timerToPassengerFromDriverAfterApplied;
  DateTime? _driverEtaUpdatedAt;
  int _driverEtaSecondsAtUpdate = 0;
  int _driverEtaCountdownTicks = 0;

  bool rideTimerBegin = false;
  double progressTimerRideBegin = 0;
  int remainingTimeTimerRideBegin = 60;
  String stringRemainingTimeRideBegin = '';

  late String rideId = 'yet';
  late String driverId = '';
  late String make = '';
  late String model = '';
  late String gender = '';
  late String carColor = '';
  late String licensePlate = '';
  late String driverName = '';
  late String passengerName = '';
  late String driverPhone = '';
  late String colorHex = '';
  late String carYear = '';
  late String driverRate = '5.0';
  late String driverRatingCount = '0';
  late String driverCompletedRides = '0';
  late String driverTier = 'Verified driver';
  late String driverToken = '';
  String totalPassenger = '0';
  double totalDriver = 0;
  double costDistance = 0;
  double costDuration = 0;
  double averageDuration = 0;
  String totalCostPassenger = '0';

  String totalPassengerSpeed = '0';
  String totalPassengerBalash = '0';
  String totalPassengerComfort = '0';
  String totalPassengerElectric = '0';
  String totalPassengerLady = '0';
  String totalPassengerScooter = '0';
  String totalPassengerVan = '0';
  String totalPassengerRayehGai = '0';
  String totalPassengerRayehGaiComfort = '0';
  String totalPassengerRayehGaiBalash = '0';

  double latePrice = 0;
  double fuelPrice = 0;
  double heavyPrice = 0;
  double naturePrice = 0;

  bool isRideFinished = false;
  String stringRemainingTimeToPassenger = '';
  String stringRemainingTimeDriverWaitPassenger5Minute = '';

  bool isDriverInPassengerWay = false;
  bool isDriverArrivePassenger = false;
  bool isSearchingWindow = false;
  bool shouldFetch = true;

  double progressTimerToPassengerFromDriverAfterApplied = 0;
  double progressTimerDriverWaitPassenger5Minute = 0;
  bool isCashSelectedBeforeConfirmRide = false;
  bool isPassengerChosen = false;

  Timer? _masterTimer;
  Timer? _searchTimer;
  Timer? _timer;
  Timer? _uiCountdownTimer;

  bool _isArrivalProcessed = false;
  bool _isFinishProcessed = false;
  bool _isCancelProcessed = false;
  bool _isAcceptanceProcessed = false;
  bool _isRatingScreenOpen = false;
  bool _isReviewProcessed = false; // 🛡️ Gatekeeper لمنع فتح التقييم مرتين
  bool _isRecalculatingRoute = false;

  String _rideAcceptedViaSource = "Unknown";

  final double kDurationScalar = 1.5348;
  // مسافة الانحراف المسموح بها بالمتر قبل إعادة حساب المسار تلقائيًا للرحلة.
  // إذا انحرف السائق عن المسار بأكثر من هذه المسافة، يُعاد حساب المسار.
  final double _deviationThresholdMeters = 30.0;
  int _routeHeadingMismatchCount = 0;

  final Map<RideState, int> _pollingIntervals = {
    RideState.noRide: 6,
    RideState.searching: 8,
    RideState.driverApplied: 10,
    RideState.driverArrived: 15,
    RideState.inProgress: 15,
    RideState.cancelled: 3600,
    RideState.finished: 3600,
    RideState.preCheckReview: 3600,
  };

  Timer? _locationPollingTimer;
  List<LatLng> _currentDriverRoutePoints = [];
  double _currentDriverRouteDistanceMeters = 0.0;
  int _currentDriverRouteDurationSeconds = 0;

  int _currentSearchPhase = 0;
  bool _isFetchingDriverLocation = false;
  Timer? _watchdogTimer;

  final List<int> _searchRadii = [2400, 3000, 3100];
  final int _searchPhaseDurationSeconds = 30;
  final int _totalSearchTimeoutSeconds = 90;

  int _noRideSearchCount = 0;
  final int _noRideMaxTries = 3;
  final int _noRideIntervalSec = 5;
  DateTime? _noRideNextAllowed;
  bool _noRideSearchCapped = false;
  int _masterIntervalSeconds = -1;

  final StreamController<String> _rideStatusStreamController =
      StreamController<String>.broadcast();
  Stream<String> get rideStatusStream => _rideStatusStreamController.stream;

  final StreamController<String> _beginRideStreamController =
      StreamController<String>.broadcast();
  Stream<String> get beginRideStream => _beginRideStreamController.stream;

  final StreamController<int> _timerStreamController = StreamController<int>();
  Stream<int> get timerStream => _timerStreamController.stream;

  bool isTimerFromDriverToPassengerAfterAppliedRunning = true;
  bool isTimerRunning = false;
  int beginRideInterval = 10;

  Timer? _rideProgressTimer;
  bool _hasShownSpeedWarning = false;
  bool rideInProgress = true;
  double elapsedTimeInSeconds = 0;
  String stringElapsedTimeRideBeginVip = '0:00';

  Map rideStatusFromStartApp = {};
  bool isStartAppHasRide = false;
  late Duration durationToAdd;
  late DateTime newTime = DateTime.now();
  String durationByPassenger = '';
  int hours = 0;
  int minutes = 0;

  int selectedReason = -1;
  String? cancelNote;
  double latitudeWhatsApp = 0;
  double longitudeWhatsApp = 0;

  // Getters for linked controllers
  LocationSearchController get locSearch =>
      Get.find<LocationSearchController>();
  MapEngineController get mapEngine => Get.find<MapEngineController>();
  NearbyDriversController get nearbyDrivers =>
      Get.find<NearbyDriversController>();
  MapSocketController get mapSocket => Get.find<MapSocketController>();
  UiInteractionsController get uiInteractions =>
      Get.find<UiInteractionsController>();

  // LocationSearchController pass-throughs
  LatLng get passengerLocation => locSearch.passengerLocation;
  set passengerLocation(LatLng val) => locSearch.passengerLocation = val;

  LatLng get newMyLocation => locSearch.newMyLocation;
  set newMyLocation(LatLng val) => locSearch.newMyLocation = val;

  LatLng get newStartPointLocation => locSearch.newStartPointLocation;
  set newStartPointLocation(LatLng val) =>
      locSearch.newStartPointLocation = val;

  LatLng get myDestination => locSearch.myDestination;
  set myDestination(LatLng val) => locSearch.myDestination = val;

  String get startNameAddress => locSearch.startNameAddress;
  set startNameAddress(String val) => locSearch.startNameAddress = val;

  String get endNameAddress => locSearch.endNameAddress;
  set endNameAddress(String val) => locSearch.endNameAddress = val;

  List<String> get placesCoordinate => locSearch.placesCoordinate;
  set placesCoordinate(List<String> val) => locSearch.placesCoordinate = val;

  int get activeMenuWaypointCount => locSearch.activeMenuWaypointCount;
  set activeMenuWaypointCount(int val) =>
      locSearch.activeMenuWaypointCount = val;

  List<LatLng?> get menuWaypoints => locSearch.menuWaypoints;
  set menuWaypoints(List<LatLng?> val) => locSearch.menuWaypoints = val;

  List<String> get menuWaypointNames => locSearch.menuWaypointNames;
  set menuWaypointNames(List<String> val) => locSearch.menuWaypointNames = val;

  bool get passengerStartLocationFromMap =>
      locSearch.passengerStartLocationFromMap;
  set passengerStartLocationFromMap(bool val) =>
      locSearch.passengerStartLocationFromMap = val;

  List get coordinatesWithoutEmpty => locSearch.coordinatesWithoutEmpty;

  // MapEngineController pass-throughs
  Set<Marker> get markers => mapEngine.markers;
  set markers(Set<Marker> val) {
    mapEngine.markers = val;
    mapEngine.update();
  }

  Set<Polyline> get polyLines => mapEngine.polyLines;
  set polyLines(Set<Polyline> val) {
    mapEngine.polyLines = val;
    mapEngine.update();
  }

  IntaleqMapController? get mapController => mapEngine.mapController;

  bool get isStyleLoaded => mapEngine.isStyleLoaded;
  set isStyleLoaded(bool val) => mapEngine.isStyleLoaded = val;

  bool get isBottomSheetShown => mapEngine.isBottomSheetShown;
  set isBottomSheetShown(bool val) => mapEngine.isBottomSheetShown = val;

  double get heightBottomSheetShown => mapEngine.heightBottomSheetShown;
  set heightBottomSheetShown(double val) =>
      mapEngine.heightBottomSheetShown = val;

  bool get isPickerShown => mapEngine.isPickerShown;
  set isPickerShown(bool val) => mapEngine.isPickerShown = val;

  bool get isMarkersShown => mapEngine.isMarkersShown;
  set isMarkersShown(bool val) => mapEngine.isMarkersShown = val;

  bool get isMainBottomMenuMap => mapEngine.isMainBottomMenuMap;
  set isMainBottomMenuMap(bool val) => mapEngine.isMainBottomMenuMap = val;

  double get mainBottomMenuMapHeight => mapEngine.mainBottomMenuMapHeight;
  set mainBottomMenuMapHeight(double val) =>
      mapEngine.mainBottomMenuMapHeight = val;

  bool get isWayPointSheet => mapEngine.isWayPointSheet;
  set isWayPointSheet(bool val) => mapEngine.isWayPointSheet = val;

  bool get isWayPointStopsSheet => mapEngine.isWayPointStopsSheet;
  set isWayPointStopsSheet(bool val) => mapEngine.isWayPointStopsSheet = val;

  bool get isWayPointStopsSheetUtilGetMap =>
      mapEngine.isWayPointStopsSheetUtilGetMap;
  set isWayPointStopsSheetUtilGetMap(bool val) =>
      mapEngine.isWayPointStopsSheetUtilGetMap = val;

  double get wayPointSheetHeight => mapEngine.wayPointSheetHeight;
  set wayPointSheetHeight(double val) => mapEngine.wayPointSheetHeight = val;

  double get cashConfirmPageShown => mapEngine.cashConfirmPageShown;
  set cashConfirmPageShown(double val) => mapEngine.cashConfirmPageShown = val;

  bool get isCashConfirmPageShown => mapEngine.isCashConfirmPageShown;
  set isCashConfirmPageShown(bool val) =>
      mapEngine.isCashConfirmPageShown = val;

  bool get isCancelRidePageShown => mapEngine.isCancelRidePageShown;
  set isCancelRidePageShown(bool val) => mapEngine.isCancelRidePageShown = val;

  void changeCashConfirmPageShown() => mapEngine.changeCashConfirmPageShown();

  void resetNoRideSearch() {
    _noRideSearchCount = 0;
    _noRideSearchCapped = false;
    _noRideNextAllowed = null;
  }

  double get paymentPageShown => mapEngine.paymentPageShown;
  set paymentPageShown(double val) => mapEngine.paymentPageShown = val;

  void changeCancelRidePageShow() => mapEngine.changeCancelRidePageShow();

  // NearbyDriversController pass-throughs
  List get carsLocationByPassenger => nearbyDrivers.carsLocationByPassenger;
  set carsLocationByPassenger(List val) =>
      nearbyDrivers.carsLocationByPassenger = val;

  List<LatLng> get driverCarsLocationToPassengerAfterApplied =>
      nearbyDrivers.driverCarsLocationToPassengerAfterApplied;
  set driverCarsLocationToPassengerAfterApplied(List<LatLng> val) =>
      nearbyDrivers.driverCarsLocationToPassengerAfterApplied = val;

  bool get noCarString => nearbyDrivers.noCarString;
  set noCarString(bool val) => nearbyDrivers.noCarString = val;

  double get speed => locSearch.speed;
  set speed(double val) => locSearch.speed = val;

  Timer? get locationPollingTimer => _locationPollingTimer;

  bool isActiveRideState() {
    return currentRideState.value == RideState.searching ||
        currentRideState.value == RideState.driverApplied ||
        currentRideState.value == RideState.driverArrived ||
        currentRideState.value == RideState.inProgress;
  }

  void startMasterTimer() {
    _masterTimer?.cancel();
    _masterTimer = Timer.periodic(const Duration(seconds: 13), (_) {
      _handleRideState(currentRideState.value);
    });
  }

  void cancelMasterTimer() {
    _masterTimer?.cancel();
    _masterTimer = null;
  }

  void startMasterTimerWithInterval(int seconds) {
    if (_masterTimer != null && _masterIntervalSeconds == seconds) return;
    _masterIntervalSeconds = seconds;
    _masterTimer?.cancel();
    _masterTimer = Timer.periodic(Duration(seconds: seconds), (_) {
      _handleRideState(currentRideState.value);
    });
  }

  void stopAllTimers() {
    Log.print('🛑 FORCE STOP: Stopping ALL Timers and Streams 🛑');
    _masterTimer?.cancel();
    _masterTimer = null;
    timerToPassengerFromDriverAfterApplied?.cancel();
    timerToPassengerFromDriverAfterApplied = null;
    _timer?.cancel();
    _timer = null;
    _uiCountdownTimer?.cancel();
    _uiCountdownTimer = null;
    _locationPollingTimer?.cancel();
    _locationPollingTimer = null;
    _watchdogTimer?.cancel();
    _watchdogTimer = null;
    _searchTimer?.cancel();
    _searchTimer = null;
    _rideProgressTimer?.cancel();
    _rideProgressTimer = null;

    isTimerRunning = false;
    isBeginRideFromDriverRunning = false;
    _isFetchingDriverLocation = false;
    update();
  }

  Future<void> _handleRideState(RideState state) async {
    if (_isRatingScreenOpen) {
      Log.print('⛔ Rating Screen is Open. Skipping Logic.');
      stopAllTimers();
      return;
    }
    Log.print('Handling state: $state');

    int effectivePollingInterval = _pollingIntervals[state] ?? 13;

    switch (state) {
      case RideState.noRide:
        final now = DateTime.now();
        if (_noRideSearchCount >= _noRideMaxTries) {
          if (!_noRideSearchCapped) {
            _noRideSearchCapped = true;
            Log.print('[noRide] search capped at $_noRideMaxTries attempts');
          }
          break;
        }
        if (_noRideNextAllowed != null && now.isBefore(_noRideNextAllowed!)) {
          break;
        }
        _noRideSearchCount++;
        Log.print('_noRideSearchCount: $_noRideSearchCount');
        _noRideNextAllowed = now.add(Duration(seconds: _noRideIntervalSec));
        nearbyDrivers.getCarsLocationByPassengerAndReloadMarker();
        nearbyDrivers.getNearestDriverByPassengerLocation();
        break;

      case RideState.cancelled:
        stopAllTimers();
        break;

      case RideState.preCheckReview:
        stopAllTimers();
        _checkLastRideForReview();
        break;

      case RideState.searching:
        if (rideId == 'yet' || rideId.isEmpty) break;
        try {
          String statusFromServer = await getRideStatus(rideId);
          if (statusFromServer == 'Apply' || statusFromServer == 'Applied') {
            await processRideAcceptance(source: "Polling");
            break;
          }
        } catch (e) {
          Log.print('Error polling getRideStatus: $e');
        }

        final now = DateTime.now();
        final int elapsedSeconds = now.difference(_searchStartTime!).inSeconds;

        if (elapsedSeconds > _totalSearchTimeoutSeconds) {
          stopAllTimers();
          currentRideState.value = RideState.noRide;
          isSearchingWindow = false;
          update();
          _showIncreaseFeeDialog();
          break;
        }

        int targetPhase =
            (elapsedSeconds / _searchPhaseDurationSeconds).floor();
        if (targetPhase >= _searchRadii.length) {
          targetPhase = _searchRadii.length - 1;
        }

        bool isNewPhase = targetPhase > _currentSearchPhase;
        bool timeToScanForNewDrivers = (elapsedSeconds % 15 == 0);

        if (isNewPhase || timeToScanForNewDrivers || elapsedSeconds < 5) {
          _currentSearchPhase = targetPhase;
          int currentRadius = _searchRadii[_currentSearchPhase];
          Log.print(
              '[Search Logic] Scanning for drivers. Phase: $_currentSearchPhase, Radius: $currentRadius');
        }

        if (elapsedSeconds < 5) {
          driversStatusForSearchWindow = 'Your order is being prepared'.tr;
        } else if (elapsedSeconds < 15) {
          driversStatusForSearchWindow = 'Your order sent to drivers'.tr;
        } else {
          driversStatusForSearchWindow =
              'The drivers are reviewing your request'.tr;
        }
        update();
        break;

      case RideState.driverApplied:
        if (!_isDriverAppliedLogicExecuted && !_isAcceptanceProcessed) {
          Log.print('[handleRideState] Execution driverApplied logic.');
          rideAppliedFromDriver(true);
          _isDriverAppliedLogicExecuted = true;
        }

        if (!mapSocket.isSocketConnected) {
          try {
            String statusFromServer = await getRideStatus(rideId);
            if (statusFromServer == 'Arrived') {
              currentRideState.value = RideState.driverArrived;
              break;
            } else if (statusFromServer == 'Begin' ||
                statusFromServer == 'inProgress') {
              processRideBegin();
              break;
            }
          } catch (e) {
            Log.print('Error polling for Arrived/Begin status: $e');
          }
        }
        if (!_isSocketHealthy()) {
          getDriverCarsLocationToPassengerAfterApplied();
        }
        break;

      case RideState.driverArrived:
        if (!_isDriverArrivedLogicExecuted) {
          _isDriverArrivedLogicExecuted = true;
          startTimerDriverWaitPassenger5Minute();
          uiInteractions.driverArrivePassengerDialoge();
        }
        break;

      case RideState.inProgress:
        if (!mapSocket.isSocketConnected) {
          try {
            String statusFromServer = await getRideStatus(rideId);
            if (statusFromServer == 'Finished' ||
                statusFromServer == 'finished') {
              Log.print(
                  '🏁 DETECTED FINISHED: Killing processes and forcing Review.');
              stopAllTimers();
              currentRideState.value = RideState.preCheckReview;
              tripFinishedFromDriver();
              _checkLastRideForReview();
              return;
            }
          } catch (e) {
            Log.print('Error polling status: $e');
          }
        }

        if (!_isRideBeginLogicExecuted) {
          _isRideBeginLogicExecuted = true;
          _executeBeginRideLogic();
        }
        if (!_isSocketHealthy()) {
          getDriverCarsLocationToPassengerAfterApplied();
        }
        break;

      case RideState.finished:
        tripFinishedFromDriver();
        stopAllTimers();
        effectivePollingInterval = 3600;
        break;
    }
    startMasterTimerWithInterval(effectivePollingInterval);
  }

  bool _isSocketHealthy() {
    return mapSocket.isSocketHealthy();
  }

  Future<void> _checkInitialRideStatus() async {
    await getRideStatusFromStartApp();
    if (rideStatusFromStartApp['data'] == null) {
      currentRideState.value = RideState.noRide;
      _handleRideState(currentRideState.value);
      return;
    }
    String _status = rideStatusFromStartApp['data']['status'] ?? '';
    String _lowerStatus = _status.toLowerCase();

    if (_lowerStatus == 'waiting' ||
        _lowerStatus == 'apply' ||
        _lowerStatus == 'applied' ||
        _lowerStatus == 'accepted' ||
        _lowerStatus == 'arrived' ||
        _lowerStatus == 'begin') {
      rideId = rideStatusFromStartApp['data']['rideId'].toString();
      currentRideState.value = _lowerStatus == 'waiting'
          ? RideState.searching
          : (_lowerStatus == 'apply' ||
                  _lowerStatus == 'applied' ||
                  _lowerStatus == 'accepted')
              ? RideState.driverApplied
              : _lowerStatus == 'arrived'
                  ? RideState.driverArrived
                  : _lowerStatus == 'begin'
                      ? RideState.inProgress
                      : _lowerStatus == 'cancel'
                          ? RideState.cancelled
                          : RideState.noRide;
    } else if (_lowerStatus == 'finished') {
      if (rideStatusFromStartApp['data']['needsReview'] == 1) {
        currentRideState.value = RideState.preCheckReview;
      } else {
        currentRideState.value = RideState.noRide;
      }
    } else {
      currentRideState.value = RideState.noRide;
    }
    _handleRideState(currentRideState.value);
  }

  Future<void> _checkLastRideForReview() async {
    // 🛡️ Gatekeeper: منع فتح التقييم مرتين (Race Condition)
    if (_isReviewProcessed) {
      Log.print("✋ _checkLastRideForReview: Already processed. Skipping.");
      return;
    }
    _isReviewProcessed = true;

    Log.print('⭐ FORCE OPEN RATING PAGE (Get.to mode)');
    await getRideStatusFromStartApp();

    if (rideStatusFromStartApp['data'] == null) {
      _isReviewProcessed = false;
      currentRideState.value = RideState.noRide;
      startMasterTimer();
      return;
    }

    String needsReview =
        rideStatusFromStartApp['data']['needsReview'].toString();

    if (needsReview == '1') {
      _isRatingScreenOpen = true;
      var args = {
        'driverId': rideStatusFromStartApp['data']['driver_id'].toString(),
        'rideId': rideStatusFromStartApp['data']['rideId'].toString(),
        'driverName': rideStatusFromStartApp['data']['driverName'],
        'price': rideStatusFromStartApp['data']['price'],
      };

      await Get.to(
        () => RatingDriverBottomSheet(),
        arguments: args,
        preventDuplicates: true,
        popGesture: false,
      );

      Log.print('✅ Rating Page Closed. Resetting App.');
      _isRatingScreenOpen = false;
      restCounter();
      currentRideState.value = RideState.noRide;
      startMasterTimer();
    } else {
      currentRideState.value = RideState.noRide;
      startMasterTimer();
    }
  }

  DateTime? _searchStartTime;
  bool _isDriverAppliedLogicExecuted = false;
  bool _isDriverArrivedLogicExecuted = false;
  bool _isRideBeginLogicExecuted = false;
  String driversStatusForSearchWindow = '';

  void startSearchingForDriver() async {
    if (currentRideState.value == RideState.searching) return;

    isSearchingWindow = true;
    currentRideState.value = RideState.searching;
    driversStatusForSearchWindow = 'Searching for nearby drivers...'.tr;
    _searchStartTime = DateTime.now();
    _currentSearchPhase = 0;
    update();

    bool rideCreated = await postRideDetailsToServer();

    if (!rideCreated) {
      isSearchingWindow = false;
      currentRideState.value = RideState.noRide;
      mySnackbarWarning("Could not create ride. Please try again.".tr);
      update();
      return;
    }

    _addRideToWaitingTable();
    mapSocket.initConnectionWithSocket();
  }

  void _showIncreaseFeeDialog() {
    Get.dialog(
      CupertinoAlertDialog(
        title: Text("No drivers accepted your request yet".tr),
        content: Text(
            "Increasing the fare might attract more drivers. Would you like to increase the price?"
                .tr),
        actions: [
          CupertinoDialogAction(
            child: Text("Cancel Ride".tr,
                style: TextStyle(color: AppColor.redColor)),
            onPressed: () {
              Get.back();
              mapEngine.changeCancelRidePageShow();
            },
          ),
          CupertinoDialogAction(
            child: Text("Increase Fare".tr,
                style: TextStyle(color: AppColor.greenColor)),
            onPressed: () {
              Get.back();
              double newPrice = double.parse(totalPassenger) * 1.10;
              increasePriceAndRestartSearch(newPrice);
            },
          ),
        ],
      ),
      barrierDismissible: false,
    );
  }

  Future<void> increasePriceAndRestartSearch(double newPrice) async {
    totalPassenger = newPrice.toStringAsFixed(2);
    update();

    await CRUD()
        .post(link: "${AppLink.server}/ride/rides/update.php", payload: {
      "id": rideId,
      "price": newPrice.toStringAsFixed(2),
    });

    Log.print(
        '[increasePrice] Price changed. Clearing notified list to resend.');
    notifiedDrivers.clear();

    _searchStartTime = DateTime.now();
    _currentSearchPhase = 0;
    isSearchingWindow = true;
    update();
    startMasterTimer();
  }

  void _stopWaitPassengerTimer({bool resetUI = false}) {
    _waitPassengerTimer?.cancel();
    _waitPassengerTimer = null;

    if (resetUI) {
      progressTimerDriverWaitPassenger5Minute = 0.0;
      remainingTimeDriverWaitPassenger5Minute = 0;
      stringRemainingTimeDriverWaitPassenger5Minute = '00:00';
      update();
    }
  }

  void _executeBeginRideLogic() {
    Log.print('[executeBeginRideLogic] execution of ride start logic...');
    _stopWaitPassengerTimer(resetUI: true);

    timeToPassengerFromDriverAfterApplied = 0;
    remainingTime = 0;
    remainingTimeToPassengerFromDriverAfterApplied = 0;
    remainingTimeDriverWaitPassenger5Minute = 0;

    rideTimerBegin = true;
    statusRide = 'Begin';
    isDriverInPassengerWay = false;
    isDriverArrivePassenger = false;

    box.write(BoxName.passengerWalletTotal, '0');
    update();

    rideIsBeginPassengerTimer();
    runWhenRideIsBegin();

    NotificationController().showNotification(
        'Trip is Begin'.tr,
        'The trip has started! Feel free to contact emergency numbers, share your trip, or activate voice recording for the journey'
            .tr,
        'start');
  }

  Future<void> processRideBegin({String source = "Unknown"}) async {
    if (currentRideState.value == RideState.inProgress ||
        _isRideStartedProcessed) {
      return;
    }
    _isRideStartedProcessed = true;
    currentRideState.value = RideState.inProgress;
    statusRide = 'Begin';
    box.write(BoxName.passengerWalletTotal, '0');

    remainingTimeDriverWaitPassenger5Minute = 0;
    _stopWaitPassengerTimer();

    // مسح الخطوط القديمة (pickup/direct) قبل رسم خط المرحلة الجديدة
    polyLines = polyLines
        .where((p) =>
            p.polylineId.value != 'main_route' &&
            p.polylineId.value != 'route_direct' &&
            !p.polylineId.value.startsWith('driver_route'))
        .toSet();

    // موقع السائق الحالي من آخر تحديث
    LatLng driverPos = passengerLocation;
    if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) {
      driverPos = driverCarsLocationToPassengerAfterApplied.last;
    }

    // رسم المسار من موقع السائق إلى الهدف بخط أزرق مستمر
    await calculateDriverToPassengerRoute(driverPos, myDestination,
        isBeginPhase: true);

    rideIsBeginPassengerTimer();
    runWhenRideIsBegin();
    _updatePassengerWalkLine();
    update();
  }

  bool _isRideStartedProcessed = false;

  void updateDriverRouteMetrics({int? etaSeconds, double? distanceMeters}) {
    if (distanceMeters != null && distanceMeters > 0) {
      distanceByPassenger = distanceMeters.toStringAsFixed(0);
    }

    if (etaSeconds == null) return;

    final int clampedEta = max(0, etaSeconds);
    timeToPassengerFromDriverAfterApplied = clampedEta;
    remainingTimeToPassengerFromDriverAfterApplied = clampedEta;
    durationToPassenger = clampedEta;
    _driverEtaSecondsAtUpdate = clampedEta;
    _driverEtaUpdatedAt = DateTime.now();

    final int minutes = (clampedEta / 60).floor();
    final int seconds = clampedEta % 60;
    stringRemainingTimeToPassenger =
        '$minutes:${seconds.toString().padLeft(2, '0')}';
  }

  void startTimerFromDriverToPassengerAfterApplied() {
    stopTimerFromDriverToPassengerAfterApplied();
    if (isTimerRunning) return;
    isTimerRunning = true;
    isTimerFromDriverToPassengerAfterAppliedRunning = true;
    _driverEtaUpdatedAt ??= DateTime.now();
    _driverEtaSecondsAtUpdate = timeToPassengerFromDriverAfterApplied;
    _driverEtaCountdownTicks = 0;

    timerToPassengerFromDriverAfterApplied =
        Timer.periodic(const Duration(seconds: 1), (timer) {
      bool isRideActive = (statusRide == 'Apply' ||
          statusRide == 'Arrived' ||
          statusRide == 'Begin' ||
          currentRideState.value == RideState.driverApplied ||
          currentRideState.value == RideState.driverArrived ||
          currentRideState.value == RideState.inProgress);

      if (!isRideActive || !isTimerFromDriverToPassengerAfterAppliedRunning) {
        timer.cancel();
        timerToPassengerFromDriverAfterApplied = null;
        isTimerRunning = false;
        return;
      }

      _driverEtaCountdownTicks++;
      if (!_timerStreamController.isClosed) {
        _timerStreamController.add(_driverEtaCountdownTicks);
      }

      final int secondsElapsedSinceEta = _driverEtaUpdatedAt == null
          ? 0
          : DateTime.now().difference(_driverEtaUpdatedAt!).inSeconds;
      remainingTimeToPassengerFromDriverAfterApplied =
          _driverEtaSecondsAtUpdate - secondsElapsedSinceEta;

      if (remainingTimeToPassengerFromDriverAfterApplied < 0) {
        remainingTimeToPassengerFromDriverAfterApplied = 0;
      }

      int minutes =
          (remainingTimeToPassengerFromDriverAfterApplied / 60).floor();
      int seconds = remainingTimeToPassengerFromDriverAfterApplied % 60;
      stringRemainingTimeToPassenger =
          '$minutes:${seconds.toString().padLeft(2, '0')}';

      if (_driverEtaCountdownTicks % 5 == 0) {
        double currentProgress = 1 -
            (remainingTimeToPassengerFromDriverAfterApplied /
                (_driverEtaSecondsAtUpdate == 0
                    ? 1
                    : _driverEtaSecondsAtUpdate));

        IosLiveActivityService.updateRideActivity(
          status: 'waiting',
          driverName: driverName,
          carDetails: '$make • $model • $carColor',
          etaText: stringRemainingTimeToPassenger,
          progress: currentProgress.clamp(0.0, 1.0),
        );
      }

      if (_driverEtaCountdownTicks % beginRideInterval == 0) {
        uploadPassengerLocation();
      } else {
        update();
      }
    });
  }

  void stopTimerFromDriverToPassengerAfterApplied() {
    isTimerFromDriverToPassengerAfterAppliedRunning = false;
    timerToPassengerFromDriverAfterApplied?.cancel();
    timerToPassengerFromDriverAfterApplied = null;
    isTimerRunning = false;
    update();
  }

  Timer? _waitPassengerTimer;
  static const int _waitPassengerTotalSeconds = 300;
  int _waitPassengerElapsedSeconds = 0;

  void startTimerDriverWaitPassenger5Minute() {
    if (currentRideState.value != RideState.driverArrived) return;

    stopTimerFromDriverToPassengerAfterApplied();
    isTimerRunning = false;
    _stopWaitPassengerTimer();

    isDriverArrivePassenger = true;
    isDriverInPassengerWay = false;
    timeToPassengerFromDriverAfterApplied = 0;

    _waitPassengerElapsedSeconds = 0;
    remainingTimeDriverWaitPassenger5Minute = _waitPassengerTotalSeconds;
    progressTimerDriverWaitPassenger5Minute = 0;

    int m = (remainingTimeDriverWaitPassenger5Minute / 60).floor();
    int s = remainingTimeDriverWaitPassenger5Minute % 60;
    stringRemainingTimeDriverWaitPassenger5Minute =
        '$m:${s.toString().padLeft(2, '0')}';

    update();

    _waitPassengerTimer = Timer.periodic(const Duration(seconds: 1), (t) {
      if (currentRideState.value != RideState.driverArrived) {
        _stopWaitPassengerTimer(resetUI: true);
        if (currentRideState.value == RideState.inProgress) {
          isDriverArrivePassenger = false;
        }
        update();
        return;
      }

      _waitPassengerElapsedSeconds++;
      int remaining = _waitPassengerTotalSeconds - _waitPassengerElapsedSeconds;
      if (remaining < 0) remaining = 0;

      remainingTimeDriverWaitPassenger5Minute = remaining;
      progressTimerDriverWaitPassenger5Minute =
          _waitPassengerElapsedSeconds / _waitPassengerTotalSeconds;

      int minutes = (remaining / 60).floor();
      int seconds = remaining % 60;
      stringRemainingTimeDriverWaitPassenger5Minute =
          '$minutes:${seconds.toString().padLeft(2, '0')}';
      update();

      if (remaining == 0) {
        _stopWaitPassengerTimer();
      }
    });
  }

  void beginRideTimer() {
    Timer.periodic(const Duration(seconds: 1), (timer) {
      if (!timerController.isClosed) {
        timerController.add(timer.tick);
      }
      update();
    });
  }

  final timerController = StreamController<int>();
  void stopRideTimer() {
    timerController.close();
    update();
  }

  void rideIsBeginPassengerTimer() {
    _rideProgressTimer?.cancel();
    _hasShownSpeedWarning = false;

    DateTime now = DateTime.now();
    DateTime expectedArrivalTime = now.add(Duration(seconds: durationToRide));

    var arrivalTime = DateFormat('hh:mm a').format(expectedArrivalTime);
    box.write(BoxName.arrivalTime, arrivalTime);

    Log.print("⏳ Ride Timer Started. Duration: $durationToRide sec");

    _rideProgressTimer =
        Timer.periodic(const Duration(seconds: 1), (timer) async {
      if (currentRideState.value != RideState.inProgress) {
        timer.cancel();
        return;
      }

      DateTime currentNow = DateTime.now();
      int remainingSeconds =
          expectedArrivalTime.difference(currentNow).inSeconds;

      if (remainingSeconds < 0) remainingSeconds = 0;

      remainingTimeTimerRideBegin = remainingSeconds;
      progressTimerRideBegin =
          durationToRide > 0 ? 1 - (remainingSeconds / durationToRide) : 1.0;

      int minutes = (remainingSeconds / 60).floor();
      int seconds = remainingSeconds % 60;
      stringRemainingTimeRideBegin =
          '$minutes:${seconds.toString().padLeft(2, '0')}';

      final percent = (progressTimerRideBegin * 100).clamp(0, 100).toInt();

      if (remainingSeconds % 5 == 0 || remainingSeconds == 0) {
        IosLiveActivityService.updateRideActivity(
          status: 'ongoing',
          driverName: driverName,
          carDetails: '$make • $model • $carColor',
          etaText: stringRemainingTimeRideBegin,
          progress: progressTimerRideBegin.clamp(0.0, 1.0),
        );
      }

      if (remainingSeconds % 60 == 0 || remainingSeconds == 0) {
        await RideLiveNotification.showTripInProgress(
          percentage: percent,
          etaText: stringRemainingTimeRideBegin,
        );
      }

      if (speed > 100 && !_hasShownSpeedWarning) {
        _hasShownSpeedWarning = true;
        _triggerSpeedWarning();
      }

      if (speed < 80 && _hasShownSpeedWarning) {
        _hasShownSpeedWarning = false;
      }

      if (remainingSeconds <= 0) {
        timer.cancel();
      }
      update();
    });
  }

  void _triggerSpeedWarning() {
    NotificationController().showNotification("Warning: Speeding detected!".tr,
        'You can call or record audio of this trip'.tr, 'tone1');

    Get.defaultDialog(
      barrierDismissible: false,
      title: "Warning: Speeding detected!".tr,
      titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
      content: Column(
        children: [
          Icon(Icons.speed, size: 50, color: AppColor.redColor),
          const SizedBox(height: 10),
          Text(
            "We noticed the speed is exceeding 100 km/h. Please slow down for your safety..."
                .tr,
            textAlign: TextAlign.center,
            style: AppStyle.title,
          ),
        ],
      ),
      confirm: MyElevatedButton(
        title: "Share Trip Details".tr,
        kolor: AppColor.redColor,
        onPressed: () {
          Get.back();
          uiInteractions.sosPassenger();
        },
      ),
      cancel: MyElevatedButton(
        title: "I'm Safe".tr,
        kolor: AppColor.greenColor,
        onPressed: () {
          Get.back();
        },
      ),
    );
  }

  void rideIsBeginPassengerTimerVIP() async {
    rideInProgress = true;
    bool sendSOS = false;
    while (rideInProgress) {
      await Future.delayed(const Duration(seconds: 1));
      elapsedTimeInSeconds++;

      int minutes = (elapsedTimeInSeconds / 60).floor();
      int seconds = (elapsedTimeInSeconds % 60).toInt();
      stringElapsedTimeRideBeginVip =
          '$minutes:${seconds.toString().padLeft(2, '0')}';

      if (speed > 100 && !sendSOS) {
        Get.defaultDialog(
          barrierDismissible: false,
          title: "Warning: Speeding detected!".tr,
          titleStyle: AppStyle.title,
          content: Text(
            "We noticed the speed is exceeding 100 km/h. Please slow down for your safety. If you feel unsafe, you can share your trip details with a contact or call the police using the red SOS button."
                .tr,
            style: AppStyle.title,
          ),
          confirm: MyElevatedButton(
            title: "Share Trip Details".tr,
            onPressed: () {
              Get.back();
              String message = "**Emergency SOS from Passenger:**\n";
              message += "* ${'Origin'.tr}: $passengerLocation\n";
              message += "* ${'Destination'.tr}: $myDestination\n";
              message += "* ${'Driver Name'.tr}: $passengerName\n";
              message += "* ${'Driver Car Plate'.tr}: $licensePlate\n\n";
              message += "* ${'Driver Phone'.tr}: $driverPhone\n\n";
              message +=
                  "${'Current Location'.tr}:https://www.google.com/maps/place/${passengerLocation.latitude},${passengerLocation.longitude} \n";
              message += "Please help! Contact me as soon as possible.".tr;

              launchCommunication(
                  'whatsapp', box.read(BoxName.sosPhonePassenger), message);
              sendSOS = true;
            },
            kolor: AppColor.redColor,
          ),
          cancel: MyElevatedButton(
            title: "Cancel".tr,
            onPressed: () {
              Get.back();
            },
            kolor: AppColor.greenColor,
          ),
        );
      }
      update();
    }
  }

  Future<void> tripFinishedFromDriver() async {
    // 🛑 Race Condition Guard: إذا تمت معالجتها مسبقاً، تخطى فوراً
    if (_isFinishProcessed) {
      Log.print("✋ tripFinishedFromDriver: Already processed. Skipping.");
      return;
    }
    _isFinishProcessed = true;

    Log.print('🧹 Cleaning UI for Finish');
    if (Get.isDialogOpen == true) Get.back();
    if (Get.isBottomSheetOpen == true) Get.back();

    statusRide = 'Finished';
    currentRideState.value = RideState.finished;

    isSearchingWindow = false;
    rideTimerBegin = false;
    shouldFetch = false;

    stopAllTimers();
    resetAllMapStates();
    mapEngine.clearPolyline();
    markers = {};
    update();
  }

  void listenToBeginRideStream() {
    beginRideStream.listen((status) {
      Log.print("Ride status: $status");
    }, onError: (error) {
      Log.print("Error in Begin Ride Stream: $error");
    });
  }

  Future<void> begiVIPTripFromPassenger() async {
    timeToPassengerFromDriverAfterApplied = 0;
    remainingTime = 0;
    isBottomSheetShown = false;
    remainingTimeToPassengerFromDriverAfterApplied = 0;
    remainingTimeDriverWaitPassenger5Minute = 0;
    rideTimerBegin = true;
    statusRideVip = 'Begin';
    isDriverInPassengerWay = false;
    isDriverArrivePassenger = false;
    update();
    rideIsBeginPassengerTimerVIP();
    runWhenRideIsBegin();
  }

  Future<void> getRideStatusFromStartApp() async {
    try {
      var res = await CRUD().get(
          link: AppLink.getRideStatusFromStartApp,
          payload: {'passenger_id': box.read(BoxName.passengerID)});
      Log.print('rideStatusFromStartApp: $res');
      if (res == 'failure') {
        rideStatusFromStartApp = {
          'data': {'status': 'NoRide', 'needsReview': false}
        };
        isStartAppHasRide = false;
      } else {
        var decoded = jsonDecode(res);
        if (decoded['status'] == 'failure') {
          rideStatusFromStartApp = {
            'data': {'status': 'NoRide', 'needsReview': false}
          };
          isStartAppHasRide = false;
        } else {
          rideStatusFromStartApp = decoded;
        }
      }

      String status =
          (rideStatusFromStartApp['data']['status'] ?? '').toString();
      String lowerStatus = status.toLowerCase();

      if (lowerStatus == 'begin' ||
          lowerStatus == 'apply' ||
          lowerStatus == 'applied' ||
          lowerStatus == 'accepted' ||
          lowerStatus == 'arrived') {
        statusRide = status;
        isStartAppHasRide = true;
        final bool isBeginStatus = lowerStatus == 'begin';
        final bool isPickupStatus = lowerStatus == 'apply' ||
            lowerStatus == 'applied' ||
            lowerStatus == 'accepted' ||
            lowerStatus == 'arrived';
        currentRideState.value = lowerStatus == 'begin'
            ? RideState.inProgress
            : lowerStatus == 'arrived'
                ? RideState.driverArrived
                : RideState.driverApplied;
        driverId =
            rideStatusFromStartApp['data']['driver_id']?.toString() ?? '';
        driverName =
            rideStatusFromStartApp['data']['driverName']?.toString() ?? '';
        driverRate =
            rideStatusFromStartApp['data']['rateDriver']?.toString() ?? '5.0';
        final LatLng? pickupPoint = _parseLatLng(
            rideStatusFromStartApp['data']['start_location']?.toString());
        final LatLng? destinationPoint = _parseLatLng(
            rideStatusFromStartApp['data']['end_location']?.toString());
        if (pickupPoint != null) {
          passengerLocation = pickupPoint;
        }
        if (destinationPoint != null) {
          myDestination = destinationPoint;
        }
        statusRideFromStart = true;
        update();

        // Safe recovery of trip data
        Map<String, dynamic>? tripData;
        try {
          var rawTrip = box.read(BoxName.tripData);
          if (rawTrip is Map) {
            tripData = Map<String, dynamic>.from(rawTrip);
          }
        } catch (e) {
          Log.print("Error reading BoxName.tripData: $e");
        }

        String? pointsString = tripData?['polyline'];

        if (pointsString == null || pointsString.isEmpty) {
          // No local polyline saved: Re-fetch the route from API
          final String startLoc =
              rideStatusFromStartApp['data']['start_location'] ?? '';
          final String endLoc =
              rideStatusFromStartApp['data']['end_location'] ?? '';
          if (startLoc.isNotEmpty && endLoc.isNotEmpty) {
            Log.print("🔄 Re-fetching route from API: $startLoc -> $endLoc");
            // Call getDirectionMap to fetch the route asynchronously
            getDirectionMap(startLoc, endLoc, []);
          }
        } else {
          List<LatLng> decodedPoints =
              await compute(decodePolylineIsolate, pointsString);

          mapEngine.clearPolyline();
          mapEngine.polylineCoordinates.clear();
          mapEngine.polylineCoordinates.addAll(decodedPoints);
          if (decodedPoints.isNotEmpty) {
            passengerLocation = pickupPoint ?? decodedPoints.first;
            myDestination = destinationPoint ?? decodedPoints.last;
          }
          var polyline = Polyline(
            polylineId: const PolylineId('main_route'),
            points: mapEngine.polylineCoordinates,
            width: 6,
            color: const Color(0xFF2196F3),
          );

          polyLines = {...polyLines, polyline};
        }

        timeToPassengerFromDriverAfterApplied = 0;
        remainingTime = 0;
        remainingTimeToPassengerFromDriverAfterApplied = 0;
        remainingTimeDriverWaitPassenger5Minute = 0;
        rideTimerBegin = isBeginStatus;
        isDriverInPassengerWay = false;
        isDriverArrivePassenger = false;

        // Safe durationToAdd parsing
        if (tripData != null && tripData['distance_m'] != null) {
          var distVal = tripData['distance_m'];
          if (distVal is Duration) {
            durationToAdd = distVal;
          } else if (distVal is num) {
            durationToAdd = Duration(seconds: distVal.toInt());
          }
        } else {
          durationToAdd = Duration.zero;
        }

        mapSocket.initConnectionWithSocket();

        if (isBeginStatus) {
          _isRideBeginLogicExecuted = true;
          _isRideStartedProcessed = true;
          await getDriverCarsLocationToPassengerAfterApplied();
          if (driverCarsLocationToPassengerAfterApplied.isNotEmpty &&
              myDestination.latitude != 0 &&
              myDestination.longitude != 0) {
            await calculateDriverToPassengerRoute(
              driverCarsLocationToPassengerAfterApplied.last,
              myDestination,
              isBeginPhase: true,
            );
          }
          rideIsBeginPassengerTimer();
          runWhenRideIsBegin();
        } else if (isPickupStatus) {
          _isAcceptanceProcessed = true;
          _isDriverAppliedLogicExecuted = true;
          await getDriverCarsLocationToPassengerAfterApplied();
          if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) {
            await calculateDriverToPassengerRoute(
              driverCarsLocationToPassengerAfterApplied.last,
              passengerLocation,
            );
            startTimerFromDriverToPassengerAfterApplied();
          }
          _startSocketWatchdog();
        }
        update();
      }
    } catch (e) {
      Log.print("Error getRideStatusFromStartApp: $e");
    }
  }

  void driverArrivePassenger() {
    timeToPassengerFromDriverAfterApplied = 0;
    remainingTime = 0;
    update();
    rideIsBeginPassengerTimer();
  }

  void cancelTimerToPassengerFromDriverAfterApplied() {
    timerToPassengerFromDriverAfterApplied?.cancel();
  }

  Future<bool> postRideDetailsToServer() async {
    if (mapEngine.polylineCoordinates.isEmpty) return false;

    LatLng startLoc = mapEngine.polylineCoordinates.first;
    LatLng endLoc = mapEngine.polylineCoordinates.last;

    Map<String, dynamic> payload = {
      "start_location": '${startLoc.latitude},${startLoc.longitude}',
      "end_location": '${endLoc.latitude},${endLoc.longitude}',
      "date": DateTime.now().toString(),
      "time": DateTime.now().toString(),
      "endtime": "00:00:00",
      "price": double.parse(totalPassenger.toString()).toStringAsFixed(2),
      "passenger_id": box.read(BoxName.passengerID).toString(),
      "driver_id": "0",
      "status": "waiting",
      "carType": box.read(BoxName.carType),
      "price_for_driver": totalPassenger.toString(),
      "price_for_passenger": totalME.toString(),
      "distance": distance.toString(),
      "passenger_name": box.read(BoxName.name).toString(),
      "passenger_phone": box.read(BoxName.phone).toString(),
      "passenger_token": box.read(BoxName.tokenFCM).toString(),
      "passenger_email": box.read(BoxName.email).toString(),
      "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(),
      "passenger_rating": (passengerRate ?? 5.0).toString(),
      "start_name": startNameAddress,
      "end_name": endNameAddress,
      "duration_text": "${(durationToRide / 60).floor()}",
      "distance_text": "$distance",
      "is_wallet": Get.find<PaymentController>().isWalletChecked.toString(),
      "has_steps": Get.find<WayPointController>().wayPoints.length > 1
          ? 'true'
          : 'false',
      "step0": placesCoordinate.length > 0 ? placesCoordinate[0] : "",
      "step1": placesCoordinate.length > 1 ? placesCoordinate[1] : "",
      "step2": placesCoordinate.length > 2 ? placesCoordinate[2] : "",
      "step3": placesCoordinate.length > 3 ? placesCoordinate[3] : "",
      "step4": placesCoordinate.length > 4 ? placesCoordinate[4] : "",
    };

    Log.print('   📦 Payload: $payload');

    try {
      var response = await CRUD().post(
          link: "${AppLink.server}/ride/rides/add_ride.php", payload: payload);

      var jsonResponse = (response is String) ? jsonDecode(response) : response;

      if (jsonResponse['status'] == 'success') {
        rideId = jsonResponse['message'].toString();
        Log.print("✅ Ride Created ID: $rideId");
        return true;
      } else {
        Log.print("❌ Ride Creation Failed: $response");
        return false;
      }
    } catch (e) {
      Log.print("❌ Exception in postRide: $e");
      return false;
    }
  }

  Future<void> rideAppliedFromDriver(bool isApplied) async {
    Log.print('[rideAppliedFromDriver] 🚀 Starting logic...');
    await getUpdatedRideForDriverApply(rideId);

    if (['Speed', 'Awfar Car'].contains(box.read(BoxName.carType))) {
      NotificationController().showNotification('Fixed Price'.tr,
          'The captain is responsible for the route.'.tr, 'ding');
    } else if (['Comfort', 'Lady'].contains(box.read(BoxName.carType))) {
      NotificationController().showNotification('Attention'.tr,
          'The price may increase if the route changes.'.tr, 'ding');
    }

    isApplied = true;
    statusRide = 'Apply';
    rideConfirm = false;
    isSearchingWindow = false;
    _isDriverAppliedLogicExecuted = true;

    update();

    // إيقاف جلب السيارات المجاورة ومسحها، باستثناء السائق الذي قبل الطلب
    mapEngine.reloadStartApp = false;
    mapEngine.markers
        .removeWhere((marker) => marker.markerId.value != driverId.toString());
    mapEngine.update();

    await getDriverCarsLocationToPassengerAfterApplied();

    if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) {
      LatLng driverPos = driverCarsLocationToPassengerAfterApplied.last;
      Log.print(
          '[rideAppliedFromDriver] 📍 Driver at: $driverPos, Passenger at: $passengerLocation');
      await calculateDriverToPassengerRoute(driverPos, passengerLocation);
      mapEngine.fitCameraToPoints(driverPos, passengerLocation);
    }

    startTimerFromDriverToPassengerAfterApplied();
  }

  Future<void> getInitialDriverDistanceAndDuration(
      LatLng driverPos, LatLng passengerPos) async {
    final String apiUrl = 'https://routec.intaleq.xyz/route';
    final String apiKey = Env.mapKeyOsm;
    final String origin = '${driverPos.latitude},${driverPos.longitude}';
    final String dest = '${passengerPos.latitude},${passengerPos.longitude}';

    final Uri uri = Uri.parse(
        '$apiUrl?origin=$origin&destination=$dest&steps=false&overview=false');

    try {
      final response = await http.get(uri, headers: {'X-API-KEY': apiKey});
      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        if (data['status'] == 'ok') {
          double durationSecondsRaw = (data['duration_s'] as num).toDouble();
          int finalDurationSeconds =
              (durationSecondsRaw * kDurationScalar).toInt();
          double distanceMeters = (data['distance_m'] as num).toDouble();

          updateDriverRouteMetrics(
            etaSeconds: finalDurationSeconds,
            distanceMeters: distanceMeters,
          );

          update();
        }
      }
    } catch (e) {
      Log.print('Error getInitialDriverDistanceAndDuration: $e');
    }
  }

  int durationToPassenger = 0;
  String distanceByPassenger = '';

  Future<void> drawDriverPathOnly(LatLng driverPos, LatLng passengerPos) async {
    final String apiUrl = 'https://routec.intaleq.xyz/route';
    final String apiKey = Env.mapKeyOsm;
    final String origin = '${driverPos.latitude},${driverPos.longitude}';
    final String dest = '${passengerPos.latitude},${passengerPos.longitude}';

    final Uri uri = Uri.parse(
        '$apiUrl?origin=$origin&destination=$dest&steps=false&overview=full');

    try {
      final response = await http.get(uri, headers: {'X-API-KEY': apiKey});
      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        if (data['status'] == 'ok' && data['polyline'] != null) {
          final String pointsString = data['polyline'];
          List<LatLng> decodedPoints =
              await compute(decodePolylineIsolate, pointsString);
          _currentDriverRoutePoints = decodedPoints;
          final double decodedDistance = _pathDistanceMeters(decodedPoints);
          if (decodedDistance > 0) {
            _currentDriverRouteDistanceMeters = decodedDistance;
          }

          polyLines = polyLines
              .where((p) =>
                  !p.polylineId.value.startsWith('driver_route') &&
                  p.polylineId.value != 'main_route' &&
                  p.polylineId.value != 'route_primary' &&
                  p.polylineId.value != 'route_direct')
              .toSet();

          // رسم خط صلب (Solid) من السائق للراكب
          final Polyline driverSolidPolyline = Polyline(
            polylineId: const PolylineId('driver_route_solid'),
            points: decodedPoints,
            color: Colors.amber, // مسار القدوم باللون الأصفر
            width: 5,
          );
          polyLines.add(driverSolidPolyline);
          update();
        }
      }
    } catch (e) {
      Log.print('Error drawing driver path: $e');
    }
  }

  void listenToRideStatusStream() {
    rideStatusStream.listen((rideStatus) {
      Log.print("Ride Status: $rideStatus");
    }, onError: (error) {
      Log.print("Error in Ride Status Stream: $error");
    });
  }

  void start15SecondTimer(String rideId) {}

  void startUiCountdown() {
    _uiCountdownTimer?.cancel();
    progress = 0;
    remainingTime = durationTimer;

    _uiCountdownTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      int i = timer.tick;
      progress = i / durationTimer;
      remainingTime = durationTimer - i;

      if (remainingTime <= 0) {
        timer.cancel();
        rideConfirm = false;
        timeToPassengerFromDriverAfterApplied += durationToPassenger;
        timerEnded();
      }
      update();
    });
  }

  double progress = 0;
  int durationTimer = 9;

  void timerEnded() async {
    runEvery30SecondsUntilConditionMet();
    isCancelRidePageShown = false;
    update();
  }

  late String driverCarModel = '', driverCarMake = '', driverLicensePlate = '';

  Future<void> getUpdatedRideForDriverApply(String rideId) async {
    if (rideId == 'yet' || rideId.isEmpty) return;

    try {
      final res = await CRUD().get(
          link: "${AppLink.server}/ride/rides/getRideOrderID.php",
          payload: {'passengerID': box.read(BoxName.passengerID).toString()});

      if (res != 'failure') {
        var response = jsonDecode(res);
        Log.print('getUpdatedRideForDriverApply Response: $response');

        if (response['status'] == 'success' &&
            response['data'] != null &&
            response['data'] is Map) {
          var data = response['data'];

          driverId = data['driver_id']?.toString() ?? '';
          driverPhone = data['phone']?.toString() ?? '';
          driverCarMake = data['make']?.toString() ?? '';
          model = data['model']?.toString() ?? '';
          colorHex = data['color_hex']?.toString() ?? '';
          carColor = data['color']?.toString() ?? '';
          make = data['make']?.toString() ?? '';
          licensePlate = data['car_plate']?.toString() ?? '';

          String firstName = data['passengerName']?.toString() ?? '';
          String lastName = data['last_name']?.toString() ?? '';
          passengerName =
              lastName.isNotEmpty ? "$firstName $lastName" : firstName;
          driverName = data['driverName']?.toString() ?? '';
          driverToken = data['token']?.toString() ?? '';
          carYear = data['year']?.toString() ?? '';
          driverRate = data['ratingDriver']?.toString() ?? '5.0';
          driverRatingCount = data['ratingCount']?.toString() ?? '0';
          driverCompletedRides = data['completedRides']?.toString() ?? '0';
          driverTier = data['driverTier']?.toString() ?? 'Verified driver';

          update();
        }
      }
    } catch (e) {
      Log.print("Error in getUpdatedRideForDriverApply: $e");
    }
  }

  String getLocationArea(double latitude, double longitude) {
    LatLng passengerPoint = LatLng(latitude, longitude);
    String previousCountry = box.read(BoxName.countryCode)?.toString() ?? '';
    String newCountry = 'Jordan';

    if (isPointInPolygon(passengerPoint, CountryPolygons.jordanBoundary)) {
      newCountry = 'Jordan';
    } else if (isPointInPolygon(
        passengerPoint, CountryPolygons.syriaBoundary)) {
      newCountry = 'Syria';
    } else if (isPointInPolygon(
        passengerPoint, CountryPolygons.egyptBoundary)) {
      newCountry = 'Egypt';
    } else {
      newCountry = 'Jordan';
    }

    box.write(BoxName.countryCode, newCountry);
    box.write(BoxName.serverChosen, AppLink.server);

    if (newCountry != previousCountry) {}

    return newCountry;
  }

  bool isPointInPolygon(LatLng point, List<LatLng> polygon) {
    int intersections = 0;
    for (int i = 0; i < polygon.length; i++) {
      LatLng vertex1 = polygon[i];
      LatLng vertex2 = polygon[(i + 1) % polygon.length];

      if (_rayIntersectsSegment(point, vertex1, vertex2)) {
        intersections++;
      }
    }
    return intersections % 2 != 0;
  }

  bool _rayIntersectsSegment(LatLng point, LatLng vertex1, LatLng vertex2) {
    double px = point.longitude;
    double py = point.latitude;
    double v1x = vertex1.longitude;
    double v1y = vertex1.latitude;
    double v2x = vertex2.longitude;
    double v2y = vertex2.latitude;

    if ((py < v1y && py < v2y) || (py > v1y && py > v2y)) {
      return false;
    }
    double intersectX = v1x + (py - v1y) * (v2x - v1x) / (v2y - v1y);
    return intersectX > px;
  }

  String generateTrackingLink(String rideId, String driverId) {
    String cleanRideId = rideId.toString().trim();
    String cleanDriverId = driverId.toString().trim();
    const String secretSalt = "Siro_Secure_Track_2025";

    String rawString = "$cleanRideId$cleanDriverId$secretSalt";
    var bytes = utf8.encode(rawString);
    var digest = md5.convert(bytes);
    String token = digest.toString();

    return "https://intaleqapp.com/track/index.php?id=$cleanRideId&token=$token";
  }

  calcualateDistsanceInMetet(LatLng prev, current) async {
    double distance2 = Geolocator.distanceBetween(
      prev.latitude,
      prev.longitude,
      current.latitude,
      current.longitude,
    );
    return distance2;
  }

  uploadPassengerLocation() async {
    await CRUD().post(link: AppLink.addpassengerLocation, payload: {
      "passengerId": box.read(BoxName.passengerID),
      "lat": passengerLocation.latitude.toString(),
      "lng": passengerLocation.longitude.toString(),
      "rideId": rideId.toString()
    });
  }

  void _showRideStartNotifications() {
    if (['Speed', 'Awfar Car'].contains(box.read(BoxName.carType))) {
      NotificationController().showNotification('Fixed Price'.tr,
          'The captain is responsible for the route.'.tr, 'ding');
    } else if (['Comfort', 'Lady'].contains(box.read(BoxName.carType))) {
      NotificationController().showNotification('Attention'.tr,
          'The price may increase if the route changes.'.tr, 'ding');
    }
  }

  bool promoTaken = false;
  final promo = TextEditingController();

  void applyPromoCodeToPassenger(BuildContext context) async {
    if (promoTaken == true) {
      MyDialog().getDialog('Promo Already Used'.tr,
          'You have already used this promo code.'.tr, () => Get.back());
      return;
    }
    if (!promoFormKey.currentState!.validate()) return;

    try {
      final res = await CRUD().post(link: AppLink.getPrices, payload: {
        'distance': distance.toString(),
        'durationToRide': durationToRide.toString(),
        'startNameAddress': startNameAddress,
        'endNameAddress': endNameAddress,
        'destLat': myDestination.latitude.toString(),
        'destLng': myDestination.longitude.toString(),
        'passengerLat': newMyLocation.latitude.toString(),
        'passengerLng': newMyLocation.longitude.toString(),
        'walletVal': box.read(BoxName.passengerWalletTotal)?.toString() ?? '0',
        'activeMenuWaypointCount': activeMenuWaypointCount.toString(),
        'promo_code': promo.text,
        'passenger_id': box.read(BoxName.passengerID),
        'country': box.read(BoxName.countryCode) ?? '',
      });

      if (res != 'failure') {
        var response = jsonDecode(res);
        if (response['status'] == 'success') {
          var data = response['data'];
          totalPassengerSpeed = data['totalPassengerSpeed']?.toString() ?? '0';
          totalPassengerBalash =
              data['totalPassengerBalash']?.toString() ?? '0';
          totalPassengerComfort =
              data['totalPassengerComfort']?.toString() ?? '0';
          totalPassengerElectric =
              data['totalPassengerElectric']?.toString() ?? '0';
          totalPassengerLady = data['totalPassengerLady']?.toString() ?? '0';
          totalPassengerScooter =
              data['totalPassengerScooter']?.toString() ?? '0';
          totalPassengerVan = data['totalPassengerVan']?.toString() ?? '0';
          totalPassengerRayehGai =
              data['totalPassengerRayehGai']?.toString() ?? '0';
          totalPassengerRayehGaiComfort =
              data['totalPassengerRayehGaiComfort']?.toString() ?? '0';
          totalPassengerRayehGaiBalash =
              data['totalPassengerRayehGaiBalash']?.toString() ?? '0';

          promoTaken = true;
          update();

          Confetti.launch(
            context,
            options:
                const ConfettiOptions(particleCount: 100, spread: 70, y: 0.6),
          );
        } else {
          MyDialog().getDialog(
              'Promo Error'.tr,
              response['message']?.toString() ?? 'Invalid Promo'.tr,
              () => Get.back());
          return;
        }
      }
      Get.back();
      await Future.delayed(const Duration(milliseconds: 120));
    } catch (e) {
      Get.snackbar('Error'.tr, e.toString(),
          backgroundColor: AppColor.redColor);
    }
  }

  double getDistanceFromText(String distanceText) {
    String distanceValue = distanceText.replaceAll(RegExp(r'[^0-9.]+'), '');
    double distance = double.parse(distanceValue);
    return distance;
  }

  double costForDriver = 0;

  Future bottomSheet() async {
    durationToAdd = Duration(seconds: durationToRide);
    hours = durationToAdd.inHours;
    minutes = (durationToAdd.inMinutes % 60).round();
    final DateTime currentTime = DateTime.now();
    newTime = currentTime.add(durationToAdd);

    try {
      final res = await CRUD().post(link: AppLink.getPrices, payload: {
        'distance': distance.toString(),
        'durationToRide': durationToRide.toString(),
        'startNameAddress': startNameAddress,
        'endNameAddress': endNameAddress,
        'destLat': myDestination.latitude.toString(),
        'destLng': myDestination.longitude.toString(),
        'passengerLat': newMyLocation.latitude.toString(),
        'passengerLng': newMyLocation.longitude.toString(),
        'walletVal': box.read(BoxName.passengerWalletTotal)?.toString() ?? '0',
        'activeMenuWaypointCount': activeMenuWaypointCount.toString(),
        'passenger_id': box.read(BoxName.passengerID) ?? '',
        'country': box.read(BoxName.countryCode) ?? '',
      });

      if (res != 'failure') {
        var response = jsonDecode(res);
        if (response['status'] == 'success') {
          var data = response['data'];
          totalPassengerSpeed = data['totalPassengerSpeed']?.toString() ?? '0';
          totalPassengerBalash =
              data['totalPassengerBalash']?.toString() ?? '0';
          totalPassengerComfort =
              data['totalPassengerComfort']?.toString() ?? '0';
          totalPassengerElectric =
              data['totalPassengerElectric']?.toString() ?? '0';
          totalPassengerLady = data['totalPassengerLady']?.toString() ?? '0';
          totalPassengerScooter =
              data['totalPassengerScooter']?.toString() ?? '0';
          totalPassengerVan = data['totalPassengerVan']?.toString() ?? '0';
          totalPassengerRayehGai =
              data['totalPassengerRayehGai']?.toString() ?? '0';
          totalPassengerRayehGaiComfort =
              data['totalPassengerRayehGaiComfort']?.toString() ?? '0';
          totalPassengerRayehGaiBalash =
              data['totalPassengerRayehGaiBalash']?.toString() ?? '0';

          totalPassenger = totalPassengerSpeed;
          totalCostPassenger = totalPassenger;
        }
      }
    } catch (e) {
      Log.print("Error fetching prices: $e");
    }

    update();
    mapEngine.changeBottomSheetShown(forceValue: true);
  }

  // حساب المسار بين السائق والراكب وعرضه على الخريطة.
  // ترسل هذه الدالة طلبًا للخادم للحصول على إحداثيات المسار وتفك تشفيره.
  // ثم تقوم بتحديث المسافة والوقت وعرض الخطوط المناسبة على الخريطة.
  Future<void> calculateDriverToPassengerRoute(
      LatLng driverPos, LatLng passengerPos,
      {bool isBeginPhase = false}) async {
    final Map<String, String> queryParams = {
      'fromLat': driverPos.latitude.toString(),
      'fromLng': driverPos.longitude.toString(),
      'toLat': passengerPos.latitude.toString(),
      'toLng': passengerPos.longitude.toString(),
    };
    final uri =
        Uri.parse(AppLink.mapSaasRoute).replace(queryParameters: queryParams);

    Log.print('📍 Calculating Driver Route: $uri');

    try {
      final response = await http.get(uri, headers: {
        'x-api-key': Env.mapSaasKey,
      }).timeout(const Duration(seconds: 20));

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

        var routeData = responseData['routes'] != null
            ? responseData['routes'][0]
            : responseData;

        double durationSecondsRaw = (routeData['duration'] as num).toDouble();
        int finalDurationSeconds =
            (durationSecondsRaw * kDurationScalar).toInt();
        double distanceMeters = (routeData['distance'] as num).toDouble();

        updateDriverRouteMetrics(
          etaSeconds: finalDurationSeconds,
          distanceMeters: distanceMeters,
        );
        _currentDriverRouteDistanceMeters = distanceMeters;
        _currentDriverRouteDurationSeconds = finalDurationSeconds;

        int minutes = (finalDurationSeconds / 60).floor();
        int seconds = finalDurationSeconds % 60;
        stringRemainingTimeToPassenger =
            '$minutes:${seconds.toString().padLeft(2, '0')}';

        Log.print(
            '✅ Driver Route Info: $minutes min, ${distanceMeters.toInt()} m');

        String pointsString =
            routeData['points'] ?? routeData['geometry'] ?? "";
        if (pointsString.isNotEmpty) {
          List<LatLng> decodedPoints =
              await compute(decodePolylineIsolate, pointsString);
          _currentDriverRoutePoints = decodedPoints;
          final double decodedDistance = _pathDistanceMeters(decodedPoints);
          if (decodedDistance > 0) {
            _currentDriverRouteDistanceMeters = decodedDistance;
          }
          // مسح كل السلمات السابقة (الخط المستمر والمتقطع على حد سواء)
          polyLines = polyLines
              .where((p) =>
                  !p.polylineId.value.startsWith('driver_route') &&
                  p.polylineId.value != 'main_route' &&
                  p.polylineId.value != 'route_primary' &&
                  p.polylineId.value != 'route_direct')
              .toSet();

          if (isBeginPhase) {
            // حالة Begin: لا نرسم مسار السائق القديم إطلاقاً لأنه وصل والآن الرحلة ستبدأ
            polyLines = polyLines
                .where((p) => !p.polylineId.value.startsWith('driver_route'))
                .toSet();
            polyLines = {
              ...polyLines,
              Polyline(
                polylineId: const PolylineId('main_route'),
                points: decodedPoints,
                color: const Color(0xFF2196F3),
                width: 6,
              )
            };
          } else {
            // مسح السلمات القديمة أولاً
            polyLines = polyLines
                .where((p) =>
                    !p.polylineId.value.startsWith('driver_route') &&
                    p.polylineId.value != 'main_route' &&
                    p.polylineId.value != 'route_primary' &&
                    p.polylineId.value != 'route_direct')
                .toSet();
            // حالة Apply/Arrived: خط متصل صلب بدل المتقطع
            polyLines = {
              ...polyLines,
              Polyline(
                polylineId: const PolylineId('driver_route_solid'),
                points: decodedPoints,
                color: Colors.amber, // مسار القدوم باللون الأصفر
                width: 5,
              )
            };
          }
        }

        mapEngine.fitCameraToPoints(driverPos, passengerPos);
        _updatePassengerWalkLine();
        update();
      }
    } catch (e) {
      Log.print('❌ Error calculating driver route: $e');
    }
  }

  Future<void> checkAndRecalculateIfDeviated(
    LatLng driverPos, {
    double? heading,
    double? speed,
  }) async {
    if (_isRecalculatingRoute || _currentDriverRoutePoints.isEmpty) return;

    double minDistance = 100000.0;
    int closestIdx = 0;
    for (int idx = 0; idx < _currentDriverRoutePoints.length; idx++) {
      final point = _currentDriverRoutePoints[idx];
      double dist = Geolocator.distanceBetween(driverPos.latitude,
          driverPos.longitude, point.latitude, point.longitude);
      if (dist < minDistance) {
        minDistance = dist;
        closestIdx = idx;
      }
    }

    final bool distanceDeviation = minDistance > _deviationThresholdMeters;
    final bool headingDeviation = _isHeadingAwayFromRoute(
      heading: heading,
      speed: speed,
      closestRouteIndex: closestIdx,
      distanceFromRouteMeters: minDistance,
    );

    if (!headingDeviation) {
      _routeHeadingMismatchCount = 0;
    } else {
      _routeHeadingMismatchCount++;
    }

    if (distanceDeviation || _routeHeadingMismatchCount >= 2) {
      Log.print(
          "⚠️ Driver deviated (${minDistance.toStringAsFixed(1)} m, heading mismatch: $_routeHeadingMismatchCount). Recalculating route...");
      _routeHeadingMismatchCount = 0;
      _isRecalculatingRoute = true;
      if (statusRide == 'Begin' ||
          statusRide == 'Arrived' ||
          currentRideState.value == RideState.inProgress ||
          currentRideState.value == RideState.driverArrived) {
        await calculateDriverToPassengerRoute(driverPos, myDestination,
            isBeginPhase: true);
      } else {
        await calculateDriverToPassengerRoute(driverPos, passengerLocation);
      }
      _isRecalculatingRoute = false;
    }
  }

  // تحديث الجزء المتبقي من المسار والمسافة والوقت بشكل تفاعلي.
  // تحدد الدالة أقرب نقطة للسائق على المسار الحالي وتقوم بقص النقاط السابقة.
  // ثم تعيد حساب المسافة والوقت المتبقيين محلياً وتحديث الخطوط على الخريطة.
  void updateRemainingRoute(LatLng driverPos, {bool updateEta = true}) {
    if (_currentDriverRoutePoints.isEmpty) return;

    int closestIdx = 0;
    double minDistance = double.infinity;
    for (int i = 0; i < _currentDriverRoutePoints.length; i++) {
      double dist = Geolocator.distanceBetween(
          driverPos.latitude,
          driverPos.longitude,
          _currentDriverRoutePoints[i].latitude,
          _currentDriverRoutePoints[i].longitude);
      if (dist < minDistance) {
        minDistance = dist;
        closestIdx = i;
      }
    }

    if (minDistance < 150.0) {
      List<LatLng> remainingPoints =
          _currentDriverRoutePoints.sublist(closestIdx);

      if (updateEta) {
        final double remainingDistance = _pathDistanceMeters(remainingPoints);
        int remainingDuration = _currentDriverRouteDurationSeconds;
        if (remainingDistance > 0 &&
            _currentDriverRouteDistanceMeters > 0 &&
            _currentDriverRouteDurationSeconds > 0) {
          remainingDuration =
              ((_currentDriverRouteDurationSeconds * remainingDistance) /
                      _currentDriverRouteDistanceMeters)
                  .round();
        }

        remainingDuration = max(0, remainingDuration);
        updateDriverRouteMetrics(
          etaSeconds: remainingDuration,
          distanceMeters: remainingDistance,
        );
      }

      polyLines = polyLines
          .where((p) =>
              !p.polylineId.value.startsWith('driver_route') &&
              p.polylineId.value != 'main_route' &&
              p.polylineId.value != 'route_primary' &&
              p.polylineId.value != 'route_direct')
          .toSet();

      if (statusRide == 'Begin' ||
          currentRideState.value == RideState.inProgress) {
        // لا نرسم أي شيء في حالة البدء لأنه وصل
        polyLines = polyLines
            .where((p) => !p.polylineId.value.startsWith('driver_route'))
            .toSet();
      } else {
        polyLines = {
          ...polyLines,
          Polyline(
            polylineId: const PolylineId('driver_route_solid'),
            points: remainingPoints,
            color: Colors.amber,
            width: 5,
          ),
        };
      }
      _updatePassengerWalkLine();
      update();
    }
  }

  Future<void> getDriverCarsLocationToPassengerAfterApplied() async {
    bool isRideActive = (statusRide == 'Apply' ||
        statusRide == 'Arrived' ||
        statusRide == 'Begin' ||
        currentRideState.value == RideState.driverApplied ||
        currentRideState.value == RideState.driverArrived ||
        currentRideState.value == RideState.inProgress);

    if (!isRideActive ||
        statusRide == 'Finished' ||
        statusRide == 'Cancel' ||
        currentRideState.value == RideState.finished ||
        currentRideState.value == RideState.noRide ||
        currentRideState.value == RideState.preCheckReview) {
      return;
    }

    if (_isFetchingDriverLocation) return;
    _isFetchingDriverLocation = true;

    try {
      var res = await CRUD().get(
          link: AppLink.getDriverCarsLocationToPassengerAfterApplied,
          payload: {'driver_id': driverId});

      if (res != 'failure') {
        var datadriverLocation = jsonDecode(res);

        if (datadriverLocation['message'] != null &&
            datadriverLocation['message'].isNotEmpty) {
          var _data = datadriverLocation['message'][0];

          LatLng newDriverPos = LatLng(
              double.parse(_data['latitude'].toString()),
              double.parse(_data['longitude'].toString()));
          double newHeading =
              double.tryParse(_data['heading']?.toString() ?? '0') ?? 0.0;
          double speed =
              double.tryParse(_data['speed']?.toString() ?? '0') ?? 0;

          if (driverCarsLocationToPassengerAfterApplied.length > 10) {
            driverCarsLocationToPassengerAfterApplied.removeAt(0);
          }
          driverCarsLocationToPassengerAfterApplied.add(newDriverPos);
          checkAndRecalculateIfDeviated(
            newDriverPos,
            heading: newHeading,
            speed: speed,
          );
          updateRemainingRoute(newDriverPos);
          if (statusRide == 'Begin' ||
              currentRideState.value == RideState.inProgress) {
            double zoom = 16.5;
            if (speed > 0) {
              zoom = 17.0 - ((speed - 10) / 70) * 2.5;
              zoom = zoom.clamp(14.5, 17.0);
            }
            if (mapEngine.mapController != null) {
              mapEngine.mapController!.animateCamera(
                  CameraUpdate.newLatLngZoom(newDriverPos, zoom));
            }
          }
          mapEngine.clearMarkersExceptStartEndAndDriver();
          reloadMarkerDriverCarsLocationToPassengerAfterApplied(
              datadriverLocation);
        }
      }
      update();
    } catch (e) {
      Log.print('Error fetching driver location: $e');
    } finally {
      _isFetchingDriverLocation = false;
    }
  }

  void reloadMarkerDriverCarsLocationToPassengerAfterApplied(
      dynamic datadriverLocation) {
    if (datadriverLocation == null ||
        datadriverLocation['message'] == null ||
        datadriverLocation['message'].isEmpty) {
      return;
    }

    var driverData = datadriverLocation['message'][0];
    LatLng newPosition = LatLng(double.parse(driverData['latitude'].toString()),
        double.parse(driverData['longitude'].toString()));
    double newHeading =
        double.tryParse(driverData['heading'].toString()) ?? 0.0;

    String icon;
    if (driverData['model'].toString().contains('دراجة') ||
        driverData['make'].toString().contains('دراجة')) {
      icon = mapEngine.motoIcon;
    } else if (driverData['gender'] == 'Female') {
      icon = mapEngine.ladyIcon;
    } else {
      icon = mapEngine.carIcon;
    }

    final String markerId = 'assigned_driver_marker';
    final mId = MarkerId(markerId);
    final existingMarker = markers.cast<Marker?>().firstWhere(
          (m) => m?.markerId == mId,
          orElse: () => null,
        );

    if (existingMarker != null) {
      mapEngine.smoothlyUpdateMarker(
          existingMarker, newPosition, newHeading, icon);
    } else {
      markers = {
        ...markers,
        Marker(
          markerId: mId,
          position: newPosition,
          rotation: newHeading,
          icon: InlqBitmap.fromStyleImage(icon),
          anchor: const Offset(0.5, 0.5),
        ),
      };
      update();
    }
  }

  void updateDriverMarker(LatLng position, double heading) {
    const String markerId = 'assigned_driver_marker';
    const mId = MarkerId(markerId);

    // Choose icon based on vehicle type
    String icon;
    if (model.contains('دراجة') || make.contains('دراجة')) {
      icon = mapEngine.motoIcon;
    } else if (gender == 'Female') {
      icon = mapEngine.ladyIcon;
    } else {
      icon = mapEngine.carIcon;
    }

    final existingMarker = markers.cast<Marker?>().firstWhere(
          (m) => m?.markerId == mId,
          orElse: () => null,
        );

    if (existingMarker != null) {
      mapEngine.smoothlyUpdateMarker(existingMarker, position, heading, icon);
    } else {
      markers = {
        ...markers,
        Marker(
          markerId: mId,
          position: position,
          icon: InlqBitmap.fromStyleImage(icon),
          rotation: heading,
          anchor: const Offset(0.5, 0.5),
        ),
      };
      update();
    }
  }

  Future runEvery30SecondsUntilConditionMet() async {
    double tripDurationInMinutes = durationToPassenger / 5;
    int loopCount = tripDurationInMinutes.ceil();
    for (var i = 0; i < loopCount; i++) {
      await Future.delayed(const Duration(seconds: 5));
      if (rideTimerBegin == true || statusRide == 'Apply') {
        await getDriverCarsLocationToPassengerAfterApplied();
      }
    }
  }

  Future runWhenRideIsBegin() async {
    double tripDurationInMinutes = durationToRide / 6;
    int loopCount = tripDurationInMinutes.ceil();
    mapEngine.clearMarkersExceptStartEndAndDriver();
    for (var i = 0; i < loopCount; i++) {
      await Future.delayed(const Duration(seconds: 4));
      await getDriverCarsLocationToPassengerAfterApplied();
    }
  }

  // بدء مراقب اتصال المقبس (Socket Watchdog).
  // يتحقق دورياً كل 5 ثوانٍ من آخر تحديث تم استلامه عبر المقبس.
  // في حال وجود خمول لأكثر من 15 ثانية، يجلب الموقع عبر واجهة التطبيق كطلب مفرد.
  // وإذا زاد الخمول عن 30 ثانية، يبدأ آلية الاقتراع الدوري كخيار احتياطي.
  void _startSocketWatchdog() {
    _watchdogTimer?.cancel();
    Log.print("👀 Starting Socket Watchdog (Hybrid Mode)...");

    _watchdogTimer = Timer.periodic(const Duration(seconds: 5), (timer) async {
      if (currentRideState.value != RideState.driverApplied &&
          currentRideState.value != RideState.driverArrived &&
          currentRideState.value != RideState.inProgress) {
        timer.cancel();
        return;
      }

      final lastTime = mapSocket.lastDriverLocationTime ??
          DateTime.now().subtract(const Duration(minutes: 1));
      final difference = DateTime.now().difference(lastTime).inSeconds;

      if (difference < 15 && mapSocket.isSocketConnected) {
        if (_locationPollingTimer != null &&
            _rideAcceptedViaSource == "Socket") {
          Log.print("✅ Socket recovered. Stopping polling fallback.");
          stopDriverLocationPolling();
        }
      } else if (difference >= 15 && difference < 30) {
        Log.print("⚠️ Socket silent for ${difference}s. Single API Poll...");
        await getDriverCarsLocationToPassengerAfterApplied();
        try {
          String statusFromServer = await getRideStatus(rideId);
          _handleServerStatusTransition(statusFromServer);
        } catch (e) {
          Log.print("Error polling ride status in watchdog: $e");
        }
      } else if (difference >= 30) {
        if (_locationPollingTimer == null) {
          Log.print(
              "🔴 Socket dead for ${difference}s. Activating polling fallback!");
          _startDriverLocationPollingWithTimer();
        } else {
          await getDriverCarsLocationToPassengerAfterApplied();
          try {
            String statusFromServer = await getRideStatus(rideId);
            _handleServerStatusTransition(statusFromServer);
          } catch (e) {
            Log.print("Error polling ride status in watchdog: $e");
          }
        }
      }
    });
  }

  // بدء الاقتراع الدوري لموقع السائق كخيار احتياطي عند توقف أو فشل المقبس.
  // يقوم هذا التوقيت دورياً كل 6 ثوانٍ بجلب موقع السائق وتحديث حالة الرحلة.
  void _startDriverLocationPollingWithTimer() {
    Log.print("📍 Starting Driver Location Polling (6s interval)");
    _locationPollingTimer?.cancel();

    _locationPollingTimer = Timer.periodic(Duration(seconds: 6), (timer) async {
      if (currentRideState.value == RideState.finished ||
          currentRideState.value == RideState.cancelled ||
          currentRideState.value == RideState.noRide) {
        timer.cancel();
        return;
      }
      await getDriverCarsLocationToPassengerAfterApplied();
      try {
        String statusFromServer = await getRideStatus(rideId);
        _handleServerStatusTransition(statusFromServer);
      } catch (e) {
        Log.print("Error polling ride status in fallback timer: $e");
      }
    });
  }

  void _handleServerStatusTransition(String status) {
    String lowerStatus = status.toLowerCase();
    Log.print(
        "🔄 _handleServerStatusTransition status: $lowerStatus | Current state: ${currentRideState.value}");

    if (lowerStatus == 'arrived' &&
        currentRideState.value != RideState.driverArrived) {
      processDriverArrival("Polling");
    } else if ((lowerStatus == 'begin' ||
            lowerStatus == 'started' ||
            lowerStatus == 'inprogress') &&
        currentRideState.value != RideState.inProgress) {
      processRideBegin(source: "Polling");
    } else if ((lowerStatus == 'finished' || lowerStatus == 'ended') &&
        currentRideState.value != RideState.finished &&
        currentRideState.value != RideState.preCheckReview) {
      Log.print(
          "🏁 Polling detected Finished. Releasing ride and moving to rating.");
      stopAllTimers();
      currentRideState.value = RideState.preCheckReview;
      tripFinishedFromDriver();
      _checkLastRideForReview();
    } else if (lowerStatus == 'cancelled' || lowerStatus == 'cancel') {
      processRideCancelledByDriver({'reason': 'Cancelled by driver'},
          source: "Polling");
    }
  }

  void stopDriverLocationPolling() {
    Log.print("🛑 Stopping Location Polling");
    _locationPollingTimer?.cancel();
    _locationPollingTimer = null;
  }

  Future<void> _addRideToWaitingTable() async {
    try {
      LatLng startLoc = mapEngine.polylineCoordinates.first;
      LatLng endLoc = mapEngine.polylineCoordinates.last;
      await CRUD().post(link: AppLink.addWaitingRide, payload: {
        'id': rideId.toString(),
        "start_location": '${startLoc.latitude},${startLoc.longitude}',
        "end_location": '${endLoc.latitude},${endLoc.longitude}',
        "date": DateTime.now().toString(),
        "time": DateTime.now().toString(),
        "price": double.parse(totalPassenger.toString()).toStringAsFixed(2),
        'passenger_id': box.read(BoxName.passengerID).toString(),
        'status': 'waiting',
        'carType': box.read(BoxName.carType),
        'passengerRate': passengerRate.toStringAsFixed(2),
        'price_for_passenger': totalME.toStringAsFixed(2),
        'distance': distance.toStringAsFixed(1),
        'duration': duration.toStringAsFixed(1),
        'payment_method':
            Get.find<PaymentController>().isWalletChecked ? 'wallet' : 'cash',
        "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(),
      });
      Log.print('[WaitingTable] Ride $rideId added to waiting_ride table.');
    } catch (e) {
      Log.print('Error adding ride to waiting_ride table: $e');
    }
  }

  double totalME = 0;
  double passengerRate = 5;
  double comfortPrice = 45;
  double speedPrice = 40;
  double mashwariPrice = 40;
  double familyPrice = 55;
  double deliveryPrice = 1.2;

  Future selectDriverAndCarForMishwariTrip() async {
    try {
      // Logic for mishwari trip driver selection
    } catch (e) {
      Log.print("Error Mishwari select: $e");
    }
  }

  List driversForMishwari = [];
  final Rx<DateTime> selectedDateTime = DateTime.now().obs;

  void updateDateTime(DateTime newDateTime) {
    selectedDateTime.value = newDateTime;
  }

  Future mishwariOption() async {
    isLoading = true;
    update();
    await selectDriverAndCarForMishwariTrip();
    Future.delayed(Duration.zero);
    isLoading = false;
    update();
    Get.to(() => CupertinoDriverListWidget());
  }

  bool isLoading = false;
  var driverIdVip = '';

  Future<void> saveTripData(
      Map<String, dynamic> driver, DateTime tripDateTime) async {
    try {
      LatLng startLoc = mapEngine.polylineCoordinates.first;
      Map<String, dynamic> tripData = {
        'id': driver['driver_id'].toString(),
        'phone': driver['phone'],
        'gender': driver['gender'],
        'name': driver['NAME'],
        'name_english': driver['name_english'],
        'address': driver['address'],
        'religion': driver['religion'] ?? 'UnKnown',
        'age': driver['age'].toString(),
        'education': driver['education'] ?? 'UnKnown',
        'license_type': driver['license_type'] ?? 'UnKnown',
        'national_number': driver['national_number'] ?? 'UnKnown',
        'car_plate': driver['car_plate'],
        'make': driver['make'],
        'model': driver['model'],
        'year': driver['year'].toString(),
        'color': driver['color'],
        'color_hex': driver['color_hex'],
        'displacement': driver['displacement'],
        'fuel': driver['fuel'],
        'token': driver['token'],
        'rating': driver['rating'].toString(),
        'countRide': driver['ride_count'].toString(),
        'passengerId': box.read(BoxName.passengerID),
        'timeSelected': tripDateTime.toIso8601String(),
        'status': 'pending',
        'startNameAddress': startNameAddress.toString(),
        'locationCoordinate': '${startLoc.latitude},${startLoc.longitude}',
      };
      Log.print('tripData: $tripData');

      var response =
          await CRUD().post(link: AppLink.addMishwari, payload: tripData);

      if (response != 'failure') {
        var id = response['message']['id'].toString();
        await CRUD()
            .post(link: '${AppLink.server}/ride/rides/add.php', payload: {
          "start_location": '${startLoc.latitude},${startLoc.longitude}',
          "end_location": '${startLoc.latitude},${startLoc.longitude}',
          "date": DateTime.now().toString(),
          "time": DateTime.now().toString(),
          "endtime": DateTime.now().add(const Duration(hours: 2)).toString(),
          "price": '50',
          "passenger_id": box.read(BoxName.passengerID).toString(),
          "driver_id": driver['driver_id'].toString(),
          "status": "waiting",
          'carType': 'vip',
          "price_for_driver": '50',
          "price_for_passenger": '50',
          "distance": '20',
          "paymentMethod": 'cash',
        }).then((value) {
          if (value is String) {
            final parsedValue = jsonDecode(value);
            rideId = parsedValue['message'];
          } else if (value is Map) {
            rideId = value['message'];
          }
        });

        driverIdVip = driver['driver_id'].toString();
        driverId = driver['driver_id'].toString();

        DateTime timeSelected = DateTime.parse(tripDateTime.toIso8601String());
        Get.find<NotificationController>().scheduleNotificationsForTimeSelected(
            "Your trip is scheduled".tr,
            "Don't forget your ride!".tr,
            "tone1",
            timeSelected);

        await NotificationService.sendNotification(
          category: 'OrderVIP',
          target: driver['token'].toString(),
          title: 'OrderVIP'.tr,
          body: '$rideId - VIP Trip',
          isTopic: false,
          tone: 'tone1',
          driverList: [
            id,
            rideId,
            driver['id'],
            passengerLocation.latitude.toString(),
            startNameAddress.toString(),
            passengerLocation.longitude.toString(),
            (box.read(BoxName.name).toString().split(' ')[0]).toString(),
            box.read(BoxName.passengerID).toString(),
            box.read(BoxName.phone).toString(),
            box.read(BoxName.email).toString(),
            box.read(BoxName.passengerPhotoUrl).toString(),
            box.read(BoxName.tokenFCM).toString(),
            (driver['token'].toString()),
          ],
        );
        if (response['message'] == "Trip updated successfully") {
          mySnackbarSuccess("Trip updated successfully".tr);
          await NotificationService.sendNotification(
            category: 'Order VIP Canceld',
            target: response['previous_driver_token'].toString(),
            title: 'Order VIP Canceld'.tr,
            body: 'Passenger cancel order'.tr,
            isTopic: false,
            tone: 'cancel',
            driverList: [],
          );
        }
        isBottomSheetShown = false;
        update();
        Get.to(() => VipWaittingPage());
      } else {
        throw Exception('Failed to save trip');
      }
    } catch (e) {
      Get.snackbar('Error'.tr, 'Failed to book trip: $e'.tr,
          backgroundColor: AppColor.redColor);
    }
  }

  Future<void> cancelVip(String token, tripId) async {
    var res = await CRUD()
        .post(link: AppLink.cancelMishwari, payload: {'id': tripId});
    if (res != 'failure') {
      Get.back();
      mySnackbarSuccess('You canceled VIP trip'.tr);
    }
  }

  void sendToDriverAgain(String token) {
    NotificationService.sendNotification(
      category: 'Order VIP Canceld',
      target: token.toString(),
      title: 'Order VIP Canceld'.tr,
      body: 'Passenger cancel order'.tr,
      isTopic: false,
      tone: 'cancel',
      driverList: [],
    );
  }

  Set<String> notifiedDrivers = {};

  Future<void> processDriverArrival(String source) async {
    if (currentRideState.value == RideState.driverArrived ||
        _isArrivalProcessed) {
      Log.print("✋ Ignored Arrival from $source. Already processed.");
      return;
    }

    _isArrivalProcessed = true;
    Log.print("🚖 Driver Arrived via $source! Processing...");

    currentRideState.value = RideState.driverArrived;
    statusRide = 'Arrived';
    await RideLiveNotification.showDriverArrived(driverName);

    uiInteractions.driverArrivePassengerDialoge();
    startTimerDriverWaitPassenger5Minute();

    if (mapEngine.polylineCoordinates.isNotEmpty) {
      mapEngine.playRouteAnimation(
          mapEngine.polylineCoordinates, mapEngine.lastComputedBounds);
    }

    if (driverCarsLocationToPassengerAfterApplied.isNotEmpty &&
        myDestination.latitude != 0 &&
        myDestination.longitude != 0) {
      await calculateDriverToPassengerRoute(
        driverCarsLocationToPassengerAfterApplied.last,
        myDestination,
        isBeginPhase: true,
      );
    }

    update();
  }

  Future<void> processRideFinished(List<dynamic> driverList,
      {String source = "Unknown"}) async {
    if (currentRideState.value == RideState.finished || _isFinishProcessed) {
      Log.print("✋ Ignored Finish Request from $source. Already Finished.");
      return;
    }

    _isFinishProcessed = true;
    Log.print("🏁 Ride Finished via $source.");

    currentRideState.value = RideState.finished;
    mapSocket.disposeRideSocket();
    stopDriverLocationPolling();
    if (Get.isRegistered<AudioRecorderController>()) {
      Get.find<AudioRecorderController>().stopRecording();
    }

    if (Get.isDialogOpen == true) Get.back();

    NotificationController().showNotification(
      'Alert'.tr,
      "Please make sure not to leave any personal belongings in the car.".tr,
      'tone1',
    );
    IosLiveActivityService.endRideActivity();
    PipService.disablePip();
    await RideLiveNotification.cancel();

    if (driverList.length >= 4) {
      String price = driverList[3].toString();
      Get.offAll(() => RateDriverFromPassenger(), arguments: {
        'driverId': driverList[0].toString(),
        'rideId': driverList[1].toString(),
        'price': price
      });
    }
  }

  Future<void> processRideCancelledByDriver(dynamic data,
      {String source = "Unknown"}) async {
    if (_isCancelProcessed) return;

    _isCancelProcessed = true;
    stopAllTimers();
    if (Get.isDialogOpen == true) Get.back();
    await RideLiveNotification.cancel();
    IosLiveActivityService.endRideActivity();
    PipService.disablePip();

    Get.defaultDialog(
      title: "Sorry 😔".tr,
      titleStyle:
          const TextStyle(color: Colors.red, fontWeight: FontWeight.bold),
      barrierDismissible: false,
      content: Column(
        children: [
          const Icon(Icons.cancel_presentation,
              size: 50, color: Colors.redAccent),
          const SizedBox(height: 10),
          Text(
            "The driver cancelled the trip for an emergency reason.\nDo you want to search for another driver immediately?"
                .tr,
            textAlign: TextAlign.center,
          ),
        ],
      ),
      actions: [
        TextButton(
          onPressed: () {
            Get.back();
            handleNoDriverFound();
          },
          child: Text("Cancel Trip".tr,
              style: const TextStyle(color: Colors.grey)),
        ),
        ElevatedButton.icon(
          style:
              ElevatedButton.styleFrom(backgroundColor: AppColor.primaryColor),
          icon: const Icon(Icons.refresh, color: Colors.white),
          label: Text("Search for another driver".tr,
              style: const TextStyle(color: Colors.white)),
          onPressed: () {
            Get.back();
            retrySearchForDrivers();
          },
        ),
      ],
    );
  }

  void showNoDriverDialog() {
    Get.defaultDialog(
      title: "No Drivers Found".tr,
      middleText:
          "Sorry, there are no cars available of this type right now.".tr,
      textConfirm: "Refresh Map".tr,
      textCancel: "Cancel".tr,
      confirmTextColor: Colors.white,
      onConfirm: () {
        Get.back();
        restCounter();
        stopAllTimers();
        clearControllersAndGoHome();
      },
    );
  }

  Future<void>
      calculateDistanceBetweenPassengerAndDriverBeforeCancelRide() async {
    await getDriverCarsLocationToPassengerAfterApplied();
    double dist = Geolocator.distanceBetween(
      passengerLocation.latitude,
      passengerLocation.longitude,
      driverCarsLocationToPassengerAfterApplied.last.latitude,
      driverCarsLocationToPassengerAfterApplied.last.longitude,
    );
    if (dist > 500) {
      isCancelRidePageShown = true;
      update();
    } else {
      Get.defaultDialog(
        barrierDismissible: false,
        title: 'The Driver Will be in your location soon .'.tr,
        middleText: 'The distance less than 500 meter.'.tr,
        confirm: Column(
          children: [
            MyElevatedButton(
              kolor: AppColor.greenColor,
              title: 'Ok'.tr,
              onPressed: () {
                Get.back();
              },
            ),
            MyElevatedButton(
              kolor: AppColor.redColor,
              title: 'No, I want to cancel this trip'.tr,
              onPressed: () {
                Get.back();
                MyDialog().getDialog(
                  'Attention'.tr,
                  'You will be charged for the cost of the driver coming to your location.'
                      .tr,
                  () async {
                    Get.back();
                    Get.find<PaymentController>()
                        .payToDriverForCancelAfterAppliedAndHeNearYou(rideId);
                  },
                );
              },
            ),
          ],
        ),
      );
    }
  }

  Future<void> cancelRideAfterRejectFromAll() async {
    locSearch.clearPlacesDestination();
    mapEngine.clearPolyline();
    data = [];
    await CRUD().post(
        link: "${AppLink.server}/ride/rides/cancel_ride_by_passenger.php",
        payload: {
          "ride_id": rideId.toString(),
          "reason": 'notApplyFromAnyDriver'
        });

    rideConfirm = false;
    statusRide = 'Cancel';
    isSearchingWindow = false;
    shouldFetch = false;
    isPassengerChosen = false;
    isCashConfirmPageShown = false;
    isCashSelectedBeforeConfirmRide = false;
    timeToPassengerFromDriverAfterApplied = 0;
    mapEngine.changeCancelRidePageShow();
    remainingTime = 0;
    update();
  }

  void selectReason0(int index, String note) {
    selectedReason = index;
    cancelNote = note;
    update();
  }

  int selectedReasonIndex = -1;
  String selectedReasonText = "";
  TextEditingController otherReasonController = TextEditingController();

  void selectReason(int index, String reason) {
    selectedReasonIndex = index;
    selectedReasonText = reason;
    update();
  }

  List data = [];

  void restCounter() {
    locSearch.clearPlacesDestination();
    mapEngine.clearPolyline();
    data = [];
    rideConfirm = false;
    shouldFetch = false;
    timeToPassengerFromDriverAfterApplied = 0;
    update();
  }

  Future<void> _checkAndRefreshMapStyle() async {
    try {
      final String styleJson = await rootBundle.loadString('assets/style.json');
      final Map<String, dynamic> decoded = json.decode(styleJson);
      final String? currentVersion =
          decoded['metadata'] != null ? decoded['metadata']['version'] : null;

      if (currentVersion == null) return;
      final String lastVersion = box.read(BoxName.styleVersion) ?? "0.0.0";

      if (currentVersion != lastVersion) {
        Log.print(
            "♻️ Map Style Version mismatch ($lastVersion -> $currentVersion). Purging offline cache...");
        await OfflineMapService.instance.clearCache();
        await Future.delayed(const Duration(milliseconds: 500));
        box.write(BoxName.styleVersion, currentVersion);
        Log.print("✅ Style Version updated to $currentVersion");
      }
    } catch (e) {
      Log.print("⚠️ Style version check failed: $e");
    }
  }

  void reinit() {
    if (currentRideState.value != RideState.noRide &&
        currentRideState.value != RideState.cancelled) {
      Log.print('ℹ️ reinit() skipped: ride is active');
      return;
    }
    Log.print('🔄 reinit() calling resetAllMapStates and restarting timers...');
    resetAllMapStates();
    stopAllTimers();
    currentRideState.value = RideState.noRide;

    // Restart location search
    locSearch.getLocation();

    // Restart lifecycle timers & stages
    getLocationArea(passengerLocation.latitude, passengerLocation.longitude);
    unawaited(_stagePricingAndState());
    unawaited(_stageNiceToHave());
    startMasterTimer();
  }

  void resetAllMapStates() {
    Log.print('🧹 Resetting all map states to prevent sticky location bug');
    locSearch.clearPlacesDestination();
    locSearch.clearPlacesStart();
    locSearch.waypoints.clear();
    locSearch.clearAllMenuWaypoints();
    if (Get.isRegistered<WayPointController>()) {
      Get.find<WayPointController>().reset();
    }

    // Call reset on mapEngine which handles clearing markers, polylines, animation timers and UI states
    mapEngine.reset();
    data = [];

    locSearch.passengerStartLocationFromMap = false;
    locSearch.startLocationFromMap = false;
    isPickerShown = false;
    locSearch.workLocationFromMap = false;
    locSearch.homeLocationFromMap = false;
    isAnotherOreder = false;
    isWhatsAppOrder = false;

    myDestination = passengerLocation;
    locSearch.hintTextDestinationPoint = 'Select your destination'.tr;

    locSearch.placeDestinationController.clear();
    locSearch.placeStartController.clear();

    rideConfirm = false;
    shouldFetch = true; // reset to true by default for next ride search polling
    isDrawingRoute = false;
    isLoading = false;

    // Reset RideLifecycleController specific search and lifecycle states
    isSearchingWindow = false;
    currentRideState.value = RideState.noRide;
    statusRide = 'wait';
    statusRideVip = 'wait';
    statusRideFromStart = false;
    isDriverInPassengerWay = false;
    isDriverArrivePassenger = false;
    _isArrivalProcessed = false;
    _isFinishProcessed = false;
    _isCancelProcessed = false;
    _isAcceptanceProcessed = false;
    _isRatingScreenOpen = false;
    _isRecalculatingRoute = false;
    _isRideStartedProcessed = false;
    _isDriverAppliedLogicExecuted = false;
    _isDriverArrivedLogicExecuted = false;
    _isRideBeginLogicExecuted = false;
    _currentDriverRoutePoints = [];
    _currentDriverRouteDistanceMeters = 0.0;
    _currentDriverRouteDurationSeconds = 0;
    _driverEtaUpdatedAt = null;
    _driverEtaSecondsAtUpdate = 0;
    _driverEtaCountdownTicks = 0;
    _routeHeadingMismatchCount = 0;
    distanceByPassenger = '';
    durationToPassenger = 0;
    stringRemainingTimeToPassenger = '';

    update();
  }

  void _handleFatalError(String title, String message) {
    if (Get.isBottomSheetOpen == true || Get.isDialogOpen == true) {
      Get.back();
    }
    if (Get.isSnackbarOpen) Get.closeCurrentSnackbar();

    isDrawingRoute = false;
    isLoading = false;
    update();

    Get.defaultDialog(
      title: title,
      titleStyle: AppStyle.title.copyWith(color: AppColor.redColor),
      middleText: message,
      middleTextStyle: AppStyle.subtitle,
      barrierDismissible: false,
      confirm: MyElevatedButton(
        title: "Close".tr,
        kolor: AppColor.redColor,
        onPressed: () {
          Get.back();
          clearControllersAndGoHome();
        },
      ),
    );
  }

  String shortenAddress(String fullAddress) {
    List<String> parts = fullAddress.split('،');
    parts = parts.map((part) => part.trim()).toList();
    parts = parts.where((part) => part.isNotEmpty).toList();

    String shortAddress = '';
    if (parts.isNotEmpty) {
      shortAddress += parts[0];
    }
    if (parts.length > 2) {
      shortAddress += '، ${parts[2]}';
    } else if (parts.length > 1) {
      shortAddress += '، ${parts[1]}';
    }

    if (parts.length > 1) {
      shortAddress += '، ${parts.last}';
    }

    shortAddress = shortAddress
        .split('،')
        .where((part) => !RegExp(r'^[0-9 ]+$').hasMatch(part.trim()))
        .join('er');

    bool isEnglish =
        RegExp(r'^[a-zA-Z0-9 ]+$').hasMatch(shortAddress.replaceAll('،', ''));

    if (isEnglish) {
      List<String> englishParts = shortAddress.split('،');
      if (englishParts.length > 2) {
        shortAddress =
            '${englishParts[0]}، ${englishParts[1]}، ${englishParts.last}';
      } else if (englishParts.length > 1) {
        shortAddress = '${englishParts[0]}، ${englishParts.last}';
      }
    }
    return shortAddress;
  }

  double distanceOfDestination = 0;
  bool haveSteps = false;

  Future<void> getMapPoints(
      String originSteps, String destinationSteps, int index) async {
    isWayPointStopsSheetUtilGetMap = false;
    await nearbyDrivers.getCarsLocationByPassengerAndReloadMarker();
    update();

    var url =
        ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destinationSteps&origin=$originSteps&key=${AK.mapAPIKEY}');
    var response = await CRUD().getGoogleApi(link: url, payload: {});

    data = response['routes'][0]['legs'];

    int durationToRide0 = data[0]['duration']['value'];
    durationToRide = durationToRide + durationToRide0;
    distance = distanceOfDestination + (data[0]['distance']['value']) / 1000;

    update();
    final String pointsString =
        response['routes'][0]["overview_polyline"]["points"];

    List<LatLng> decodedPoints =
        await compute(decodePolylineIsolate, pointsString);
    for (int i = 0; i < decodedPoints.length; i++) {
      mapEngine.polylineCoordinates.add(decodedPoints[i]);
    }

    if (polyLines.isEmpty) {
      var polyline = Polyline(
        polylineId: PolylineId('route_$index'),
        points: locSearch.polylineCoordinatesPointsAll[index],
        width: 6,
        color: const Color(0xFF2196F3),
      );

      polyLines = {...polyLines, polyline};
      rideConfirm = false;
      update();
    }
  }

  void updateCameraForDistanceAfterGetMap() {
    LatLng coord1 = LatLng(
        double.parse(locSearch.coordinatesWithoutEmpty.first.split(',')[0]),
        double.parse(locSearch.coordinatesWithoutEmpty.first.split(',')[1]));

    LatLng coord2 = LatLng(
        double.parse(locSearch.coordinatesWithoutEmpty.last.split(',')[0]),
        double.parse(locSearch.coordinatesWithoutEmpty.last.split(',')[1]));

    LatLng northeastBound;
    LatLng southwestBound;

    if (coord1.latitude > coord2.latitude) {
      northeastBound = coord1;
      southwestBound = coord2;
    } else {
      northeastBound = coord2;
      southwestBound = coord1;
    }

    LatLngBounds boundsObj =
        LatLngBounds(northeast: northeastBound, southwest: southwestBound);

    final latDiff = (northeastBound.latitude - southwestBound.latitude).abs();
    final lngDiff = (northeastBound.longitude - southwestBound.longitude).abs();

    if (latDiff < 0.0001 || lngDiff < 0.0001) {
      final center = LatLng(
        (northeastBound.latitude + southwestBound.latitude) / 2,
        (northeastBound.longitude + southwestBound.longitude) / 2,
      );
      mapController!.animateCamera(CameraUpdate.newLatLngZoom(center, 17));
    } else {
      try {
        var cameraUpdate = CameraUpdate.newLatLngBounds(boundsObj,
            left: 180, top: 180, right: 180, bottom: 180);
        mapController!.animateCamera(cameraUpdate);
      } catch (e) {
        final center = LatLng(
          (northeastBound.latitude + southwestBound.latitude) / 2,
          (northeastBound.longitude + southwestBound.longitude) / 2,
        );
        mapController!.animateCamera(CameraUpdate.newLatLngZoom(center, 17));
      }
    }
    update();
  }

  int selectedIndex = -1;
  void selectCarFromList(int index) {
    selectedIndex = index;
    carTypes.forEach((element) => element.isSelected = false);
    carTypes[index].isSelected = true;
    update();
  }

  Future<void> showBottomSheet1() async {
    await bottomSheet();
    isBottomSheetShown = true;
    heightBottomSheetShown = 250;
    update();
  }

  double calculateAngleBetweenLocations(LatLng start, LatLng end) {
    double startLat = start.latitude * pi / 180;
    double startLon = start.longitude * pi / 180;
    double endLat = end.latitude * pi / 180;
    double endLon = end.longitude * pi / 180;

    double dLon = endLon - startLon;

    double y = sin(dLon) * cos(endLat);
    double x =
        cos(startLat) * sin(endLat) - sin(startLat) * cos(endLat) * cos(dLon);

    double angle = atan2(y, x);
    double angleDegrees = angle * 180 / pi;

    return angleDegrees;
  }

  get dataCarsLocationByPassenger {
    return nearbyDrivers.carsLocationByPassenger;
  }

  set dataCarsLocationByPassenger(var val) {
    nearbyDrivers.carsLocationByPassenger = val;
  }

  double calculateBearing(double lat1, double lon1, double lat2, double lon2) {
    return nearbyDrivers.calculateBearing(lat1, lon1, lat2, lon2);
  }

  void analyzeBehavior(Position currentPosition, List<LatLng> routePoints) {
    nearbyDrivers.analyzeBehavior(currentPosition, routePoints);
  }

  void detectStops(Position currentPosition) {
    nearbyDrivers.detectStops(currentPosition);
  }

  Future<void> getDirectionMap(String origin, String destination,
      [List<String> waypoints = const [], int attemptCount = 0]) async {
    if (attemptCount == 0) {
      isDrawingRoute = true;
      update();
      if (isDrawingRoute) showDrawingBottomSheet();

      await nearbyDrivers.getCarsLocationByPassengerAndReloadMarker();
    }

    if (origin.isEmpty) {
      origin = '${passengerLocation.latitude},${passengerLocation.longitude}';
    }

    var coordDestination = destination.split(',');
    double latDest = double.parse(coordDestination[0]);
    double lngDest = double.parse(coordDestination[1]);
    myDestination = LatLng(latDest, lngDest);

    Uri uri;

    var originCoords = origin.split(',');
    final Map<String, String> queryParams = {
      'fromLat': originCoords[0].trim(),
      'fromLng': originCoords[1].trim(),
      'toLat': latDest.toString(),
      'toLng': lngDest.toString(),
    };

    for (int i = 0; i < activeMenuWaypointCount; i++) {
      final wp = menuWaypoints[i];
      if (wp != null) {
        queryParams['stop${i + 1}Lat'] = wp.latitude.toString();
        queryParams['stop${i + 1}Lng'] = wp.longitude.toString();
      }
    }

    uri = Uri.parse(AppLink.mapSaasRoute).replace(queryParameters: queryParams);

    Log.print(
        'Requesting Route URI (SaaS, Attempt: ${attemptCount + 1}): $uri');

    http.Response response;
    Map<String, dynamic> responseData;

    try {
      response = await http.get(uri, headers: {
        'x-api-key': Env.mapSaasKey,
      }).timeout(const Duration(seconds: 20));

      responseData = json.decode(response.body);

      bool isRequestValid = response.statusCode == 200;

      if (!isRequestValid) {
        if (attemptCount < 2) {
          await _retryProcess(origin, destination, waypoints, attemptCount);
          return;
        }
        _handleFatalError(
            "Server Error".tr, "Connection failed. Please try again.".tr);
        return;
      }

      double apiDistanceMeters;
      String pointsString;
      dynamic routeData;

      apiDistanceMeters = (responseData['distance'] as num).toDouble();
      pointsString = responseData['points'] ?? "";
      routeData = responseData;

      var origCoords = origin.split(',');
      double startLat = double.parse(origCoords[0]);
      double startLng = double.parse(origCoords[1]);

      double aerialDistance =
          Geolocator.distanceBetween(startLat, startLng, latDest, lngDest);

      if (apiDistanceMeters < 50.0 && aerialDistance > 200.0) {
        Log.print(
            "⚠️ Suspicious Route detected! Server: $apiDistanceMeters m | Aerial: $aerialDistance m");

        if (attemptCount < 2) {
          Log.print("🔄 Retrying request (Attempt ${attemptCount + 2})...");
          await Future.delayed(const Duration(seconds: 1));
          await getDirectionMap(
              origin, destination, waypoints, attemptCount + 1);
          return;
        } else {
          Log.print("❌ All retries failed. Calculating Route is impossible.");
          _handleFatalError(
              "Route Not Found".tr,
              "We couldn't find a valid route to this destination. Please try selecting a different point."
                  .tr);
          return;
        }
      }

      box.remove(BoxName.tripData);
      box.write(BoxName.tripData, routeData);

      durationToRide =
          ((routeData['duration'] as num) * kDurationScalar).toInt();
      double distanceOfTrip = apiDistanceMeters / 1000.0;
      distance = distanceOfTrip;

      data = routeData['legs'] != null && routeData['legs'].isNotEmpty
          ? (routeData['legs'][0]['steps'] ?? [])
          : [];

      List<LatLng> decodedPoints = [];
      if (pointsString.isNotEmpty) {
        decodedPoints = await compute(decodePolylineIsolate, pointsString);
      }

      if (decodedPoints.isEmpty) {
        _handleFatalError("Map Error".tr, "Received empty route data.".tr);
        return;
      }

      mapEngine.polylineCoordinates.clear();
      mapEngine.polylineCoordinates.addAll(decodedPoints);

      final LatLng startLoc = mapEngine.polylineCoordinates.first;
      final LatLng endLoc = mapEngine.polylineCoordinates.last;

      startNameAddress = responseData['startName'] ?? 'Start Point'.tr;
      endNameAddress = responseData['endName'] ?? 'Destination'.tr;
      Log.print('📍 ROUTE START: $startNameAddress');
      Log.print('📍 ROUTE END:   $endNameAddress');

      if (responseData['bbox'] != null) {
        List<dynamic> bbox = responseData['bbox'];
        if (bbox.length == 4) {
          mapEngine.lastComputedBounds = LatLngBounds(
            southwest: LatLng(bbox[1], bbox[0]),
            northeast: LatLng(bbox[3], bbox[2]),
          );
        }
      } else {
        double? minLat, maxLat, minLng, maxLng;
        for (LatLng point in mapEngine.polylineCoordinates) {
          minLat =
              minLat == null ? point.latitude : min(minLat, point.latitude);
          maxLat =
              maxLat == null ? point.latitude : max(maxLat, point.latitude);
          minLng =
              minLng == null ? point.longitude : min(minLng, point.longitude);
          maxLng =
              maxLng == null ? point.longitude : max(maxLng, point.longitude);
        }
        if (minLat != null) {
          mapEngine.lastComputedBounds = LatLngBounds(
              northeast: LatLng(maxLat!, maxLng!),
              southwest: LatLng(minLat!, minLng!));
        }
      }

      if (isDrawingRoute) {
        Log.print('🔔 Finalizing route drawing state');
        isDrawingRoute = false;
        isLoading = false;
        update();
      }

      durationToAdd = Duration(seconds: durationToRide);
      hours = durationToAdd.inHours;
      minutes = (durationToAdd.inMinutes % 60).round();

      markers = {
        Marker(
          markerId: const MarkerId('start'),
          position: startLoc,
          icon: InlqBitmap.fromStyleImage('orange_marker'),
          infoWindow: const InfoWindow(title: 'A'),
          anchor: const Offset(0.5, 1.0),
        ),
        Marker(
          markerId: const MarkerId('end'),
          position: endLoc,
          icon: InlqBitmap.fromStyleImage('violet_marker'),
          infoWindow: const InfoWindow(title: 'B'),
          anchor: const Offset(0.5, 1.0),
        ),
      };

      for (int i = 0; i < activeMenuWaypointCount; i++) {
        final wp = menuWaypoints[i];
        if (wp != null) {
          final bool isFirstWaypoint = i == 0;
          markers.add(Marker(
            markerId: MarkerId('waypoint_$i'),
            position: wp,
            icon: InlqBitmap.fromStyleImage(
                isFirstWaypoint ? 'orange_marker' : 'violet_marker'),
            infoWindow:
                InfoWindow(title: isFirstWaypoint ? 'Stop 1' : 'Stop 2'),
            anchor: const Offset(0.5, 1.0),
          ));
        }
      }

      if (polyLines.isNotEmpty) mapEngine.clearPolyline();

      rideConfirm = false;
      isMarkersShown = true;
      update();

      await bottomSheet();

      await mapEngine.playRouteAnimation(
          mapEngine.polylineCoordinates, mapEngine.lastComputedBounds);
    } catch (e, stackTrace) {
      if (isDrawingRoute) {
        isDrawingRoute = false;
        isLoading = false;
        update();
      }

      Log.print('🚨 CRITICAL ERROR IN getDirectionMap: $e');
      Log.print('🚨 STACKTRACE: $stackTrace');

      if (attemptCount < 2) {
        await _retryProcess(origin, destination, waypoints, attemptCount);
      } else {
        _handleFatalError("Connection Error".tr,
            "Please check your internet and try again.".tr);
      }
    }
  }

  Future<void> _retryProcess(String origin, String dest, List<String> waypoints,
      int currentAttempt) async {
    Log.print(
        "🔄 Exception or Error caught. Retrying in 1s... (Attempt ${currentAttempt + 1})");
    await Future.delayed(const Duration(seconds: 1));
    getDirectionMap(origin, dest, waypoints, currentAttempt + 1);
  }

  bool _isUsingFallback = false;

  void _startPollingFallback() {
    if (_isUsingFallback) return;

    Log.print('🔄 Starting Polling Fallback Mode');
    _isUsingFallback = true;

    startMasterTimer();
  }

  Future<void> _restorePolyline(String polylineString) async {
    try {
      List<LatLng> points =
          await compute(decodePolylineIsolate, polylineString);

      mapEngine.polylineCoordinates.clear();
      mapEngine.polylineCoordinates.addAll(points);

      mapEngine.clearPolyline();
      mapEngine.polyLines = {
        ...mapEngine.polyLines,
        Polyline(
          polylineId: const PolylineId('route_direct'),
          points: mapEngine.polylineCoordinates,
          color: const Color(0xFF2196F3),
          width: 6,
        )
      };

      update();
    } catch (e) {
      Log.print('Error restoring polyline: $e');
    }
  }

  Future<void> processRideAcceptance(
      {Map<String, dynamic>? driverData, required String source}) async {
    if (_isAcceptanceProcessed ||
        currentRideState.value == RideState.driverApplied ||
        currentRideState.value == RideState.driverArrived ||
        currentRideState.value == RideState.inProgress) {
      Log.print("✋ Ignored Acceptance from $source. Already processed.");
      return;
    }

    _rideAcceptedViaSource = source;

    _isAcceptanceProcessed = true;
    _isDriverAppliedLogicExecuted = true;
    Log.print("🚀 Winner: $source triggered acceptance! Processing...");

    _masterTimer?.cancel();

    currentRideState.value = RideState.driverApplied;
    statusRide = 'Apply';
    isSearchingWindow = false;

    if (driverData != null && driverData.isNotEmpty) {
      Log.print("📥 Populating Data from $source payload...");
      _fillDriverDataLocally(driverData);
    } else {
      Log.print("⚠️ No Data in Payload. Fallback to API.");
      await getUpdatedRideForDriverApply(rideId);
    }

    await IosLiveActivityService.startRideActivity(
      rideId: rideId,
      driverName: driverName,
      carDetails: '$make • $carColor',
      etaText: stringRemainingTimeToPassenger,
      progress: 0.0,
    );

    _showRideStartNotifications();
    final etaText = stringRemainingTimeToPassenger;
    final carInfo = '$make • $model • $licensePlate';

    await RideLiveNotification.showDriverOnWay(
      driverName: driverName,
      etaText: etaText,
      carInfo: carInfo,
    );

    update();

    await getDriverCarsLocationToPassengerAfterApplied();
    _startSocketWatchdog();

    if (driverCarsLocationToPassengerAfterApplied.isNotEmpty) {
      LatLng driverPos = driverCarsLocationToPassengerAfterApplied.last;

      await calculateDriverToPassengerRoute(driverPos, passengerLocation);

      startTimerFromDriverToPassengerAfterApplied();
    }

    PipService.enablePip();

    if (source == "Socket" && mapSocket.isSocketConnected) {
      Log.print(
          "🧠 Smart Mode: Socket accepted ride. Skipping polling, relying on WebSocket.");
    } else {
      Log.print("🔄 Fallback Mode: $source accepted ride. Starting polling.");
      _startDriverLocationPollingWithTimer();
    }
  }

  void _fillDriverDataLocally(Map<String, dynamic> data) {
    try {
      driverId = data['driver_id']?.toString() ?? '';
      driverPhone = data['phone']?.toString() ?? '';

      String fName = (data['first_name'] ?? data['driver_first_name'] ?? '')
          .toString()
          .trim();
      String lName = (data['last_name'] ?? data['driver_last_name'] ?? '')
          .toString()
          .trim();
      final socketDriverName =
          (data['driverName'] ?? data['driver_name'] ?? '').toString().trim();
      driverName = socketDriverName.isNotEmpty
          ? socketDriverName
          : [fName, lName].where((part) => part.isNotEmpty).join(' ');

      make = data['make']?.toString() ?? '';
      model = data['model']?.toString() ?? '';
      gender = data['gender']?.toString() ?? '';
      carColor = data['color']?.toString() ?? '';
      colorHex = data['color_hex']?.toString() ?? '';
      licensePlate = data['car_plate']?.toString() ?? '';
      carYear = data['year']?.toString() ?? '';

      // المحاولة الفورية لرسم السائق إذا توفرت الإحداثيات في البيانات
      double lat = double.tryParse(
              data['latitude']?.toString() ?? data['lat']?.toString() ?? '0') ??
          0;
      double lng = double.tryParse(data['longitude']?.toString() ??
              data['lng']?.toString() ??
              '0') ??
          0;
      double heading = double.tryParse(data['heading']?.toString() ?? '0') ?? 0;

      if (lat != 0 && lng != 0) {
        LatLng initialPos = LatLng(lat, lng);
        if (driverCarsLocationToPassengerAfterApplied.isEmpty) {
          driverCarsLocationToPassengerAfterApplied.add(initialPos);
        } else {
          driverCarsLocationToPassengerAfterApplied[0] = initialPos;
        }
        // تحديث الماركر فوراً لضمان ظهوره بشكل موثوق
        updateDriverMarker(initialPos, heading);
      }

      driverRate = data['ratingDriver']?.toString() ?? '5.0';
      driverRatingCount = data['ratingCount']?.toString() ?? '0';
      driverCompletedRides = data['completedRides']?.toString() ?? '0';
      driverTier = data['driverTier']?.toString() ?? 'Verified driver';
      driverToken = data['token']?.toString() ?? '';

      update();
    } catch (e) {
      Log.print("Error parsing socket driver data: $e");
    }
  }

  Future<void> cancelRide() async {
    if (selectedReasonIndex == -1) {
      Get.snackbar(
        'Attention'.tr,
        'Please select a reason first'.tr,
        snackPosition: SnackPosition.BOTTOM,
        backgroundColor: Colors.orange,
        colorText: Colors.white,
      );
      return;
    }

    String finalReason = selectedReasonText;
    if (finalReason == "Other".tr) {
      if (otherReasonController.text.trim().isEmpty) {
        Get.snackbar("Attention".tr, "Please write the reason...".tr,
            backgroundColor: Colors.red, colorText: Colors.white);
        return;
      }
      finalReason = otherReasonController.text.trim();
    }

    Get.back();
    if (isCancelRidePageShown) {
      mapEngine.changeCancelRidePageShow();
    }

    resetAllMapStates();

    stopAllTimers();
    currentRideState.value = RideState.cancelled;
    await RideLiveNotification.cancel();
    IosLiveActivityService.endRideActivity();
    PipService.disablePip();

    if (rideId != 'yet' && rideId != null) {
      Log.print(
          '📡 Sending Cancel Request to Server with Reason: $finalReason');

      try {
        await CRUD().post(
          link: "${AppLink.server}/ride/rides/cancel_ride_by_passenger.php",
          payload: {
            "ride_id": rideId.toString(),
            "reason": finalReason,
            "driver_token": driverToken,
          },
        );
      } catch (e) {
        Log.print("Error cancelling on server: $e");
      }
    }

    clearControllersAndGoHome();
  }

  Future<String?> getAIKey(String key) async {
    var res =
        await CRUD().get(link: AppLink.getapiKey, payload: {"keyName": key});
    if (res != 'failure') {
      var d = jsonDecode(res)['message'];
      return d[key].toString();
    }
    return null;
  }

  Future<String> getRideStatus(String rideId) async {
    final response = await CRUD().get(
        link: "${AppLink.rideServerSide}/ride/rides/getRideStatus.php",
        payload: {'id': rideId});
    Log.print(response);
    Log.print('2176');
    return jsonDecode(response)['data'];
  }

  void handleActiveRideOnStartup(dynamic data) {
    try {
      if (data == null || data['has_active_ride'] != true) {
        Log.print('[Startup] No active ride');
        currentRideState.value = RideState.noRide;
        startMasterTimer();
        return;
      }

      Log.print('[Startup] ✅ Active ride found!');

      var rideData = data['ride'];
      rideId = rideData['ride_id'].toString();
      currentRideId = rideId;
      driverId = rideData['driver_id']?.toString() ?? '';

      String status = rideData['status']?.toString().toLowerCase() ?? '';

      if (status == 'waiting' || status == 'searching') {
        currentRideState.value = RideState.searching;
        isSearchingWindow = true;
      } else if (status == 'apply' || status == 'applied') {
        currentRideState.value = RideState.driverApplied;
        statusRide = 'Apply';

        mapSocket.socket.emit('subscribe_driver_location', {
          'ride_id': rideId,
          'driver_id': driverId,
        });

        if (rideData['driver_info'] != null) {
          var dInfo = rideData['driver_info'];
          passengerName = dInfo['first_name']?.toString() ?? '';
          driverPhone = dInfo['phone']?.toString() ?? '';
          model = dInfo['model']?.toString() ?? '';
          licensePlate = dInfo['license_plate']?.toString() ?? '';
        }
      } else if (status == 'arrived') {
        currentRideState.value = RideState.driverArrived;
        statusRide = 'Arrived';
        isDriverArrivePassenger = true;
      } else if (status == 'begin' || status == 'started') {
        currentRideState.value = RideState.inProgress;
        statusRide = 'Begin';
        rideTimerBegin = true;

        if (rideData['polyline'] != null) {
          _restorePolyline(rideData['polyline']);
        }

        rideIsBeginPassengerTimer();
      }

      update();
      startMasterTimer();
    } catch (e) {
      Log.print('[Startup] Error: $e');
      currentRideState.value = RideState.noRide;
      startMasterTimer();
    }
  }

  Future<void> handleNoDriverFound() async {
    stopAllTimers();
    await RideLiveNotification.cancel();
    IosLiveActivityService.endRideActivity();
    PipService.disablePip();
    _isCancelProcessed = false;
    currentRideState.value = RideState.noRide;
    resetAllMapStates();
    clearControllersAndGoHome();

    Get.defaultDialog(
      title: "We apologize 😔".tr,
      middleText: "No drivers found at the moment.\nPlease try again later.".tr,
      confirm: ElevatedButton(
        onPressed: () => Navigator.pop(Get.context!),
        child: Text("Ok".tr),
      ),
    );
  }

  bool isDriversDataValid() {
    return dataCarsLocationByPassenger != 'failure' &&
        dataCarsLocationByPassenger != null &&
        (dataCarsLocationByPassenger is Map) &&
        dataCarsLocationByPassenger.containsKey('message') &&
        dataCarsLocationByPassenger['message'] != null;
  }

  void retrySearchForDrivers() async {
    _isCancelProcessed = false;
    isSearchingWindow = true;
    currentRideState.value = RideState.searching;
    driversStatusForSearchWindow = 'Searching for nearby drivers...'.tr;
    update();

    try {
      Log.print("🔄 Retrying search for ride ID: $rideId");

      var payload = {
        "ride_id": rideId.toString(),
        "passenger_id": box.read(BoxName.passengerID).toString(),
        "passenger_name": box.read(BoxName.name).toString(),
        "passenger_phone": box.read(BoxName.phone).toString(),
        "passenger_email": box.read(BoxName.email).toString(),
        "passenger_token": box.read(BoxName.tokenFCM).toString(),
        "passenger_wallet": box.read(BoxName.passengerWalletTotal).toString(),
        "passenger_rating": "5.0",
        "start_lat": startLocation.latitude.toString(),
        "start_lng": startLocation.longitude.toString(),
        "end_lat": endLocation.latitude.toString(),
        "end_lng": endLocation.longitude.toString(),
        "start_name": startNameAddress,
        "end_name": endNameAddress,
        "distance": distance.toString(),
        "distance_text": distanceByPassenger,
        "duration_text": durationToPassenger.toString(),
        "price": totalPassenger.toString(),
        "price_for_driver": costForDriver.toString(),
        "car_type": box.read(BoxName.carType).toString(),
        "is_wallet": Get.find<PaymentController>().isWalletChecked.toString(),
        "has_steps": Get.find<WayPointController>().wayPoints.length > 1
            ? "true"
            : "false",
      };

      var response = await CRUD().post(
        link: "${AppLink.rideServerSide}/rides/retry_search_drivers.php",
        payload: payload,
      );

      if (response['status'] == 'success') {
        Log.print("✅ Search reset successfully.");
        startSearchingTimer();
      } else {
        Log.print("❌ Failed to reset search: $response");
        handleNoDriverFound();
      }
    } catch (e) {
      Log.print("❌ Exception in retrySearchForDrivers: $e");
      handleNoDriverFound();
    }
  }

  Future<void> startSearchingTimer() async {
    _searchTimer?.cancel();
    int seconds = 0;

    Log.print("⏳ Search Timer Started (90s)...");
    await RideLiveNotification.showSearching(driversStatusForSearchWindow);

    _searchTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      seconds++;

      if (currentRideState.value != RideState.searching) {
        timer.cancel();
        return;
      }

      if (seconds >= 90) {
        timer.cancel();
        handleNoDriverFound();
      }
    });
  }

  void showNoDriversDialog() {
    Get.dialog(
      BackdropFilter(
        filter: ImageFilter.blur(sigmaX: 5, sigmaY: 5),
        child: CupertinoAlertDialog(
          title: Text("No Car or Driver Found in your area.".tr,
              style: AppStyle.title
                  .copyWith(fontSize: 20, fontWeight: FontWeight.bold)),
          content: Text("No Car or Driver Found in your area.".tr,
              style: AppStyle.title.copyWith(fontSize: 16)),
          actions: [
            CupertinoDialogAction(
              onPressed: () {
                Get.back();
                clearControllersAndGoHome();
              },
              child:
                  Text('OK'.tr, style: TextStyle(color: AppColor.greenColor)),
            ),
          ],
        ),
      ),
      barrierDismissible: false,
    );
  }

  Future<void> _stageNiceToHave() async {
    Log.print('🚀 MapPassengerController: Starting _stageNiceToHave');

    await Future.wait([
      Future(() async {
        try {
          Log.print('🔍 Loading Favorites...');
          await locSearch.getFavioratePlaces();
        } catch (e) {
          Log.print("Error: $e");
        }
      }),
      Future(() async {
        try {
          Log.print('🔍 Loading Waypoints...');
          locSearch.readyWayPoints();
        } catch (e) {
          Log.print("Error: $e");
        }
      }),
      Future(() async {
        try {
          Log.print('🔍 Loading Rate...');
          await getPassengerRate();
        } catch (e) {
          Log.print("Error: $e");
        }
      }),
      Future(() async {
        try {
          Log.print('🔍 Loading Coupons...');
          await firstTimeRunToGetCoupon();
        } catch (e) {
          Log.print("Error: $e");
        }
      }),
    ]);
    Log.print('✅ MapPassengerController: _stageNiceToHave complete');
    try {
      cardNumber = await SecureStorage().readData(BoxName.cardNumber);
    } catch (e) {
      Log.print("Error: $e");
    }
  }

  Future<void> _stagePricingAndState() async {
    try {} catch (e) {
      Log.print("Error: $e");
    }
    try {
      await _checkInitialRideStatus();
    } catch (e) {
      Log.print("Error: $e");
    }
    _applyLowEndModeIfNeeded();
  }

  void _applyLowEndModeIfNeeded() {
    // Placeholder comment from original
  }

  void showDrawingBottomSheet() {
    Log.print(
        '🔔 showDrawingBottomSheet called. isDrawingRoute: $isDrawingRoute');

    final context = Get.context;
    if (context == null) return;

    WidgetsBinding.instance.addPostFrameCallback((_) {
      // Close any existing open dialogs first
      if (Get.isDialogOpen == true) {
        Get.back();
      }

      Get.dialog(
        Dialog(
          backgroundColor: Colors.transparent,
          elevation: 0,
          child: Container(
            padding: const EdgeInsets.all(24),
            width: 180,
            decoration: BoxDecoration(
              color: Colors.white.withOpacity(0.95),
              borderRadius: BorderRadius.circular(24),
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withOpacity(0.15),
                  blurRadius: 20,
                  spreadRadius: 5,
                )
              ],
            ),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              mainAxisAlignment: MainAxisAlignment.center,
              crossAxisAlignment: CrossAxisAlignment.center,
              children: [
                // App Logo
                Image.asset(
                  'assets/images/logo.gif',
                  height: 64,
                  errorBuilder: (context, error, stackTrace) => const Icon(
                    Icons.map,
                    size: 64,
                    color: AppColor.primaryColor,
                  ),
                ),
                const SizedBox(height: 16),
                const SizedBox(
                  width: 24,
                  height: 24,
                  child: MyCircularProgressIndicator(),
                ),
                const SizedBox(height: 16),
                Text(
                  'Drawing route on map...'.tr,
                  style: const TextStyle(
                    fontWeight: FontWeight.bold,
                    fontSize: 14,
                    color: AppColor.primaryColor,
                  ),
                  textAlign: TextAlign.center,
                ),
              ],
            ),
          ),
        ),
        barrierDismissible: false,
      );

      // Auto-dismiss after exactly 2 seconds
      Future.delayed(const Duration(seconds: 2), () {
        if (Get.isDialogOpen == true) {
          Get.back();
        }
      });
    });
  }

  @override
  void onInit() async {
    super.onInit();
    await _checkAndRefreshMapStyle();
    Get.put(DeepLinkController(), permanent: true);
    await initilizeGetStorage();
    getLocationArea(passengerLocation.latitude, passengerLocation.longitude);
    unawaited(_stagePricingAndState());
    unawaited(_stageNiceToHave());
    startMasterTimer();
  }

  @override
  void onClose() {
    stopAllTimers();
    if (!_timerStreamController.isClosed) {
      _timerStreamController.close();
    }
    if (!_beginRideStreamController.isClosed) {
      _beginRideStreamController.close();
    }
    if (!_rideStatusStreamController.isClosed) {
      _rideStatusStreamController.close();
    }
    if (!timerController.isClosed) {
      timerController.close();
    }
    super.onClose();
  }

  void clearControllersAndGoHome() {
    Get.offAll(() => const MapPagePassenger());
  }

  /// Builds a Set of short [Polyline] segments that simulate a dashed line.
  /// intaleq_maps (MapLibre) doesn't support `patterns`, so we manually
  /// split the route into dash/gap alternating segments.
  Set<Polyline> _buildDashedPolylines({
    required List<LatLng> points,
    required double dashLengthMeters,
    required double gapLengthMeters,
    required Color color,
    required int width,
    required String idPrefix,
  }) {
    final Set<Polyline> result = {};
    if (points.length < 2) return result;

    int segmentIndex = 0;
    bool isDash = true;
    double remaining = dashLengthMeters;
    List<LatLng> currentSegment = [points[0]];

    for (int i = 0; i < points.length - 1; i++) {
      final LatLng a = points[i];
      final LatLng b = points[i + 1];
      double segLen = _haversineDistance(a, b);
      double covered = 0.0;

      while (covered < segLen) {
        double leftInSeg = segLen - covered;
        if (remaining <= leftInSeg) {
          // interpolate the endpoint of this dash/gap
          double fraction = (covered + remaining) / segLen;
          LatLng interp = LatLng(
            a.latitude + fraction * (b.latitude - a.latitude),
            a.longitude + fraction * (b.longitude - a.longitude),
          );
          currentSegment.add(interp);

          if (isDash && currentSegment.length >= 2) {
            result.add(Polyline(
              polylineId: PolylineId('${idPrefix}_seg_$segmentIndex'),
              points: List.from(currentSegment),
              color: color,
              width: width,
            ));
          }

          final double consumed = remaining;
          segmentIndex++;
          isDash = !isDash;
          remaining = isDash ? dashLengthMeters : gapLengthMeters;
          currentSegment = [interp];
          covered += consumed;
        } else {
          currentSegment.add(b);
          covered = segLen;
          remaining -= leftInSeg;
        }
      }
    }

    // Flush last dash segment
    if (isDash && currentSegment.length >= 2) {
      result.add(Polyline(
        polylineId: PolylineId('${idPrefix}_seg_$segmentIndex'),
        points: List.from(currentSegment),
        color: color,
        width: width,
      ));
    }

    return result;
  }

  /// Haversine distance in meters between two LatLng points.
  LatLng? _parseLatLng(String? raw) {
    if (raw == null || raw.trim().isEmpty) return null;
    final parts = raw.split(',');
    if (parts.length < 2) return null;
    final lat = double.tryParse(parts[0].trim());
    final lng = double.tryParse(parts[1].trim());
    if (lat == null || lng == null) return null;
    if (lat == 0 && lng == 0) return null;
    return LatLng(lat, lng);
  }

  bool _isHeadingAwayFromRoute({
    required double? heading,
    required double? speed,
    required int closestRouteIndex,
    required double distanceFromRouteMeters,
  }) {
    if (heading == null || speed == null || speed < 2.5) return false;
    if (_currentDriverRoutePoints.length < 2) return false;
    if (distanceFromRouteMeters < 10) return false;

    int fromIndex = closestRouteIndex;
    int toIndex =
        min(closestRouteIndex + 1, _currentDriverRoutePoints.length - 1);
    if (fromIndex == toIndex && fromIndex > 0) {
      fromIndex--;
    }
    if (fromIndex == toIndex) return false;

    final double routeBearing = _bearingBetween(
      _currentDriverRoutePoints[fromIndex],
      _currentDriverRoutePoints[toIndex],
    );
    final double angleDiff = _angleDifference(heading, routeBearing);
    return angleDiff > 110;
  }

  double _bearingBetween(LatLng a, LatLng b) {
    final double lat1 = a.latitude * pi / 180;
    final double lat2 = b.latitude * pi / 180;
    final double dLng = (b.longitude - a.longitude) * pi / 180;
    final double y = sin(dLng) * cos(lat2);
    final double x = cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(dLng);
    return (atan2(y, x) * 180 / pi + 360) % 360;
  }

  double _angleDifference(double a, double b) {
    final double diff = ((a - b + 540) % 360) - 180;
    return diff.abs();
  }

  double _pathDistanceMeters(List<LatLng> points) {
    if (points.length < 2) return 0.0;
    double total = 0.0;
    for (int i = 0; i < points.length - 1; i++) {
      total += _haversineDistance(points[i], points[i + 1]);
    }
    return total;
  }

  double _haversineDistance(LatLng a, LatLng b) {
    const R = 6371000.0;
    final dLat = (b.latitude - a.latitude) * pi / 180;
    final dLng = (b.longitude - a.longitude) * pi / 180;
    final sinLat = sin(dLat / 2);
    final sinLng = sin(dLng / 2);
    final h = sinLat * sinLat +
        cos(a.latitude * pi / 180) *
            cos(b.latitude * pi / 180) *
            sinLng *
            sinLng;
    return 2 * R * atan2(pow(h, 0.5).toDouble(), pow(1 - h, 0.5).toDouble());
  }

  // دالة لبناء الخط المنقط
  List<Polyline> _buildDashedLine(LatLng start, LatLng end,
      {required Color color, required String prefixId}) {
    List<Polyline> segments = [];
    double dist = Geolocator.distanceBetween(
        start.latitude, start.longitude, end.latitude, end.longitude);

    const double dashLengthMeters = 8.0;
    const double gapLengthMeters = 6.0;

    double latDiff = end.latitude - start.latitude;
    double lngDiff = end.longitude - start.longitude;

    double totalLength = 0;
    int segmentCount = 0;

    while (totalLength < dist) {
      double startFraction = totalLength / dist;
      double endFraction = (totalLength + dashLengthMeters) / dist;

      if (endFraction > 1.0) {
        endFraction = 1.0;
      }

      double startLat = start.latitude + latDiff * startFraction;
      double startLng = start.longitude + lngDiff * startFraction;
      double endLat = start.latitude + latDiff * endFraction;
      double endLng = start.longitude + lngDiff * endFraction;

      segments.add(
        Polyline(
          polylineId: PolylineId('${prefixId}_dash_$segmentCount'),
          points: [LatLng(startLat, startLng), LatLng(endLat, endLng)],
          color: color,
          width: 4,
        ),
      );
      segmentCount++;
      totalLength += dashLengthMeters + gapLengthMeters;
    }
    return segments;
  }

  // تحديث الخط المنقط ومكان أيقونة المشي للراكب
  void _updatePassengerWalkLine() {
    polyLines.removeWhere(
        (p) => p.polylineId.value.startsWith('passenger_walk_line'));
    markers.removeWhere((m) => m.markerId.value == 'walk_end_marker');

    bool shouldShowWalkPath =
        (statusRide == 'Apply' || statusRide == 'Arrived') &&
            _currentDriverRoutePoints.isNotEmpty &&
            passengerLocation.latitude != 0;

    if (shouldShowWalkPath) {
      final LatLng lastRoadPt = _currentDriverRoutePoints.last;

      final walkDashes = _buildDashedLine(
        lastRoadPt,
        passengerLocation,
        color: Colors.blueGrey,
        prefixId: 'passenger_walk_line',
      );
      polyLines.addAll(walkDashes);

      markers.add(
        Marker(
          markerId: const MarkerId('walk_end_marker'),
          position: lastRoadPt,
          icon: InlqBitmap.fromStyleImage('walk_icon'),
          anchor: const Offset(0.5, 0.5),
        ),
      );
    }
    mapEngine.update();
    update();
  }

  initilizeGetStorage() async {
    if (box.read(BoxName.addWork) == null) {
      box.write(BoxName.addWork, 'addWork');
    }
    if (box.read(BoxName.addHome) == null) {
      box.write(BoxName.addHome, 'addHome');
    }
  }

  getPassengerRate() async {
    var res = await CRUD().get(
        link: AppLink.getPassengerRate,
        payload: {'passenger_id': box.read(BoxName.passengerID)});
    if (res != 'failure') {
      var json = jsonDecode(res);
      var message = json['data'] ?? json['message'];
      if (message['rating'] == null) {
        passengerRate = 5.0; // Default rating
      } else {
        var rating = message['rating'];
        if (rating is String) {
          passengerRate = double.tryParse(rating) ?? 5.0;
        } else if (rating is num) {
          passengerRate = rating.toDouble();
        } else {
          passengerRate = 5.0;
        }
      }
    } else {
      passengerRate = 5.0;
    }
  }

  firstTimeRunToGetCoupon() async {
    if (box.read(BoxName.isFirstTime).toString() == '0' &&
        box.read(BoxName.isInstall).toString() == '1' &&
        box.read(BoxName.isGiftToken).toString() == '0') {
      var promo, discount, validity;
      var resPromo = await CRUD().get(link: AppLink.getPromoFirst, payload: {
        "passengerID": box.read(BoxName.passengerID).toString(),
      });
      if (resPromo != 'failure') {
        var d1 = jsonDecode(resPromo);
        promo = d1['message']['promo_code'];
        discount = d1['message']['amount'];
        validity = d1['message']['validity_end_date'];
      }
      box.write(BoxName.isFirstTime, '1');
      Get.dialog(
        AlertDialog(
          contentPadding: EdgeInsets.zero,
          content: SizedBox(
            width: 300,
            child: PromoBanner(
              promoCode: promo ?? '',
              discountPercentage: discount ?? '',
              validity: validity ?? '',
            ),
          ),
        ),
      );
    }
  }
}
