import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:math' as math;
import 'package:http/http.dart' as http;
import 'package:sefer_driver/controller/home/captin/behavior_controller.dart';
import 'package:sefer_driver/controller/home/captin/home_captain_controller.dart';
import 'package:sefer_driver/views/widgets/error_snakbar.dart';
import 'package:sefer_driver/views/widgets/mydialoug.dart';
import 'package:bubble_head/bubble.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:google_polyline_algorithm/google_polyline_algorithm.dart';
import 'package:url_launcher/url_launcher.dart';

import '../../../constant/api_key.dart';
import '../../../constant/box_name.dart';
import '../../../constant/colors.dart';
import '../../../constant/country_polygons.dart';
import '../../../constant/links.dart';
import '../../../constant/table_names.dart';
import '../../../env/env.dart';
import '../../../main.dart';
import '../../../print.dart';
import '../../../views/Rate/rate_passenger.dart';
import '../../../views/home/Captin/home_captain/home_captin.dart';
import '../../firebase/firbase_messge.dart';
import '../../firebase/notification_service.dart';
import '../../functions/crud.dart';
import '../../functions/location_controller.dart';
import '../../functions/tts.dart';

class MapDriverController extends GetxController {
  bool isLoading = true;
  final formKey1 = GlobalKey<FormState>();
  final formKey2 = GlobalKey<FormState>();
  final formKeyCancel = GlobalKey<FormState>();
  final messageToPassenger = TextEditingController();
  final sosEmergincyNumberCotroller = TextEditingController();
  final cancelTripCotroller = TextEditingController();
  List data = [];
  List dataDestination = [];
  LatLngBounds? boundsData;
  BitmapDescriptor carIcon = BitmapDescriptor.defaultMarker;
  BitmapDescriptor passengerIcon = BitmapDescriptor.defaultMarker;
  BitmapDescriptor startIcon = BitmapDescriptor.defaultMarker;
  BitmapDescriptor endIcon = BitmapDescriptor.defaultMarker;
  final List<LatLng> polylineCoordinates = [];
  final List<LatLng> polylineCoordinatesDestination = [];
  List<Polyline> polyLines = [];
  List<Polyline> polyLinesDestination = [];
  Set<Marker> markers = {};
  late String passengerLocation;
  late String passengerDestination;
  late String step0;
  late String step1;
  late String step2;
  late String step3;
  late String step4;
  late String passengerWalletBurc;
  late String timeOfOrder;
  late String duration;
  late String totalCost;
  String distance = '0';
  String? passengerName;
  late String passengerEmail;
  late String totalPricePassenger;
  late String passengerPhone;
  late String rideId;
  late String isHaveSteps;
  String paymentAmount = '0';
  late String paymentMethod;
  late String passengerId;
  late String driverId;
  late String tokenPassenger;
  late String durationToPassenger;
  late String walletChecked;
  late String direction;
  late String durationOfRideValue;
  late String status;
  int timeWaitingPassenger = 5; //5 miniute
  bool isPassengerInfoWindow = false;
  bool isBtnRideBegin = false;
  bool isArrivedSend = true;
  bool isdriverWaitTimeEnd = false;
  bool isRideFinished = false;
  bool isRideStarted = false;
  bool isPriceWindow = false;
  double passengerInfoWindowHeight = Get.height * .38;
  double driverEndPage = 100;
  double progress = 0;
  double progressToPassenger = 0;
  double progressInPassengerLocationFromDriver = 0;
  bool isRideBegin = false;
  int progressTimerToShowPassengerInfoWindowFromDriver = 25;
  int remainingTimeToShowPassengerInfoWindowFromDriver = 25;
  int remainingTimeToPassenger = 60;
  int remainingTimeInPassengerLocatioWait = 60;
  bool isDriverNearPassengerStart = false;
  GoogleMapController? mapController;
  late LatLng myLocation;
  int remainingTimeTimerRideBegin = 60;
  String stringRemainingTimeRideBegin = '';
  String stringRemainingTimeRideBegin1 = '';
  double progressTimerRideBegin = 0;
  late Timer timer;
  String? mapAPIKEY;
  final zones = <Zone>[];
  String canelString = 'yet';
  LatLng latLngPassengerLocation = LatLng(0, 0);
  late LatLng latLngPassengerDestination = LatLng(0, 0);

  List<Map<String, dynamic>> routeSteps = [];
  String currentInstruction = "";
  int currentStepIndex = 0;
  bool isTtsEnabled = false;

  void toggleTts() {
    isTtsEnabled = !isTtsEnabled;
    box.write(BoxName.ttsEnabled, isTtsEnabled);
    update(); // تحديث الواجهة
  }

  @override
  void onClose() {
    print("--- Controller is closing. Cleaning up. ---");
    _rideTimer?.cancel(); // إيقاف العداد عند الخروج
    timer.cancel();
    _posSub?.cancel();
    _navigationTimer?.cancel();
    super.onClose();
  }

  void onMapCreated(GoogleMapController controller) async {
    myLocation = Get.find<LocationController>().myLocation;
    // myLocation = myLocation;
    mapController = controller;
    controller.getVisibleRegion();
    // LatLngBounds bounds = await controller.getVisibleRegion();
    controller.animateCamera(
      CameraUpdate.newLatLng(Get.find<LocationController>().myLocation),
    );
    update();
    // Set up a timer or interval to trigger the marker update every 3 seconds.
    timer = Timer.periodic(const Duration(seconds: 1), (_) {
      updateMarker();
    });
  }

  void changeStatusDriver() {
    status = 'On';
    update();
  }

  void changeDriverEndPage() {
    remainingTimeTimerRideBegin < 60 ? driverEndPage = 160 : 100;
    update();
  }

  takeSnapMap() {
    mapController!.takeSnapshot();
  }

  @override
  void dispose() {
    mapController!.dispose();
    super.dispose();
  }

  Future openGoogleMapFromDriverToPassenger() async {
    var endLat = latLngPassengerLocation.latitude;
    var endLng = latLngPassengerLocation.longitude;

    var startLat = Get.find<LocationController>().myLocation.latitude;
    var startLng = Get.find<LocationController>().myLocation.longitude;

    String url =
        'https://www.google.com/maps/dir/$startLat,$startLng/$endLat,$endLng/&directionsmode=driving';
    if (await canLaunchUrl(Uri.parse(url))) {
      await launchUrl(Uri.parse(url));
    } else {
      throw 'Could not launch google maps';
    }
  }

  void clearPolyline() {
    polyLines = [];
    polyLinesDestination = [];
    polylineCoordinates.clear();
    polylineCoordinatesDestination.clear();
    update();
  }

  void changeRideToBeginToPassenger() {
    isRideBegin = true;
    passengerInfoWindowHeight = Get.height * .22;
    update();
  }

  cancelTripFromDriverAfterApplied() async {
    if (formKeyCancel.currentState!.validate()) {
      // 1. إيقاف استقبال التحديثات فوراً
      box.write(BoxName.statusDriverLocation, 'off');

      // إظهار لودينج لمنع المستخدم من الضغط مرتين
      Get.dialog(const Center(child: CircularProgressIndicator()),
          barrierDismissible: false);

      try {
        // 2. إرسال الإشعار للراكب
        NotificationService.sendNotification(
          target: tokenPassenger.toString(),
          title: "Cancel Trip from driver",
          body:
              "Trip Cancelled from driver. We are looking for a new driver. Please wait."
                  .tr,
          isTopic: false,
          tone: 'cancel',
          driverList: [],
          category: "Cancel Trip from driver",
        );

        // 3. تحديث السيرفرات (نستخدم await لضمان وصول الأمر للسيرفر قبل إغلاق الصفحة)
        // ملاحظة: تأكد من صحة هذه الروابط
        CRUD().post(link: "${AppLink.server}/ride/rides/update.php", payload: {
          "id": (rideId).toString(),
          "status": 'CancelFromDriverAfterApply'
        });

        CRUD().post(link: "${AppLink.rideServer}/rides/update.php", payload: {
          "id": (rideId).toString(),
          "status": 'CancelFromDriverAfterApply'
        });

        // هذا الرابط كان يسبب مشكلة HTML، وضعناه في try منفصل كي لا يوقف العملية
        try {
          CRUD().postFromDialogue(
              link: '${AppLink.server}/driver_order/add.php',
              payload: {
                'driver_id': box.read(BoxName.driverID).toString(),
                'order_id': (rideId).toString(),
                'status': 'CancelFromDriverAfterApply'
              });
        } catch (e) {
          Log.print("Error logging driver order: $e");
        }

        CRUD().post(
            link:
                "${AppLink.ride}/cancelRide/addCancelTripFromDriverAfterApplied.php",
            payload: {
              "order_id": (rideId).toString(),
              "driver_id": box.read(BoxName.driverID).toString(),
              "status": 'reject After Applied',
              "notes": (cancelTripCotroller.text).toString()
            });

        // 4. تنظيف البيانات المحلية
        // CRUD().post(
        //     link: "${AppLink.ride}/cancelRide/deletArgumets.php",
        //     payload: {
        //       "driver_id": box.read(BoxName.driverID).toString(),
        //     });

        box.remove(BoxName.rideArgumentsFromBackground);
        box.remove(BoxName.rideArguments);
        box.write(BoxName.statusDriverLocation, 'off');

        // 5. حل مشكلة الكراش (SQL UNIQUE constraint failed)
        // نضع الكود داخل try-catch، إذا كان الرقم موجوداً مسبقاً سيتم تجاهل الإضافة ولن ينهار التطبيق
        try {
          await sql.insertData({
            'order_id': (rideId),
            'created_at': DateTime.now().toString(),
            'driver_id': box.read(BoxName.driverID).toString(),
          }, TableName.driverOrdersRefuse);
        } catch (e) {
          Log.print(
              "Order already exists in refused list, skipping insert. Error: $e");
        }

        box.write(BoxName.rideStatus, 'Cancel');
        Log.print('rideStatus from map 240 : ${box.read(BoxName.rideStatus)}');

        // تحديث القائمة الرئيسية
        Get.find<HomeCaptainController>().getRefusedOrderByCaptain();

        // إغلاق اللودينج والانتقال
        if (Get.isDialogOpen ?? false) Get.back();
        Get.offAll(() => HomeCaptain());
      } catch (e) {
        // في حال حدوث أي خطأ غير متوقع، نغلق اللودينج ونخرج أيضاً لضمان عدم تعليق التطبيق
        if (Get.isDialogOpen ?? false) Get.back();
        Log.print("Error during cancellation: $e");
        Get.offAll(() => HomeCaptain());
      }
    }
  }

  void startTimerToShowPassengerInfoWindowFromDriver() async {
    if (box.read(BoxName.rideStatus) == 'Begin') {
      Log.print('rideStatus from map 248 : ${box.read(BoxName.rideStatus)}');
      isPassengerInfoWindow = false;
    } else {
      isPassengerInfoWindow = true;
      for (int i = 0; i <= int.parse(durationToPassenger); i++) {
        await Future.delayed(const Duration(seconds: 1));
        progressToPassenger = i / int.parse(durationToPassenger);
        remainingTimeToPassenger = int.parse(durationToPassenger) - i;
        if (remainingTimeToPassenger == 0) {
          isBtnRideBegin = true;
          update();
        }

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

        update();
      }
    }

    // update();
    // startTimerToShowDriverToPassengerDuration();
  }

  String stringRemainingTimeToPassenger = '';

  String stringRemainingTimeWaitingPassenger = '';

  void startTimerToShowDriverWaitPassengerDuration() async {
    for (int i = 0; i <= timeWaitingPassenger * 60; i++) {
      await Future.delayed(const Duration(seconds: 1));
      progressInPassengerLocationFromDriver = i / (timeWaitingPassenger * 60);
      remainingTimeInPassengerLocatioWait = (timeWaitingPassenger * 60) - i;
      if (isRideBegin == true) {
        remainingTimeInPassengerLocatioWait = 0;
        update();
      }
      if (remainingTimeInPassengerLocatioWait == 0) {
        isdriverWaitTimeEnd = true;
        update();
      }
      'remainingTimeInPassengerLocatioWait $remainingTimeInPassengerLocatioWait';

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

      update();
    }
  }

  void driverGoToPassenger() async {
    changeRideToBeginToPassenger();
    box.write(BoxName.rideStatus, 'Applied');
    Log.print('rideStatus from map 304 : ${box.read(BoxName.rideStatus)}');
    update();
    await CRUD()
        .post(link: "${AppLink.server}/ride/rides/update.php", payload: {
      'id': (rideId),
      'driverGoToPassengerTime': DateTime.now().toString(),
      'status': 'Applied'
    });
    CRUD().post(link: "${AppLink.rideServer}/ride/rides/update.php", payload: {
      'id': (rideId),
      'driverGoToPassengerTime': DateTime.now().toString(),
      'status': 'Applied'
    });

    NotificationService.sendNotification(
      target: tokenPassenger.toString(),
      title: 'Driver Is Going To Passenger'.tr,
      body: box.read(BoxName.nameDriver).toString(),
      isTopic: false, // Important: this is a token
      tone: 'start',
      driverList: [], category: 'Driver Is Going To Passenger',
    );
  }

  bool isSocialPressed = false;
  driverCallPassenger() async {
    String scam = await getDriverScam();

    // نحول القيمة لرقم بشكل آمن
    int scamCount = int.tryParse(scam) ?? 0;

    // لو فوق الحد المطلوب
    if (scamCount > 3) {
      box.write(BoxName.statusDriverLocation, 'on');
      Get.find<LocationController>().stopLocationUpdates();

      await CRUD().post(
        link: AppLink.addNotificationCaptain,
        payload: {
          'driverID': box.read(BoxName.driverID),
          'title': 'scams operations'.tr,
          'body':
              'you have connect to passengers and let them cancel the order'.tr,
        },
      );

      return;
    }

    // تسجيل التحايل بشكل طبيعي
    if (isSocialPressed == true && passengerId != null && rideId != null) {
      box.write(BoxName.statusDriverLocation, 'off');

      await CRUD().post(
        link: AppLink.addDriverScam,
        payload: {
          'driverID': box.read(BoxName.driverID),
          'passengerID': passengerId,
          'rideID': rideId,
          'isDriverCalledPassenger': 'true',
        },
      );
    }
  }

// دالة مساعدة لحماية التطبيق من كراش الخرائط
  Future<void> safeAnimateCamera(CameraUpdate cameraUpdate) async {
    // 1. تحقق مما إذا كان الكونترولر مغلقاً (المستخدم خرج من الصفحة)
    if (isClosed) return;

    // 2. تحقق من أن متغير الخريطة موجود
    if (mapController == null) return;

    try {
      // 3. حاول التحريك
      await mapController!.animateCamera(cameraUpdate);
    } catch (e) {
      // 4. إذا حدث خطأ (مثل أن الخريطة لم تعد موجودة في الذاكرة)، تجاهله ولا توقف التطبيق
      Log.print("SafeAnimateCamera Error ignored: $e");
    }
  }

  Future<String> getDriverScam() async {
    var res = await CRUD().post(link: AppLink.getDriverScam, payload: {
      'driverID': box.read(BoxName.driverID),
    });

    if (res == 'failure') {
      box.write(BoxName.statusDriverLocation, 'off');
      return '0';
    }
    var d = (res);
    Log.print('d: ${d}');

    // 1. Check if the response status is 'failure' (API level check)
    if (d['status'] == 'failure') {
      // If the API status is failure, the message is a String (e.g., 'No ride scam record found')
      // and there's no 'count' array to read.
      return '0';
    }

    // 2. Safely access the List/Map structure for 'count'
    // This assumes a successful response looks like:
    // {'status': 'success', 'message': [{'count': '12'}]}
    var messageData = d['message'];

    // Check if messageData is actually a List before accessing index [0]
    if (messageData is List &&
        messageData.isNotEmpty &&
        messageData[0] is Map) {
      return messageData[0]['count'];
    }

    // Fallback if the successful data structure is unexpected
    return '0';

    // --- FIX END ---
  }

  void startRideFromStartApp() {
    // if (box.read(BoxName.rideStatus) == 'Begin') {
    changeRideToBeginToPassenger();
    isPassengerInfoWindow = false;
    isRideStarted = true;
    isRideFinished = false;
    remainingTimeInPassengerLocatioWait = 0;
    timeWaitingPassenger = 0;
    box.write(BoxName.statusDriverLocation, 'on');
    update();
    // }

    // rideIsBeginPassengerTimer();
  }

  Position? currentPosition;
  startRideFromDriver() async {
    double _distance =
        await calculateDistanceBetweenDriverAndPassengerLocation();

    if (_distance < 400) {
      changeRideToBeginToPassenger();
      isPassengerInfoWindow = false;
      isRideStarted = true;
      isRideFinished = false;
      remainingTimeInPassengerLocatioWait = 0;
      timeWaitingPassenger = 0;
      box.write(BoxName.statusDriverLocation, 'on');

      // // ابدأ التتبع الحيّ لخطوات الملاحة
      await startListeningStepNavigation();
      // box.write(BoxName.rideStatus, 'Begin'); //
      // todo ride details
      // Get.find<HomeCaptainController>().changeToAppliedRide('Begin');
      box.write(BoxName.rideStatus, 'Begin');
      Log.print('rideStatus from map 399  : ${box.read(BoxName.rideStatus)}');
      // Get.find<HomeCaptainController>().update();
      update();
      await CRUD().post(link: AppLink.updateRides, payload: {
        'id': (rideId),
        'rideTimeStart': DateTime.now().toString(),
        'status': 'Begin',
      });
      CRUD().post(link: "${AppLink.server}/ride/rides/update.php", payload: {
        'id': (rideId),
        'rideTimeStart': DateTime.now().toString(),
        'status': 'Begin',
      });
      CRUD().post(link: AppLink.addDriverOrder, payload: {
        'driver_id': box.read(BoxName.driverID).toString(),
        'order_id': (rideId).toString(),
        'status': 'Begin'
      });

      NotificationService.sendNotification(
        target: tokenPassenger.toString(),
        title: 'Trip is Begin'.tr,
        body: box.read(BoxName.nameDriver).toString(),
        isTopic: false, // Important: this is a token
        tone: 'start',
        driverList: [], category: 'Trip is Begin',
      );
      rideIsBeginPassengerTimer();

      update();
    } else {
      Get.back();
      MyDialog().getDialog('Your are far from passenger location'.tr,
          'go to your passenger location before\nPassenger cancel trip'.tr, () {
        Get.back();
      });
    }
  }

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

  double speedoMeter = 0;
  void updateLocation() async {
    try {
      for (var i = 0; i < remainingTimeTimerRideBegin; i++) {
        await Future.delayed(const Duration(seconds: 1));

        await safeAnimateCamera(
          CameraUpdate.newCameraPosition(
            CameraPosition(
              bearing: Get.find<LocationController>().heading,
              target: myLocation,
              zoom: 17,
            ),
          ),
        );
        // });
        update();
      }

      // Stop listening after ride finishes
      if (!isRideBegin) {}
    } catch (error) {
      debugPrint('Error listening to GPS: $error');
      // Handle GPS errors gracefully
    }

    // Periodically call updateLocation again
    await Future.delayed(const Duration(seconds: 1));
    updateLocation();
  }

  calculateDistanceBetweenDriverAndPassengerLocation() async {
    Get.put(LocationController());
    var res = await CRUD().get(
        link: AppLink.getLatestLocationPassenger,
        payload: {'rideId': (rideId)});
    if (res != 'failure') {
      var passengerLatestLocationString = jsonDecode(res)['message'];

      double distance2 = Geolocator.distanceBetween(
        double.parse(passengerLatestLocationString[0]['lat'].toString()),
        double.parse(passengerLatestLocationString[0]['lng'].toString()),
        Get.find<LocationController>().myLocation.latitude,
        Get.find<LocationController>().myLocation.longitude,
      );
      return distance2;
    } else {
      double distance2 = Geolocator.distanceBetween(
        latLngPassengerLocation.latitude,
        latLngPassengerLocation.longitude,
        Get.find<LocationController>().myLocation.latitude,
        Get.find<LocationController>().myLocation.longitude,
      );
      return distance2;
    }
  }

  addWaitingTimeCostFromPassengerToDriverWallet() async {
    double distance2 =
        await calculateDistanceBetweenDriverAndPassengerLocation();

    if (distance2 > 100) {
      MyDialog().getDialog('Your are far from passenger location'.tr,
          'go to your passenger location before\nPassenger cancel trip'.tr, () {
        Get.back();
      });
    } else {
      double costOfWaiting5Minute = box.read(BoxName.countryCode) == 'Egypt'
          ? (distanceBetweenDriverAndPassengerWhenConfirm * .08) + (5 * 1)
          : (distanceBetweenDriverAndPassengerWhenConfirm * 1100) +
              (5 * .06); //for Eygpt other like jordan .06 per minute
      await CRUD().post(link: AppLink.updateRides, payload: {
        'id': (rideId),
        'rideTimeStart': DateTime.now().toString(),
        'status': 'CancelAfterWait',
      });
      CRUD().post(link: AppLink.addDriverOrder, payload: {
        'driver_id': box.read(BoxName.driverID).toString(),
        'order_id': (rideId).toString(),
        'status': 'CancelAfterWait'
      });

      var paymentTokenWait =
          await generateTokenDriver(costOfWaiting5Minute.toString());
      var res = await CRUD().post(link: AppLink.addDrivePayment, payload: {
        'rideId': (rideId),
        'amount': (costOfWaiting5Minute.toString()),
        'payment_method': 'wait-cancel',
        'passengerID': (passengerId),
        'token': paymentTokenWait,
        'driverID': box.read(BoxName.driverID).toString(),
      });
      var paymentTokenWait1 =
          await generateTokenDriver(costOfWaiting5Minute.toString());
      var res1 =
          await CRUD().post(link: AppLink.addDriversWalletPoints, payload: {
        'paymentID': 'rideId${(rideId)}',
        'amount': (costOfWaiting5Minute).toStringAsFixed(0),
        'paymentMethod': 'wait',
        'token': paymentTokenWait1,
        'driverID': box.read(BoxName.driverID).toString(),
      });
      if (res != 'failure') {
        Get.snackbar(
          'You will get cost of your work for this trip'.tr,
          '${'you gain'.tr} $costOfWaiting5Minute \$${' in your wallet'.tr}',
          backgroundColor: AppColor.deepPurpleAccent,
        );
      }
      var paymentTokenWaitPassenger1 =
          await generateTokenPassenger((costOfWaiting5Minute * -1).toString());
      await CRUD().post(link: AppLink.addPassengersWallet, payload: {
        'passenger_id': (passengerId),
        'balance': (costOfWaiting5Minute * -1).toString(),
        'token': paymentTokenWaitPassenger1,
      });
      box.write(BoxName.statusDriverLocation, 'off');
      Get.offAll(HomeCaptain());
    }
  }

  Future<void> finishRideFromDriver() async {
    // double distanceToDestination = Geolocator.distanceBetween(
    //   latLngPassengerDestination.latitude,
    //   latLngPassengerDestination.longitude,
    //   Get.find<LocationController>().myLocation.latitude,
    //   Get.find<LocationController>().myLocation.longitude,
    // );
    final originalDistanceM = double.parse(distance.toString()) * 1000;

    // 2. احسب المسافة التي قطعها السائق حتى الآن
    final movedDistanceM = Geolocator.distanceBetween(
      Get.find<LocationController>().myLocation.latitude,
      Get.find<LocationController>().myLocation.longitude,
      latLngPassengerLocation.latitude,
      latLngPassengerLocation.longitude,
    );
    // originalDistanceM - distanceToDestination;

    // 3. عتبة ثلث المسافة
    final oneThirdDistanceM = originalDistanceM / 5;

    // Logging للتتبع
    Log.print('originalDistanceM: $originalDistanceM');
    // Log.print('distanceToDestinationM: $distanceToDestination');
    Log.print('movedDistanceM: $movedDistanceM');
    Log.print('oneThirdDistanceM: $oneThirdDistanceM');

    // 4. إذا لم يقطع السائق ثلث المسافة، نعرض التأكيد
    if (movedDistanceM > oneThirdDistanceM) {
      MyDialog().getDialog(
        'Are you sure to exit ride?'.tr,
        '',
        () {
          Get.back();
          finishRideFromDriver1();
        },
      );
    } /////

    else {
      final textToSpeechController = Get.put(TextToSpeechController());
      MyDialog().getDialog(
        "You haven't moved sufficiently!".tr,
        '',
        () => Get.back(),
      );
      await textToSpeechController
          .speakText("You haven't moved sufficiently!".tr);
    }
  }

  String paymentToken = '';
  Future<String> generateTokenDriver(String amount) async {
    var res =
        await CRUD().postWallet(link: AppLink.addPaymentTokenDriver, payload: {
      'driverID': box.read(BoxName.driverID).toString(),
      'amount': amount.toString(),
    });
    var d = (res);
    return d['message'];
  }

  String paymentTokenPassenger = '';
  Future<String> generateTokenPassenger(String amount) async {
    var res = await CRUD()
        .postWallet(link: AppLink.addPaymentTokenPassenger, payload: {
      'passengerId': passengerId,
      'amount': amount.toString(),
    });
    var d = (res);
    return d['message'];
  }

  // ... other controller code ...

  /// Refactored function to finish a ride with consolidated API calls.
  void finishRideFromDriver1() async {
    // Show a loading indicator to the user
    Get.dialog(Center(child: CircularProgressIndicator()),
        barrierDismissible: false);

    isRideFinished = true;
    isRideStarted = false;
    isPriceWindow = false;
    box.write(BoxName.rideStatus, 'Finished');
    Log.print(
        'rideStatus from map (refactored) : ${box.read(BoxName.rideStatus)}');

    // --- 1. Calculate Total Cost (Logic remains the same) ---
    if (price < 17200) {
      totalCost = (carType == 'Comfort' ||
              carType == 'Mishwar Vip' ||
              carType == 'Lady')
          ? '20000'
          : '17200';
    } else if (price < double.parse(totalPricePassenger)) {
      totalCost = totalPricePassenger;
    } else {
      totalCost = (carType == 'Comfort' ||
              carType == 'Mishwar Vip' ||
              carType == 'Lady')
          ? price.toStringAsFixed(2)
          : totalPricePassenger;
    }
    paymentAmount = totalCost;
    box.write(BoxName.statusDriverLocation, 'off');

    // --- 2. Prepare Payloads for Consolidated API Calls ---
    // final nowString = DateTime.now().toString();

    final rideUpdatePayload = {
      'rideId': rideId.toString(),
      // 'rideTimeFinish': nowString,
      'status': 'Finished',
      'price': totalCost,
    };

    final String paymentAuthToken =
        await generateTokenDriver(paymentAmount.toString());

    final paymentProcessingPayload = {
      'rideId': rideId.toString(),
      'driverId': box.read(BoxName.driverID).toString(),
      'passengerId': passengerId.toString(),
      'paymentAmount': paymentAmount,
      'paymentMethod': paymentMethod,
      'walletChecked': walletChecked.toString(),
      'passengerWalletBurc': passengerWalletBurc.toString(),
      'authToken': paymentAuthToken,
    };

    // --- 3. Execute API Calls in Parallel ---
    try {
      List<Future<dynamic>> apiCalls = [];

      // apiCalls.add(CRUD().post(
      //   link: "${AppLink.rideServer}/rides/finish_ride_updates.php",
      //   payload: rideUpdatePayload,
      // ));
      apiCalls.add(CRUD().post(
        link: "${AppLink.ride}/rides/finish_ride_updates.php",
        payload: rideUpdatePayload,
      ));

      apiCalls.add(CRUD().postWallet(
        link: "${AppLink.paymentServer}/ride/payment/process_ride_payments.php",
        payload: paymentProcessingPayload,
      ));

      final results = await Future.wait(apiCalls);

      // --- 4. *** CRITICAL STEP: Verify BOTH results were successful *** ---
      // Assuming CRUD().post returns a Map<String, dynamic> like {'success': true, 'message': '...'}
      final rideUpdate1 = results[0];
      // final rideUpdate2 = results[1];
      final paymentRealResult = results[1]; // هذا هو الدفع الحقيقي!

      // المنطق: يجب أن ينجح تحديث واحد للرحلة على الأقل + يجب أن ينجح الدفع
      bool isRideUpdated = (rideUpdate1['status'] == 'success');
      bool isPaymentSuccess = (paymentRealResult['status'] == 'success');

      if (isRideUpdated && isPaymentSuccess) {
        // --- SUCCESS: Both API calls succeeded, now proceed ---

        // Dismiss the loading indicator

        Get.put(DriverBehaviorController())
            .sendSummaryToServer(driverId, rideId);

        NotificationService.sendNotification(
          target: tokenPassenger.toString(),
          title: "Driver Finish Trip".tr,
          body: '${'you will pay to Driver'.tr} $paymentAmount \$'.tr,
          isTopic: false, // Important: this is a token
          tone: 'ding',
          driverList: [
            box.read(BoxName.driverID),
            rideId,
            box.read(BoxName.tokenDriver),
            paymentAmount.toString()
          ],
          category: 'Driver Finish Trip',
        );
        Get.back();

        Get.to(() => RatePassenger(), arguments: {
          'passengerId': passengerId,
          'rideId': rideId,
          'price': paymentAmount.toString(),
          'walletChecked': walletChecked
        });
      } else {
        // --- FAILURE: One or both API calls failed ---
        // The transaction on the server side would have been rolled back.
        // We throw an exception to be caught by the catch block, which will revert the UI state.
        throw Exception(
            'Failed. Ride1: ${rideUpdate1['status']},  Payment: ${paymentRealResult['status']}');
      }
    } catch (e) {
      // --- CATCH ALL ERRORS (Network, Server Failure, etc.) ---
      Get.back(); // Dismiss the loading indicator

      Log.print("Error finishing ride: $e");
      Get.snackbar("Error", "Could not finish the ride. Please try again.");

      // Revert state because the operation failed
      isRideFinished = false;
      isRideStarted = true;
      isPriceWindow = true;
      box.write(BoxName.rideStatus, 'InProgress'); // Revert status
    }
  }

  void cancelCheckRideFromPassenger() async {
    var res = await CRUD().get(
        link: "${AppLink.endPoint}/ride/driver_order/getOrderCancelStatus.php",
        payload: {
          'order_id': (rideId),
        }); //.then((value) {
    var response = jsonDecode(res);
    canelString = response['data']['status'];
    update();
    if (canelString == 'Cancel') {
      remainingTimeTimerRideBegin = 0;
      remainingTimeToShowPassengerInfoWindowFromDriver = 0;
      remainingTimeToPassenger = 0;
      isRideStarted = false;
      isRideFinished = false;
      isPassengerInfoWindow = false;
      clearPolyline();
      update();
      MyDialog().getDialog(
        'Order Cancelled'.tr,
        'Order Cancelled by Passenger'.tr,
        () {
          Get.offAll(HomeCaptain());
        },
      );
    }
  }

  int rideTimerFromBegin = 0;
  double price = 0;
  DateTime currentTime = DateTime.now();

  /// أثناء الرحلة: نعرض السعر لحظياً بدون مضاعفة العمولة في كل ثانية.
  /// - نستخدم سعر الدقيقة حسب الوقت، مع قواعد الرحلات البعيدة:
  ///   >25كم أو >35كم => دقيقة = 600، سقف 60 دقيقة، ومع >35كم عفو 10 دقائق.
  /// - سرعة طويلة: لو المسافة المخططة > 40كم نستخدم 2600 ل.س/كم للـ Speed،
////  ونطبق نفس نسبة التخفيض على Comfort/Electric/Van.
  /// - نضيف فقط "الزيادة" فوق التسعيرة المقتبسة (وقت زائد + كم زائد).
  /// - نعكس العمولة kazán مرة واحدة على الزيادة (وليس كل ثانية).

  // =========================================================
  // الدالة الرئيسية المعدلة (العداد)
  // =========================================================
  // متغيرات العداد الجديد
  Timer? _rideTimer;
  DateTime? _rideStartTime;

  void rideIsBeginPassengerTimer() {
    // 1. تنظيف أي عداد سابق
    _rideTimer?.cancel();

    // 2. تحديد وقت البدء الفعلي (لحساب الوقت بدقة حتى لو علق الجهاز)
    _rideStartTime = DateTime.now();

    // 3. تجهيز الثوابت والقيم المبدئية مرة واحدة (Performance)
    final hc = Get.find<HomeCaptainController>();
    final loc = Get.find<LocationController>();

    // الأسعار
    final double perKmSpeedBase = hc.speedPrice;
    final double perKmComfortRaw = hc.comfortPrice;
    final double perKmDelivery = hc.deliveryPrice;
    final double perKmVanRaw = hc.familyPrice;
    const double electricUpliftKm = 400;
    final double perKmElectricRaw = perKmComfortRaw + electricUpliftKm;

    // أسعار الدقائق
    final double perMinNature = hc.naturePrice;
    final double perMinLate = hc.latePrice;
    final double perMinHeavy = hc.heavyPrice;

    // القيم القادمة من الحجز
    final double basePassengerQuote = safeParseDouble(totalCost);
    final int quotedMinutes = safeParseInt(duration) != 0
        ? safeParseInt(duration)
        : safeParseInt(durationOfRideValue);
    final double kazanPct = safeParseDouble(kazan) / 100.0;

    // تحديد المسافة المخططة
    double plannedKm = 0.0;
    try {
      plannedKm = safeParseDouble(distance);
    } catch (_) {}
    final double startKm = loc.totalDistance;
    if (plannedKm <= 0) {
      plannedKm = (startKm > 0) ? startKm : 0.0;
    }

    // فلترة الاهتزاز (Jitter Filter)
    double lastKmForNoise = startKm;
    const double jitterMeters = 10.0;

    // 4. بدء العداد الدوري (كل 1 ثانية لتحديث الواجهة بسلاسة)
    _rideTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      // أمان: إذا انتهت الرحلة، أوقف العداد
      if (box.read(BoxName.rideStatus) != 'Begin') {
        timer.cancel();
        return;
      }

      final now = DateTime.now();
      // حساب الثواني المنقضية بدقة من ساعة النظام
      final int elapsedSeconds = now.difference(_rideStartTime!).inSeconds;

      // -- تحديث المسافة (فلترة الضجيج) --
      double liveKm = loc.totalDistance;
      final double deltaMeters = (liveKm - lastKmForNoise) * 1000.0;

      // إذا كانت الحركة أقل من 10 أمتار، نعتبرها اهتزاز GPS ولا نزيدها
      if (deltaMeters < jitterMeters) {
        liveKm = lastKmForNoise;
      } else {
        lastKmForNoise = liveKm;
      }

      // -- تحديث السعر (كل 5 ثواني لتخفيف الحسابات، أو كل ثانية إذا أردت دقة لحظية) --
      if (elapsedSeconds % 1 == 0) {
        // جعلناها كل ثانية لدقة العرض

        // استدعاء دالة حساب السعر المفصولة (أنظف للكود)
        price = _calculateCurrentPrice(
          now: now,
          elapsedSeconds: elapsedSeconds,
          liveKm: liveKm,
          plannedKm: plannedKm,
          startKm: startKm,
          quotedMinutes: quotedMinutes,
          basePassengerQuote: basePassengerQuote,
          kazanPct: kazanPct,

          // تمرير الأسعار
          perKmSpeedBase: perKmSpeedBase,
          perKmComfortRaw: perKmComfortRaw,
          perKmDelivery: perKmDelivery,
          perKmVanRaw: perKmVanRaw,
          perKmElectricRaw: perKmElectricRaw,
          perMinNature: perMinNature,
          perMinLate: perMinLate,
          perMinHeavy: perMinHeavy,
        );
      }

      // -- تحديث عناصر الواجهة --
      speed = loc.speed * 3.6;

      // حساب الوقت المتبقي (Countdown)
      int totalQuotedSeconds = quotedMinutes * 60;
      int remainingSeconds = totalQuotedSeconds - elapsedSeconds;

      if (remainingSeconds < 0) remainingSeconds = 0;

      remainingTimeTimerRideBegin = remainingSeconds;
      progressTimerRideBegin = (totalQuotedSeconds == 0)
          ? 0.0
          : (elapsedSeconds / totalQuotedSeconds).clamp(0.0, 1.0);

      // تغيير تصميم الصفحة في الدقائق الأخيرة
      remainingTimeTimerRideBegin < 60 ? driverEndPage = 160 : 100;

      // إيقاف بث الموقع للسائق في آخر دقيقة (حسب منطقك القديم)
      if (remainingTimeTimerRideBegin < 60) {
        box.write(BoxName.statusDriverLocation, 'off');
      }

      updateMarker();
      update(); // تحديث GetBuilder
    });
  }

  // =========================================================
  // دالة حساب السعر (منطق مفصول ونظيف)
  // =========================================================
  double _calculateCurrentPrice({
    required DateTime now,
    required int elapsedSeconds,
    required double liveKm,
    required double plannedKm,
    required double startKm,
    required int quotedMinutes,
    required double basePassengerQuote,
    required double kazanPct,
    // الأسعار
    required double perKmSpeedBase,
    required double perKmComfortRaw,
    required double perKmDelivery,
    required double perKmVanRaw,
    required double perKmElectricRaw,
    required double perMinNature,
    required double perMinLate,
    required double perMinHeavy,
  }) {
    // الثوابت الخاصة بالمسافات الطويلة
    const double longSpeedThresholdKm = 40.0;
    const double longSpeedPerKm = 2600.0;
    const double mediumDistThresholdKm = 25.0;
    const double longDistThresholdKm = 35.0;
    const double longTripPerMin = 600.0;
    const int minuteCapMedium = 60;
    const int minuteCapLong = 60;
    const int freeMinutesLong = 10;
    const double extraReduction100 = 0.07;
    const double maxReductionCap = 0.35;

    // 1. تحديد سياق المطار
    bool isAirport(String s) =>
        s.toLowerCase().contains('airport') || s.contains('مطار');
    final bool airportCtx =
        isAirport(startNameLocation) || isAirport(endNameLocation);

    // 2. تحديد المسافة المعتمدة للحساب
    final double plannedForRulesKm = (plannedKm > 0) ? plannedKm : liveKm;

    // 3. تحديد نسبة التخفيض للمسافات الطويلة
    double reductionPct = 0.0;
    if (plannedForRulesKm > 40.0) {
      double r40 = 0.0;
      if (perKmSpeedBase > 0) {
        r40 = (1.0 - (longSpeedPerKm / perKmSpeedBase))
            .clamp(0.0, maxReductionCap);
      }
      reductionPct = r40;
      if (plannedForRulesKm > 100.0) {
        reductionPct = (r40 + extraReduction100).clamp(0.0, maxReductionCap);
      }
    }

    // 4. تحديد سعر الكيلومتر بناءً على النوع والمسافة
    final bool isLong = plannedForRulesKm > longSpeedThresholdKm;

    // حساب أسعار الكيلومتر المخفضة
    final double finalPerKmSpeed = isLong ? longSpeedPerKm : perKmSpeedBase;
    final double finalPerKmBalash =
        (finalPerKmSpeed - 500).clamp(0, double.infinity);
    final double finalPerKmComfort = (perKmComfortRaw * (1.0 - reductionPct));
    final double finalPerKmElectric = (perKmElectricRaw * (1.0 - reductionPct));
    final double finalPerKmVan = (perKmVanRaw * (1.0 - reductionPct));

    double perKmForType;
    switch (carType) {
      case 'Speed':
        perKmForType = finalPerKmSpeed;
        break;
      case 'Awfar Car':
        perKmForType = finalPerKmBalash;
        break;
      case 'Comfort':
      case 'Mishwar Vip':
      case 'RayehGaiComfort':
      case 'Lady':
        perKmForType = finalPerKmComfort;
        break;
      case 'Electric':
        perKmForType = finalPerKmElectric;
        break;
      case 'Van':
        perKmForType = finalPerKmVan;
        break;
      case 'Delivery':
        perKmForType = perKmDelivery;
        break;
      default:
        perKmForType = finalPerKmSpeed;
    }

    // 5. تحديد سعر الدقيقة الفعلي
    double perMinEff;
    int capMinutes = 1 << 30; // رقم كبير جداً (لا سقف)
    int freeMinutes = 0;

    // قواعد المسافات
    if (plannedForRulesKm > longDistThresholdKm) {
      perMinEff = longTripPerMin;
      capMinutes = minuteCapLong;
      freeMinutes = freeMinutesLong;
    } else if (plannedForRulesKm > mediumDistThresholdKm) {
      perMinEff = longTripPerMin;
      capMinutes = minuteCapMedium;
    } else {
      // القواعد الزمنية العادية
      final h = now.hour;
      if (airportCtx) {
        perMinEff = perMinLate;
      } else if (h >= 21 || h < 1) {
        perMinEff = perMinLate;
      } else if (h >= 14 && h <= 17) {
        perMinEff = perMinHeavy;
      } else {
        perMinEff = perMinNature;
      }
    }

    // 6. حساب الدقائق الإضافية
    final int elapsedMinutes = (elapsedSeconds ~/ 60);
    int extraMinutes = 0;
    final int rawExtra = elapsedMinutes - quotedMinutes - freeMinutes;

    if (rawExtra > 0) {
      extraMinutes = rawExtra;
      // تطبيق سقف الدقائق إن وجد
      if (capMinutes < (1 << 30)) {
        final int maxBillable = (capMinutes - quotedMinutes - freeMinutes);
        if (extraMinutes > maxBillable) {
          extraMinutes = maxBillable < 0 ? 0 : maxBillable;
        }
      }
    }

    // 7. حساب الكيلومترات الإضافية
    double extraKm = liveKm - plannedForRulesKm;
    if (plannedKm <= 0) {
      extraKm = liveKm - startKm;
    }
    if (extraKm < 0) extraKm = 0;

    // 8. الحساب النهائي
    final double extraCost =
        (extraMinutes * perMinEff) + (extraKm * perKmForType);

    // إضافة العمولة فقط على الزيادة
    return basePassengerQuote + (extraCost * (1.0 + kazanPct));
  }
  //   price = double.parse(totalCost);

  //   for (int i = 0; i <= durationOfRide; i++) {
  //     await Future.delayed(const Duration(seconds: 1));
  //     recentDistanceToDash = Get.find<LocationController>().totalDistance;
  //     // rideTimerFromBegin = i;
  //     if (int.parse(duration) + 300 > i) {
  //       price = double.parse(totalCost);
  //     } else {
  //       if (startNameLocation.toLowerCase().contains('airport') ||
  //           endNameLocation.toLowerCase().contains('airport') ||
  //           startNameLocation.contains('مطار') ||
  //           startNameLocation.contains('المطار') ||
  //           endNameLocation.contains('مطار') ||
  //           endNameLocation.contains('المطار')) {
  //         price = carType == 'Comfort' // || carType == 'Free Ride'
  //             ? price + ((i ~/ 60) - int.parse(duration)) * latePrice
  //             : carType == 'Lady'
  //                 ? price + ((i ~/ 60) - int.parse(duration)) * latePrice
  //                 : carType == 'RayehGaiComfort'
  //                     ? (i ~/ 60) * latePrice +
  //                         (recentDistanceToDash *
  //                             Get.find<HomeCaptainController>().comfortPrice)
  //                     : (i ~/ 60) * latePrice +
  //                         (recentDistanceToDash *
  //                             Get.find<HomeCaptainController>().mashwariPrice);
  //       } else if (currentTime.hour >= 21 && currentTime.hour < 0) {
  //         price = carType == 'Comfort' // || carType == 'Free Ride'
  //             ? price + ((i ~/ 60) - int.parse(duration)) * latePrice
  //             : carType == 'Lady'
  //                 ? price + ((i ~/ 60) - int.parse(duration)) * latePrice
  //                 : carType == 'RayehGaiComfort'
  //                     ? (i ~/ 60) * latePrice +
  //                         (recentDistanceToDash *
  //                             Get.find<HomeCaptainController>().comfortPrice)
  //                     : (i ~/ 60) * latePrice +
  //                         (recentDistanceToDash *
  //                             Get.find<HomeCaptainController>().mashwariPrice);
  //       } else if (currentTime.hour >= 1 && currentTime.hour < 5) {
  //         if (startNameLocation.contains('club') ||
  //             startNameLocation.contains('nightclub') ||
  //             startNameLocation.contains('ديسكو') ||
  //             startNameLocation.contains('ملهى ليلي') ||
  //             startNameLocation.contains('Night club')) {
  //           price = carType == 'Comfort' // || carType == 'Free Ride'
  //               ? (i ~/ 60) * (latePrice + .5) * 2 +
  //                   (price) -
  //                   int.parse(duration) * (latePrice + .5) * 2
  //               : carType == 'Lady'
  //                   ? (i ~/ 60) * (latePrice + .5) * 2 +
  //                       (price) -
  //                       int.parse(duration) * (latePrice + .5) * 2
  //                   : carType == 'RayehGaiComfort'
  //                       ? (i ~/ 60) * (latePrice + .5) * 2 + (price)
  //                       : (i ~/ 60) * (latePrice + .5) * 2 + (price);
  //         }
  //         price = carType == 'Comfort' // || carType == 'Free Ride'
  //             ? (latePrice + 0.5) * ((i ~/ 60) - int.parse(duration)) + price
  //             : carType == 'Lady'
  //                 ? (latePrice + 0.5) * ((i ~/ 60) - int.parse(duration)) +
  //                     price
  //                 : carType == 'RayehGaiComfort'
  //                     ? (latePrice + 0.5) * ((i ~/ 60) - int.parse(duration)) +
  //                         price
  //                     : price;
  //       } else if (currentTime.hour >= 14 && currentTime.hour <= 17) {
  //         price = carType == 'Comfort' // || carType == 'Free Ride'
  //             ? Get.find<HomeCaptainController>().heavyPrice *
  //                     ((i ~/ 60) - int.parse(duration)) +
  //                 price -
  //                 (0.5 * int.parse(duration))
  //             : carType == 'Lady'
  //                 ? Get.find<HomeCaptainController>().heavyPrice *
  //                         ((i ~/ 60) - int.parse(duration)) +
  //                     price -
  //                     (0.5 * int.parse(duration))
  //                 : carType == 'RayehGaiComfort'
  //                     ? (i ~/ 60) *
  //                             (Get.find<HomeCaptainController>().heavyPrice) +
  //                         (recentDistanceToDash *
  //                             Get.find<HomeCaptainController>().comfortPrice)
  //                     : (i ~/ 60) *
  //                             (Get.find<HomeCaptainController>().heavyPrice) +
  //                         (recentDistanceToDash *
  //                             Get.find<HomeCaptainController>().mashwariPrice);
  //       } else {
  //         price = carType == 'Comfort' // || carType == 'Free Ride'
  //             ? (i ~/ 60) + (price) - int.parse(duration)
  //             : carType == 'Lady'
  //                 ? (i ~/ 60) + (price) - int.parse(duration)
  //                 : carType == 'RayehGaiComfort'
  //                     ? (i ~/ 60) +
  //                         (recentDistanceToDash *
  //                             Get.find<HomeCaptainController>().comfortPrice)
  //                     : (i ~/ 60) +
  //                         (recentDistanceToDash *
  //                             Get.find<HomeCaptainController>().mashwariPrice);
  //       }
  //     }

  //     // $1 for each minute + $4 for each km
  //     price = (price * double.parse(kazan)) + price; // Add 10% tax
  //     speed = Get.find<LocationController>().speed * 3.6;
  //     progressTimerRideBegin = i / durationOfRide;
  //     remainingTimeTimerRideBegin = durationOfRide - i;
  //     remainingTimeTimerRideBegin < 60 ? driverEndPage = 160 : 100;
  //     updateMarker();
  //     if (remainingTimeTimerRideBegin < 60) {
  //       // to make driver available on last 2 minute in his trip
  //       box.write(BoxName.statusDriverLocation, 'off');
  //     }
  //     int minutes = (remainingTimeTimerRideBegin / 60).floor();
  //     int seconds = remainingTimeTimerRideBegin % 60;
  //     stringRemainingTimeRideBegin =
  //         '$minutes:${seconds.toString().padLeft(2, '0')}';
  //     int minutes1 = (i / 60).floor();
  //     int seconds1 = i % 60;
  //     stringRemainingTimeRideBegin1 =
  //         '$minutes1:${seconds1.toString().padLeft(2, '0')}';
  //     update();
  //   }
  // }

  double recentDistanceToDash = 0;
  double recentAngelToMarker = 0;
  double speed = 0;
  void updateMarker() async {
    // Remove the existing marker with the ID `MyLocation`.
    markers.remove(MarkerId('MyLocation'));

    // Add a new marker with the ID `MyLocation` at the current location of the user.
    LocationController locationController = Get.find<LocationController>();
    myLocation = locationController.myLocation;

    markers.add(
      Marker(
        markerId: MarkerId('MyLocation'.tr),
        position: myLocation,
        draggable: true,
        icon: carIcon,
        rotation: locationController.heading,
        // infoWindow: const InfoWindow(
        //   title: 'Time',
        // ),
      ),
    );

    // Animate camera only once after updating the marker
    // mapController!.animateCamera(
    //   CameraUpdate.newLatLng(myLocation),
    // );
    update();
  }

  void addCustomCarIcon() {
    ImageConfiguration config = ImageConfiguration(
        size: const Size(30, 35), devicePixelRatio: Get.pixelRatio);
    BitmapDescriptor.asset(
      config,
      'assets/images/car.png',
      // mipmaps: false,
    ).then((value) {
      carIcon = value;
      update();
    });
  }

  void addCustomStartIcon() async {
// Create the marker with the resized image

    ImageConfiguration config = ImageConfiguration(
        size: const Size(30, 30), devicePixelRatio: Get.pixelRatio);
    BitmapDescriptor.asset(
      config,
      'assets/images/A.png',
    ).then((value) {
      startIcon = value;
      update();
    });
  }

  void addCustomEndIcon() {
    ImageConfiguration config = ImageConfiguration(
        size: const Size(25, 25), devicePixelRatio: Get.pixelRatio);
    BitmapDescriptor.asset(
      config,
      'assets/images/b.png',
    ).then((value) {
      endIcon = value;
      update();
    });
  }

  void addCustomPassengerIcon() {
    ImageConfiguration config = ImageConfiguration(
        size: const Size(30, 30), devicePixelRatio: Get.pixelRatio
        // scale: 1.0,
        );
    BitmapDescriptor.asset(
      config,
      'assets/images/picker.png',
    ).then((value) {
      passengerIcon = value;
      update();
    });
  }

  var activeRouteSteps = <Map<String, dynamic>>[];
  var traveledPathPoints = <LatLng>[]; // المسار المقطوع (رمادي)
  var upcomingPathPoints = <LatLng>[]; // المسار المتبقي (أزرق/أحمر)

  // --- متغيرات الأيقونات والمواقع ---
  var heading = 0.0;
  // ... يمكنك إضافة أيقونات البداية والنهاية هنا

  // --- متغيرات قياس الأداء الذكي ---
  final List<int> _performanceReadings = [];
  final int _readingsToCollect = 10; // اجمع 10 قراءات
  bool _hasMadeDecision = false;
  var updateInterval = 5.obs; // القيمة الافتراضية

  // --- متغيرات داخلية للملاحة ---
  var _stepBounds = <LatLngBounds>[];
  var _stepEndPoints = <LatLng>[];
  var _allPointsForActiveRoute = <LatLng>[];
  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;

    // Check if the point is outside the vertical bounds of the segment
    if ((py < v1y && py < v2y) || (py > v1y && py > v2y)) {
      return false;
    }

    // Calculate the intersection of the ray and the segment
    double intersectX = v1x + (py - v1y) * (v2x - v1x) / (v2y - v1y);

    // Check if the intersection is to the right of the point
    return intersectX > px;
  }

  // Function to check if the point is inside the polygon
  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]; // Loop back to the start

      if (_rayIntersectsSegment(point, vertex1, vertex2)) {
        intersections++;
      }
    }

    // If the number of intersections is odd, the point is inside
    return intersections % 2 != 0;
  }

  String getLocationArea(double latitude, double longitude) {
    LatLng passengerPoint = LatLng(latitude, longitude);

    // 1. فحص الأردن
    if (isPointInPolygon(passengerPoint, CountryPolygons.jordanBoundary)) {
      box.write(BoxName.countryCode, 'Jordan');
      // يمكنك تعيين AppLink.endPoint هنا إذا كان منطقك الداخلي لا يزال يعتمد عليه
      // box.write(BoxName.serverChosen,
      //     AppLink.IntaleqSyriaServer); // مثال: اختر سيرفر سوريا للبيانات
      return 'Jordan';
    }

    // 2. فحص سوريا
    if (isPointInPolygon(passengerPoint, CountryPolygons.syriaBoundary)) {
      box.write(BoxName.countryCode, 'Syria');
      // box.write(BoxName.serverChosen, AppLink.IntaleqSyriaServer);
      return 'Syria';
    }

    // 3. فحص مصر
    if (isPointInPolygon(passengerPoint, CountryPolygons.egyptBoundary)) {
      box.write(BoxName.countryCode, 'Egypt');
      // box.write(BoxName.serverChosen, AppLink.IntaleqAlexandriaServer);
      return 'Egypt';
    }

    // 4. الافتراضي (إذا كان خارج المناطق المخدومة)
    box.write(BoxName.countryCode, 'Jordan');
    // box.write(BoxName.serverChosen, AppLink.IntaleqSyriaServer);
    return 'Unknown Location (Defaulting to Jordan)';
  }

  // استبدل دالة getRoute الحالية في الكنترولر بهذا الكود المعدل
  // هذه النسخة تحتوي على خاصية إعادة المحاولة (Retry) وتستخدم API routec.intaleq.xyz

// استبدل دالة getRoute الحالية في الكنترولر بهذا الكود المعدل
  // هذه النسخة تحتوي على خاصية إعادة المحاولة (Retry) وتستخدم API routec.intaleq.xyz

  Future<void> getRoute({
    required LatLng origin,
    required LatLng destination,
    required Color routeColor,
  }) async {
    // 1. إعداد المتغيرات
    // getLocationArea(origin.latitude, origin.longitude);

    String _dynamicApiUrl = 'https://routec.intaleq.xyz/route';
    final String _routeApiKey = Env.mapKeyOsm;

    var url =
        '$_dynamicApiUrl?origin=${origin.latitude},${origin.longitude}&destination=${destination.latitude},${destination.longitude}&steps=true&overview=full';

    // 2. منطق التكرار (Retry Logic)
    var responseData;
    int retryCount = 0;
    const int maxRetries = 3; // عدد المحاولات القصوى

    while (retryCount < maxRetries) {
      try {
        if (retryCount > 0) {
          print("Retrying route fetch... Attempt ${retryCount + 1}");
        }

        var response = await http.get(
          Uri.parse(url),
          headers: {'X-API-KEY': _routeApiKey},
        );

        if (response.statusCode == 200) {
          var decoded = jsonDecode(response.body);

          // التحقق من صلاحية البيانات (يجب أن تكون status == ok)
          if (decoded != null && decoded['status'] == 'ok') {
            responseData = decoded;
            print("Route fetched successfully on attempt ${retryCount + 1}");
            break; // نجاح! الخروج من الحلقة
          } else {
            print("API returned status: ${decoded?['status']} (Not OK)");
          }
        } else {
          print("Route API returned HTTP error: ${response.statusCode}");
        }
      } catch (e) {
        print("Exception calling route API: $e");
      }

      // إذا وصلنا هنا، فهذا يعني فشل المحاولة
      retryCount++;
      if (retryCount < maxRetries) {
        // انتظار ثانيتين قبل المحاولة التالية لتخفيف الضغط وإعطاء وقت للشبكة
        await Future.delayed(Duration(seconds: 2));
      }
    }

    // 3. التحقق النهائي بعد انتهاء المحاولات
    if (responseData == null || responseData['status'] != 'ok') {
      print("Failed to fetch route after $maxRetries attempts.");
      // هنا يمكنك إظهار رسالة للمستخدم: "تعذر رسم المسار، يرجى التحقق من الشبكة"
      mySnackeBarError("تعذر رسم المسار، يرجى التحقق من الشبكة");
      // isLoading.value = false;
      return;
    }

    // 4. معالجة البيانات (Parsing) - فقط إذا نجحنا في جلبها
    _resetRouteState(); // تنظيف الحالة القديمة

    try {
      // استخراج النقاط ورسم المسار
      final pointsString = responseData["polyline"];

      // تأكد من أن دالة فك التشفير موجودة لديك
      _allPointsForActiveRoute = decodePolylineToLatLng(pointsString);
      upcomingPathPoints.assignAll(_allPointsForActiveRoute);

      // استخراج الخطوات
      final stepsList = List<Map<String, dynamic>>.from(responseData['steps']);

      // تعديل هيكلة الخطوات لتتناسب مع الكود القديم
      for (var step in stepsList) {
        // إنشاء التعليمات من الـ maneuver
        step['html_instructions'] = _createInstructionFromManeuver(step);

        // ضبط الموقع النهائي للخطوة
        if (step['maneuver'] != null && step['maneuver']['location'] != null) {
          var loc = step['maneuver']['location']; // [lng, lat]
          step['end_location'] = {'lat': loc[1], 'lng': loc[0]};
        }
      }

      activeRouteSteps.assignAll(stepsList);
      _prepareStepData(activeRouteSteps);

      // نطق التعليمات الأولية
      if (activeRouteSteps.isNotEmpty) {
        currentInstruction =
            _parseInstruction(activeRouteSteps[0]['html_instructions']);

        if (isTtsEnabled) {
          if (Get.isRegistered<TextToSpeechController>()) {
            Get.find<TextToSpeechController>().speakText(currentInstruction);
          } else {
            Get.put(TextToSpeechController()).speakText(currentInstruction);
          }
        }
      }

      // تحديث حدود الكاميرا (Bounds)
      if (_allPointsForActiveRoute.isNotEmpty) {
        final bounds = _boundsFromLatLngList(_allPointsForActiveRoute);
        _fitToBounds(bounds);
      }

      update(); // تحديث الواجهة
    } catch (e) {
      print("Critical Error parsing route data: $e");
      // هذا الـ Catch يحميك من أي تغيير غير متوقع في شكل البيانات القادمة من السيرفر
    }
  }

  String _createInstructionFromManeuver(Map<String, dynamic> step) {
    final maneuver = step['maneuver'];
    final type = maneuver['type'] ?? 'continue';
    final modifier = maneuver['modifier'] ?? 'straight';
    final name = step['name'] ?? '';

    String instruction = "";

    switch (type) {
      case 'depart':
        instruction = "انطلق";
        break;
      case 'arrive':
        instruction = "لقد وصلت إلى وجهتك";
        if (name.isNotEmpty) instruction += "، $name";
        return instruction;
      case 'turn':
      case 'fork':
      case 'off ramp':
      case 'on ramp':
      case 'roundabout':
        instruction = _getTurnInstruction(modifier);
        break;
      case 'continue':
        instruction = "استمر";
        break;
      default:
        instruction = "اتجه";
    }

    if (name.isNotEmpty) {
      if (instruction == "استمر") {
        instruction += " على $name";
      } else {
        instruction += " إلى $name";
      }
    } else if (type == 'continue' && modifier == 'straight') {
      instruction = "استمر بشكل مستقيم";
    }

    return instruction;
  }

  /**
   * دالة مساعدة لترجمة تعليمات الانعطاف
   */
  String _getTurnInstruction(String modifier) {
    switch (modifier) {
      case 'uturn':
        return "قم بالاستدارة والعودة";
      case 'sharp right':
        return "انعطف يمينًا بحدة";
      case 'right':
        return "انعطف يمينًا";
      case 'slight right':
        return "انعطف يمينًا قليلاً";
      case 'straight':
        return "استمر بشكل مستقيم";
      case 'slight left':
        return "انعطف يسارًا قليلاً";
      case 'left':
        return "انعطف يسارًا";
      case 'sharp left':
        return "انعطف يسارًا بحدة";
      default:
        return "اتجه";
    }
  }

  /**
   * دالة لحساب حدود الخريطة (Bounds) من قائمة نقاط
   */
  LatLngBounds _boundsFromLatLngList(List<LatLng> list) {
    assert(list.isNotEmpty);
    double? x0, x1, y0, y1;
    for (LatLng latLng in list) {
      if (x0 == null) {
        x0 = x1 = latLng.latitude;
        y0 = y1 = latLng.longitude;
      } else {
        if (latLng.latitude > x1!) x1 = latLng.latitude;
        if (latLng.latitude < x0) x0 = latLng.latitude;
        if (latLng.longitude > y1!) y1 = latLng.longitude;
        if (latLng.longitude < y0!) y0 = latLng.longitude;
      }
    }
    return LatLngBounds(
        northeast: LatLng(x1!, y1!), southwest: LatLng(x0!, y0!));
  }
// // دالة موحّدة وقوية لجلب أي مسار
//   Future<void> getRoute({
//     required LatLng origin,
//     required LatLng destination,
//     required Color routeColor,
//   }) async {
//     // إظهار مؤشر التحميل لو رغبت
//     // isLoading.value = true;

//     var url =
//         ('${AppLink.googleMapsLink}directions/json?language=ar&destination=${destination.latitude},${destination.longitude}&origin=${origin.latitude},${origin.longitude}&key=${AK.mapAPIKEY}');
//     var response = await CRUD().getGoogleApi(link: url, payload: {});

//     if (response == null || response['routes'].isEmpty) {
//       // isLoading.value = false;
//       // أظهر رسالة خطأ
//       return;
//     }

//     _resetRouteState(); // تنظيف الحالة القديمة قبل رسم الجديد

//     final route = response['routes'][0];
//     final leg = route['legs'][0];

//     // استخراج النقاط ورسم المسار المبدئي بالكامل
//     final pointsString = route["overview_polyline"]["points"];
//     _allPointsForActiveRoute = decodePolylineToLatLng(pointsString);
//     upcomingPathPoints.assignAll(_allPointsForActiveRoute);

//     // استخراج خطوات الملاحة
//     activeRouteSteps.assignAll(List<Map<String, dynamic>>.from(leg['steps']));
//     _prepareStepData(activeRouteSteps);

//     // تحديث التعليمات الأولية
//     if (activeRouteSteps.isNotEmpty) {
//       currentInstruction =
//           _parseInstruction(activeRouteSteps[0]['html_instructions']);
//       Get.isRegistered<TextToSpeechController>()
//           ? Get.find<TextToSpeechController>().speakText(currentInstruction)
//           : Get.put(TextToSpeechController()).speakText(currentInstruction);
//     }

//     // تحديث الكاميرا لتناسب المسار الجديد
//     final boundsData = route["bounds"];
//     _fitToBounds(LatLngBounds(
//       northeast: LatLng(
//           boundsData['northeast']['lat'], boundsData['northeast']['lng']),
//       southwest: LatLng(
//           boundsData['southwest']['lat'], boundsData['southwest']['lng']),
//     ));

//     // isLoading.value = false;
//     update(); // تحديث الواجهة مرة واحدة بعد كل العمليات
//   }

  // الدالة التي يتم استدعاؤها من خدمة الموقع كل 5 ثوان (أو حسب الفترة المحددة)
  void onLocationUpdated(Position newPosition) {
    myLocation = LatLng(newPosition.latitude, newPosition.longitude);
    heading = newPosition.heading;

    // -->> منطق قياس الأداء يبدأ هنا <<--
    final stopwatch = Stopwatch()..start();

    // -->> منطق الملاحة وتحديث المسار <<--
    _onLocationTick(myLocation);

    stopwatch.stop();

    // -->> تحليل الأداء واتخاذ القرار <<--
    if (!_hasMadeDecision) {
      _performanceReadings.add(stopwatch.elapsedMilliseconds);
      if (_performanceReadings.length >= _readingsToCollect) {
        _analyzePerformance();
        _hasMadeDecision = true;
      }
    }
  }
// =================================================================
  // 3. منطق الملاحة الداخلي (Internal Navigation Logic)
  // =================================================================

  void _onLocationTick(LatLng pos) {
    if (activeRouteSteps.isEmpty || currentStepIndex >= _stepBounds.length) {
      return;
    }

    final double dToEnd =
        _distanceMeters(pos, _stepEndPoints[currentStepIndex]);
    if (dToEnd <= 35) {
      // 35 متر عتبة للوصول لنهاية الخطوة
      _advanceStep();
    }
  }

  void _advanceStep() {
    if (currentStepIndex >= _stepBounds.length - 1) {
      // وصل للنهاية
      currentInstruction = "لقد وصلت إلى وجهتك";
      return;
    }

    currentStepIndex++;
    currentInstruction = _parseInstruction(
        activeRouteSteps[currentStepIndex]['html_instructions']);
    Get.isRegistered<TextToSpeechController>()
        ? Get.find<TextToSpeechController>().speakText(currentInstruction)
        : Get.put(TextToSpeechController()).speakText(currentInstruction);

    // -->> هنا يتم تحديث لون المسار <<--
    _updateTraveledPath();

    _fitToBounds(_stepBounds[currentStepIndex],
        padding: 80); // تقريب الكاميرا على الخطوة التالية
    update();
  }

  void _updateTraveledPath() {
    // استخراج كل النقاط للخطوات التي تم اجتيازها
    List<LatLng> pointsForTraveledSteps = [];
    for (int i = 0; i < currentStepIndex; i++) {
      final stepPolyline = activeRouteSteps[i]['polyline']['points'];
      pointsForTraveledSteps.addAll(decodePolylineToLatLng(stepPolyline));
    }
    traveledPathPoints.assignAll(pointsForTraveledSteps);
  }

  void _prepareStepData(List<Map<String, dynamic>> steps) {
    _stepBounds.clear();
    _stepEndPoints.clear();

    for (final s in steps) {
      // 1. استخراج نقطة النهاية (الكود الحالي سليم)
      final end = s['end_location'];
      _stepEndPoints.add(LatLng(
        (end['lat'] as num).toDouble(),
        (end['lng'] as num).toDouble(),
      ));

      // 2. فك تشفير البوليلاين الخاص بالخطوة وتحويله إلى LatLng
      // -->> هنا تم التصحيح <<--
      List<LatLng> pts = decodePolyline(s['polyline']['points'])
          .map((point) => LatLng(point[0].toDouble(), point[1].toDouble()))
          .toList();

      // أضف نقاط البداية والنهاية إذا لم تكن موجودة في البوليلاين لضمان دقة الحدود
      if (pts.isNotEmpty) {
        final start = s['start_location'];
        final startLatLng = LatLng(
            (start['lat'] as num).toDouble(), (start['lng'] as num).toDouble());
        if (pts.first != startLatLng) {
          pts.insert(0, startLatLng);
        }
      }

      _stepBounds.add(_boundsFromPoints(pts));
    }
  }

// A helper function to decode and convert the polyline string
  List<LatLng> decodePolylineToLatLng(String polylineString) {
    // 1. Decode the string into a list of number lists (e.g., [[lat, lng], ...])
    List<List<num>> decodedPoints = decodePolyline(polylineString);

    // 2. Map each [lat, lng] pair to a LatLng object, ensuring conversion to double
    List<LatLng> latLngPoints = decodedPoints
        .map((point) => LatLng(point[0].toDouble(), point[1].toDouble()))
        .toList();

    return latLngPoints;
  }

  // =================================================================
  // 4. منطق الأداء الذكي (Smart Performance Logic)
  // =================================================================
  void _analyzePerformance() {
    final int sum = _performanceReadings.reduce((a, b) => a + b);
    final double averageTime = sum / _performanceReadings.length;
    if (averageTime > 1000) {
      // إذا كانت العملية تستغرق أكثر من ثانية
      _suggestOptimization();
    }
  }

  void _suggestOptimization() {
    Get.snackbar(
      "تحسين أداء التطبيق",
      "لضمان أفضل تجربة، نقترح تعديل الإعدادات لتناسب جهازك. هل تود المتابعة؟",
      duration: const Duration(seconds: 15),
      mainButton: TextButton(
        child: const Text("نعم، قم بالتحسين"),
        onPressed: () {
          updateInterval.value = 8; // غير الفترة إلى 8 ثوانٍ
          // save setting to shared_preferences
          box.write(BoxName.updateInterval, 8);
          Get.back();
        },
      ),
    );
  }

  // =================================================================
  // 5. دوال مساعدة (Helper Functions)
  // =================================================================
  void _resetRouteState() {
    activeRouteSteps.clear();
    traveledPathPoints.clear();
    upcomingPathPoints.clear();
    _allPointsForActiveRoute.clear();
    currentStepIndex = 0;
  }

  String _parseInstruction(String html) =>
      html.replaceAll(RegExp(r'<[^>]*>'), '');

  Future<void> _fitToBounds(LatLngBounds b, {double padding = 60}) async {
    // نستخدم الدالة الآمنة التي أنشأناها
    await safeAnimateCamera(CameraUpdate.newLatLngBounds(b, padding));
  }

  double distanceBetweenDriverAndPassengerWhenConfirm = 0;
  getMap(String origin, destination) async {
    isLoading = false;

    update();

    var url =
        ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');

    var response = await CRUD().getGoogleApi(link: url, payload: {});
    Log.print('response: $response');
    data = response['routes'][0]['legs'];
    distanceBetweenDriverAndPassengerWhenConfirm =
        (data[0]['distance']['value']) / 1000;
    final points =
        decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
    for (int i = 0; i < points.length; i++) {
      double lat = points[i][0].toDouble();
      double lng = points[i][1].toDouble();
      polylineCoordinates.add(LatLng(lat, lng));
    }
    if (polyLines.isNotEmpty) {
      clearPolyline();
      var polyline = Polyline(
        polylineId: PolylineId(response["routes"][0]["summary"]),
        points: polylineCoordinates,
        width: 10,
        color: AppColor.blueColor,
      );
      polyLines.add(polyline);
      // rideConfirm = false;
      update();
    } else {
      var polyline = Polyline(
        polylineId: PolylineId(response["routes"][0]["summary"]),
        points: polylineCoordinates,
        width: 10,
        color: AppColor.blueColor,
      );
      // final dataBounds = response["routes"][0]["bounds"];

      // updateCameraFromBoundsAfterGetMap(dataBounds);

// Fit the camera to the bounds

      polyLines.add(polyline);
      // rideConfirm = false;
      //  Define the northeast and southwest coordinates
      final bounds = response["routes"][0]["bounds"];
      LatLng northeast =
          LatLng(bounds['northeast']['lat'], bounds['northeast']['lng']);
      LatLng southwest =
          LatLng(bounds['southwest']['lat'], bounds['southwest']['lng']);

// Create the LatLngBounds object
      boundsData = LatLngBounds(northeast: northeast, southwest: southwest);

// Fit the camera to the bounds
      var cameraUpdate = CameraUpdate.newLatLngBounds(boundsData!, 140);
      await safeAnimateCamera(cameraUpdate);
      update();
    }
  }

  void checkForNextStep(LatLng currentPosition) {
    if (currentStepIndex >= routeSteps.length) return;

    final step = routeSteps[currentStepIndex];
    final endLocation = step['end_location'];
    final endLatLng = LatLng(endLocation['lat'], endLocation['lng']);

    final distance = calculateDistance(
      currentPosition.latitude,
      currentPosition.longitude,
      endLatLng.latitude,
      endLatLng.longitude,
    );

    if (distance < 50) {
      // 50 متر قبل النقطة
      currentStepIndex++;
      if (currentStepIndex < routeSteps.length) {
        currentInstruction = _parseInstruction(
            routeSteps[currentStepIndex]['html_instructions']);
        Get.isRegistered<TextToSpeechController>()
            ? Get.find<TextToSpeechController>().speakText(currentInstruction)
            : Get.put(TextToSpeechController()).speakText(currentInstruction);
        Log.print('Current Instruction: $currentInstruction');
        update();
      }
    }
  }

  /// Calculates the distance in meters between two latitude/longitude points.
  double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
    const double earthRadius = 6371000; // meters
    double dLat = _degreesToRadians(lat2 - lat1);
    double dLon = _degreesToRadians(lon2 - lon1);

    double a = (sin(dLat / 2) * sin(dLat / 2)) +
        cos(_degreesToRadians(lat1)) *
            cos(_degreesToRadians(lat2)) *
            (sin(dLon / 2) * sin(dLon / 2));
    double c = 2 * atan2(sqrt(a), sqrt(1 - a));
    double distance = earthRadius * c;
    return distance;
  }

  double _degreesToRadians(double degrees) {
    return degrees * (3.1415926535897932 / 180.0);
  }

  // String _parseInstruction(String htmlInstruction) {
  //   return htmlInstruction.replaceAll(RegExp(r'<[^>]*>'), '');
  // }

  void checkDestinationProximity() {
    final distance = calculateDistance(
      myLocation.latitude,
      myLocation.longitude,
      latLngPassengerDestination.latitude,
      latLngPassengerDestination.longitude,
    );

    if (distance < 300) {
      // 300 متر قبل الوجهة
      // Get.find<FirebaseMessagesController>().sendNotificationToDriverMAP(
      //   "You are near the destination",
      //   "You are near the destination".tr,
      //   tokenPassenger,
      //   [
      //     box.read(BoxName.driverID),
      //     rideId,
      //     box.read(BoxName.tokenDriver),
      //     paymentAmount.toString()
      //   ],
      //   'ding.wav',
      // );
      NotificationService.sendNotification(
        target: tokenPassenger.toString(),
        title: "You are near the destination".tr,
        body: "You are near the destination".tr,
        isTopic: false, // Important: this is a token
        tone: 'ding',
        driverList: [
          box.read(BoxName.driverID),
          rideId,
          box.read(BoxName.tokenDriver),
          paymentAmount.toString()
        ],
        category: "You are near the destination",
      );
      // يمكن إضافة أي إجراء آخر هنا عند الاقتراب من الوجهة
    }
  }

  List<LatLngBounds> stepBounds = [];
  List<LatLng> stepEndPoints = [];
  List<String> stepInstructions = [];
  StreamSubscription<Position>? _posSub;
  DateTime _lastCameraUpdateTs = DateTime.fromMillisecondsSinceEpoch(0);

  getMapDestination(String origin, destination) async {
    var url =
        ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');

    var response = await CRUD().getGoogleApi(link: url, payload: {});
    dataDestination = response['routes'][0]['legs'];
    final points =
        decodePolyline(response["routes"][0]["overview_polyline"]["points"]);

    polylineCoordinatesDestination.clear();
    for (int i = 0; i < points.length; i++) {
      double lat = points[i][0].toDouble();
      double lng = points[i][1].toDouble();
      polylineCoordinatesDestination.add(LatLng(lat, lng));
    }

    // الخطوات من أول leg
    routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);

    // حضّر بيانات كل step
    _prepareStepData(routeSteps);

    currentStepIndex = 0;
    if (stepInstructions.isNotEmpty) {
      currentInstruction = _parseInstruction(stepInstructions[0]);
      Log.print('currentInstruction: $currentInstruction');
      Get.isRegistered<TextToSpeechController>()
          ? Get.find<TextToSpeechController>().speakText(currentInstruction)
          : Get.put(TextToSpeechController()).speakText(currentInstruction);
    }

    // ارسم الـ polyline الرئيسي (مثل ما لديك)
    final summary = response["routes"][0]["summary"];
    final polyline = Polyline(
      polylineId: PolylineId(summary.isEmpty ? 'route' : summary),
      points: polylineCoordinatesDestination,
      width: 10,
      color: AppColor.redColor,
    );
    polyLinesDestination.add(polyline);

    // fit للخريطة على أول step
    if (stepBounds.isNotEmpty) {
      _fitToBounds(stepBounds[0]);
    }

    update();

    // // ابدأ التتبع الحيّ لخطوات الملاحة
    // await startListeningStepNavigation();
  }

// ————————————————————————————————————————————————————————————————
// يُنادى عند كل تحديث للموقع
  // void _onLocationTick(LatLng pos) async {
  //   if (routeSteps.isEmpty || currentStepIndex >= stepBounds.length) return;

  //   // إذا تجاوزت نهاية الـ step الحالية بمسافة صغيرة -> انتقل
  //   final double dToEnd = _distanceMeters(pos, stepEndPoints[currentStepIndex]);
  //   final bool nearEnd = dToEnd <= 25; // عتبة 25م (عدّلها حسب حاجتك)

  //   // إذا دخلت نطاق الـ step التالية -> انتقل
  //   bool insideNext = false;
  //   if (currentStepIndex < stepBounds.length - 1) {
  //     insideNext = _contains(stepBounds[currentStepIndex + 1], pos);
  //   }

  //   if (nearEnd || insideNext) {
  //     _advanceStep();
  //     return;
  //   }

  //   // تحديث الكاميرا بشكل خفيف أثناء الحركة (كل 2 ثانية مثلاً)
  //   final now = DateTime.now();
  //   if (now.difference(_lastCameraUpdateTs).inMilliseconds > 2000) {
  //     _lastCameraUpdateTs = now;
  //     // ركّز على bounds الحالية مع الحفاظ على تتبّع عام
  //     _fitToBounds(stepBounds[currentStepIndex], padding: 60);
  //   }
  // }

  LatLngBounds _boundsFromPoints(List<LatLng> pts) {
    double? minLat, maxLat, minLng, maxLng;
    for (final p in pts) {
      minLat = (minLat == null) ? p.latitude : math.min(minLat, p.latitude);
      maxLat = (maxLat == null) ? p.latitude : math.max(maxLat, p.latitude);
      minLng = (minLng == null) ? p.longitude : math.min(minLng, p.longitude);
      maxLng = (maxLng == null) ? p.longitude : math.max(maxLng, p.longitude);
    }
    return LatLngBounds(
      southwest: LatLng(minLat ?? 0, minLng ?? 0),
      northeast: LatLng(maxLat ?? 0, maxLng ?? 0),
    );
  }

  bool _contains(LatLngBounds b, LatLng p) {
    final south = math.min(b.southwest.latitude, b.northeast.latitude);
    final north = math.max(b.southwest.latitude, b.northeast.latitude);
    final west = math.min(b.southwest.longitude, b.northeast.longitude);
    final east = math.max(b.southwest.longitude, b.northeast.longitude);
    return (p.latitude >= south &&
        p.latitude <= north &&
        p.longitude >= west &&
        p.longitude <= east);
  }

  double _distanceMeters(LatLng a, LatLng b) {
    // هافرساين مبسطة
    const R = 6371000.0; // m
    final dLat = _deg2rad(b.latitude - a.latitude);
    final dLng = _deg2rad(b.longitude - a.longitude);
    final s1 = math.sin(dLat / 2);
    final s2 = math.sin(dLng / 2);
    final aa = s1 * s1 +
        math.cos(_deg2rad(a.latitude)) *
            math.cos(_deg2rad(b.latitude)) *
            s2 *
            s2;
    final c = 2 * math.atan2(math.sqrt(aa), math.sqrt(1 - aa));
    return R * c;
  }

  double _deg2rad(double d) => d * math.pi / 180.0;

// تحريك الكاميرا لباوند معيّن
  // Future<void> _fitToBounds(LatLngBounds b, {double padding = 40}) async {
  //   if (mapController == null) return;
  //   try {
  //     // أحياناً يلزم انتظار فريم حتى تكون الخريطة مرسومة
  //     await Future.delayed(const Duration(milliseconds: 50));
  //     await mapController!.animateCamera(
  //       CameraUpdate.newLatLngBounds(b, padding),
  //     );
  //   } catch (_) {
  //     // fallback لو حصلت مشكلة الحجم
  //     final center = LatLng(
  //       (b.northeast.latitude + b.southwest.latitude) / 2,
  //       (b.northeast.longitude + b.southwest.longitude) / 2,
  //     );
  //     await mapController!.animateCamera(CameraUpdate.newLatLng(center));
  //   }
  // }

// الانتقال للخطوة التالية وتحديث التعليمات والكاميرا
  // void _advanceStep() {
  //   if (currentStepIndex >= stepBounds.length - 1) return;

  //   currentStepIndex++;
  //   currentInstruction = _parseInstruction(stepInstructions[currentStepIndex]);

  //   // نطق التعليمات
  //   if (Get.isRegistered<TextToSpeechController>()) {
  //     Get.find<TextToSpeechController>().speakText(currentInstruction);
  //   } else {
  //     Get.put(TextToSpeechController()).speakText(currentInstruction);
  //   }

  //   // تركيز الكاميرا على باوند الخطوة الجديدة
  //   _fitToBounds(stepBounds[currentStepIndex]);
  //   update();
  // }

  // void _prepareStepData(List<Map<String, dynamic>> steps) {
  //   stepBounds.clear();
  //   stepEndPoints.clear();
  //   stepInstructions.clear();

  //   for (final s in steps) {
  //     // 1) instruction
  //     final html = (s['html_instructions'] ?? '').toString();
  //     stepInstructions.add(html);

  //     // 2) end point
  //     final end = s['end_location'];
  //     final endLatLng = LatLng(
  //         (end['lat'] as num).toDouble(), (end['lng'] as num).toDouble());
  //     stepEndPoints.add(endLatLng);

  //     // 3) bounds من الـ polyline (إن لم يوجد bounds جاهز من الـ API)
  //     List<LatLng> pts = [];
  //     if (s['polyline'] != null && s['polyline']['points'] != null) {
  //       final decoded = decodePolyline(s['polyline']['points']);
  //       for (var p in decoded) {
  //         pts.add(LatLng((p[0] as num).toDouble(), (p[1] as num).toDouble()));
  //       }
  //     } else {
  //       // fallback: استخدم start/end فقط
  //       final start = s['start_location'];
  //       pts.add(LatLng((start['lat'] as num).toDouble(),
  //           (start['lng'] as num).toDouble()));
  //       pts.add(endLatLng);
  //     }
  //     stepBounds.add(_boundsFromPoints(pts));
  //   }
  // }
//   getMapDestination(String origin, destination) async {
//     var url =
//         ('${AppLink.googleMapsLink}directions/json?&language=${box.read(BoxName.lang)}&avoid=tolls|ferries&destination=$destination&origin=$origin&key=${AK.mapAPIKEY}');

//     var response = await CRUD().getGoogleApi(link: url, payload: {});
//     dataDestination = response['routes'][0]['legs'];
//     final points =
//         decodePolyline(response["routes"][0]["overview_polyline"]["points"]);
//     for (int i = 0; i < points.length; i++) {
//       double lat = points[i][0].toDouble();
//       double lng = points[i][1].toDouble();
//       polylineCoordinatesDestination.add(LatLng(lat, lng));
//     }
//     // استخراج الخطوات
//     routeSteps = List<Map<String, dynamic>>.from(dataDestination[0]['steps']);
//     Log.print('routeSteps: ${routeSteps}');
//     currentStepIndex = 0;
//     if (routeSteps.isNotEmpty) {
//       currentInstruction =
//           _parseInstruction(routeSteps[0]['html_instructions']);
//       Log.print('currentInstruction: ${currentInstruction}');
//       Get.isRegistered<TextToSpeechController>()
//           ? Get.find<TextToSpeechController>().speakText(currentInstruction)
//           : Get.put(TextToSpeechController()).speakText(currentInstruction);
//     }
//     update();

// // دالة مساعدة لتنظيف التعليمات

//     if (polyLinesDestination.isNotEmpty) {
//       // clearPolyline();
//       var polyline = Polyline(
//         polylineId: PolylineId(response["routes"][0]["summary"]),
//         points: polylineCoordinatesDestination,
//         width: 10,
//         color: AppColor.redColor,
//       );
//       polyLinesDestination.add(polyline);
//       // rideConfirm = false;
//       update();
//     } else {
//       var polyline = Polyline(
//         polylineId: PolylineId(response["routes"][0]["summary"]),
//         points: polylineCoordinatesDestination,
//         width: 10,
//         color: AppColor.redColor,
//       );
//       // final dataBounds = response["routes"][0]["bounds"];

//       // updateCameraFromBoundsAfterGetMap(dataBounds);
//       // polyLinesDestination.add(polyline);
//       // rideConfirm = false;
//       //  Define the northeast and southwest coordinates

//       update();
//     }
//   }

  void updateCameraFromBoundsAfterGetMap(dynamic response) {
    final bounds = response["routes"][0]["bounds"];
    LatLng northeast =
        LatLng(bounds['northeast']['lat'], bounds['northeast']['lng']);
    LatLng southwest =
        LatLng(bounds['southwest']['lat'], bounds['southwest']['lng']);

// Create the LatLngBounds object
    LatLngBounds boundsData =
        LatLngBounds(northeast: northeast, southwest: southwest);

// Fit the camera to the bounds
    var cameraUpdate = CameraUpdate.newLatLngBounds(boundsData, 140);
    safeAnimateCamera(cameraUpdate);
  }

  void changePassengerInfoWindow() {
    isPassengerInfoWindow = !isPassengerInfoWindow;
    passengerInfoWindowHeight = isPassengerInfoWindow == true ? 200 : 0;
    update();
  }

  double mpg = 0;
  calculateConsumptionFuel() {
    mpg = Get.find<HomeCaptainController>().fuelPrice /
        12; //todo in register car add mpg in box
    update();
  }

  argumentLoading() async {
    try {
      passengerLocation = Get.arguments['passengerLocation'];
      passengerDestination = Get.arguments['passengerDestination'];
      duration = Get.arguments['Duration'];
      totalCost = Get.arguments['totalCost'];
      passengerId = Get.arguments['passengerId'];
      driverId = Get.arguments['driverId'];
      distance = Get.arguments['Distance'];
      passengerName = Get.arguments['name'];
      passengerEmail = Get.arguments['email'];
      totalPricePassenger = Get.arguments['totalPassenger'];
      passengerPhone = Get.arguments['phone'];
      walletChecked = Get.arguments['WalletChecked'];
      tokenPassenger = Get.arguments['tokenPassenger'];
      direction = Get.arguments['direction'];
      durationToPassenger = Get.arguments['DurationToPassenger'];
      rideId = Get.arguments['rideId'];
      durationOfRideValue = Get.arguments['durationOfRideValue'];
      paymentAmount = Get.arguments['paymentAmount'];
      paymentMethod = Get.arguments['paymentMethod'];
      isHaveSteps = Get.arguments['isHaveSteps'];
      step0 = Get.arguments['step0'];
      step1 = Get.arguments['step1'];
      step2 = Get.arguments['step2'];
      step3 = Get.arguments['step3'];
      step4 = Get.arguments['step4'];
      passengerWalletBurc = Get.arguments['passengerWalletBurc'];
      timeOfOrder = Get.arguments['timeOfOrder'];
      carType = Get.arguments['carType'];
      kazan = Get.arguments['kazan'];
      startNameLocation = Get.arguments['startNameLocation'];
      endNameLocation = Get.arguments['endNameLocation'];

// Parse to double
      latlng(passengerLocation, passengerDestination);

      String lat =
          Get.find<LocationController>().myLocation.latitude.toString();
      String lng =
          Get.find<LocationController>().myLocation.longitude.toString();
      String origin = '$lat,$lng';
      // Set the origin and destination coordinates for the Google Maps directions request.
      Future.delayed(const Duration(seconds: 1));
      getRoute(
          origin: Get.find<LocationController>().myLocation,
          destination: latLngPassengerLocation,
          routeColor: Colors.yellow // أو أي لون
          );
      update();
    } catch (e) {
      Log.print("Error parsing arguments: $e");
    }
  }

  latlng(String passengerLocation, passengerDestination) {
    double latPassengerLocation =
        double.parse(passengerLocation.toString().split(',')[0]);
    double lngPassengerLocation =
        double.parse(passengerLocation.toString().split(',')[1]);
    double latPassengerDestination =
        double.parse(passengerDestination.toString().split(',')[0]);
    double lngPassengerDestination =
        double.parse(passengerDestination.toString().split(',')[1]);
    latLngPassengerLocation =
        LatLng(latPassengerLocation, lngPassengerLocation);
    latLngPassengerDestination =
        LatLng(latPassengerDestination, lngPassengerDestination);
  }

  late Duration durationToAdd;
  int hours = 0;
  int minutes = 0;
  String carType = '';
  late String kazan;
  late String startNameLocation;
  late String endNameLocation;

  Future<void> runGoogleMapDirectly() async {
    if (box.read(BoxName.googlaMapApp) == true) {
      if (Platform.isAndroid) {
        Bubble().startBubbleHead(sendAppToBackground: true);
      }
      await openGoogleMapFromDriverToPassenger();
    }
  }

  @override
  void onInit() async {
    mapAPIKEY = await storage.read(key: BoxName.mapAPIKEY);
    // Get the passenger location from the arguments.
    await argumentLoading();
    Get.put(FirebaseMessagesController());
    runGoogleMapDirectly();
    addCustomCarIcon();
    addCustomPassengerIcon();
    addCustomStartIcon();
    addCustomEndIcon();
    // updateMarker();
    // updateLocation();
    startTimerToShowPassengerInfoWindowFromDriver();
    durationToAdd = Duration(seconds: int.parse(duration));
    hours = durationToAdd.inHours;
    minutes = (durationToAdd.inMinutes % 60).round();
    calculateConsumptionFuel();
    updateLocation();
    // cancelCheckRidefromPassenger();
    // checkIsDriverNearPassenger();
    super.onInit();
  }

  Timer? _navigationTimer;
  Future<void> startListeningStepNavigation() async {
    _posSub?.cancel();
    _navigationTimer?.cancel();

    // تعريف متغير لتخزين آخر موقع
    Position? lastKnownPosition;

    _posSub = Geolocator.getPositionStream(
      locationSettings: LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 5,
      ),
    ).listen((position) {
      myLocation = LatLng(position.latitude, position.longitude);
      heading = position.heading;
      lastKnownPosition = position; // تحديث الموقع
      update();
    });

    _navigationTimer = Timer.periodic(
        Duration(seconds: box.read(BoxName.updateInterval) ?? 5), (timer) {
      // التحقق من أن الموقع ليس null وأن الكنترولر لم يتم إغلاقه
      if (lastKnownPosition != null && !isClosed) {
        onLocationUpdated(lastKnownPosition!);
      }
    });
  }

  void stopListeningStepNavigation() {
    _posSub?.cancel();
    _posSub = null;
  }
}

double safeParseDouble(dynamic value, {double defaultValue = 0.0}) {
  if (value == null) return defaultValue;
  if (value is double) return value;
  if (value is int) return value.toDouble();
  return double.tryParse(value.toString()) ?? defaultValue;
}

int safeParseInt(dynamic value, {int defaultValue = 0}) {
  if (value == null) return defaultValue;
  if (value is int) return value;
  return int.tryParse(value.toString()) ?? defaultValue;
}
