import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:convert'; // <<<--- إضافة جديدة
import 'package:flutter/foundation.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:http/http.dart' as http; // <<<--- إضافة جديدة
import 'package:sefer_driver/constant/colors.dart';
import 'package:sefer_driver/controller/functions/crud.dart';

import '../../../constant/box_name.dart';
import '../../../constant/country_polygons.dart';
import '../../../constant/links.dart';
import '../../../env/env.dart';
import '../../../main.dart';
import '../../../print.dart';
// import '../../functions/crud.dart'; // <<<--- تم إلغاء الاعتماد عليه
import '../../functions/tts.dart';
import 'decode_polyline_isolate.dart';

class NavigationController extends GetxController {
  // --- متغيرات الحالة العامة ---
  bool isLoading = false;
  GoogleMapController? mapController;
  final TextEditingController placeDestinationController =
      TextEditingController();

  // --- متغيرات الخريطة والموقع ---
  LatLng? myLocation;
  double heading = 0.0;
  final Set<Marker> markers = {};
  final Set<Polyline> polylines = {};
  BitmapDescriptor carIcon = BitmapDescriptor.defaultMarker;
  BitmapDescriptor destinationIcon = BitmapDescriptor.defaultMarker;

  // --- متغيرات النظام الذكي للتحديث ---
  Timer? _locationUpdateTimer;
  Duration _currentUpdateInterval = const Duration(seconds: 2);

  // --- متغيرات البحث عن الأماكن ---
  List<dynamic> placesDestination = [];
  Timer? _debounce;

  // --- متغيرات الملاحة (Navigation) ---
  LatLng? _finalDestination;
  List<Map<String, dynamic>> routeSteps = [];
  List<LatLng> _fullRouteCoordinates = [];
  List<List<LatLng>> _stepPolylines = [];
  bool _nextInstructionSpoken = false;

  String currentInstruction = "";
  String nextInstruction = "";
  int currentStepIndex = 0;
  // <<<--- [تعديل] ---: متغير جديد لتتبع المسار المقطوع بدقة
  int _lastTraveledIndexInFullRoute = 0;
  double currentSpeed = 0.0;
  String distanceToNextStep = "";
  final List<LatLngBounds> _stepBounds = [];

  // --- ثوابت الـ API الجديد ---
  static const String _routeApiBaseUrl = 'https://routec.intaleq.xyz/';
  static final String _routeApiKey = Env.mapKeyOsm;

  @override
  void onInit() {
    super.onInit();
    _initialize();
  }

  Future<void> _initialize() async {
    await _loadCustomIcons();
    await _getCurrentLocationAndStartUpdates();
    if (!Get.isRegistered<TextToSpeechController>()) {
      Get.put(TextToSpeechController());
    }
  }

  @override
  void onClose() {
    _locationUpdateTimer?.cancel();
    mapController?.dispose();
    _debounce?.cancel();
    placeDestinationController.dispose();
    super.onClose();
  }

  // =======================================================================
  // ١. النظام الذكي لتحديد الموقع والتحديث
  // =======================================================================

// Helper function to check if a ray from the point intersects with a polygon segment
  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)';
  }

  Future<void> _getCurrentLocationAndStartUpdates() async {
    try {
      Position position = await Geolocator.getCurrentPosition(
          desiredAccuracy: LocationAccuracy.high);
      myLocation = LatLng(position.latitude, position.longitude);
      getLocationArea(myLocation!.latitude, myLocation!.longitude);
      update();
      animateCameraToPosition(myLocation!);
      _startLocationTimer();
    } catch (e) {
      print("Error getting location: $e");
    }
  }

  void _startLocationTimer() {
    _locationUpdateTimer?.cancel();
    _locationUpdateTimer = Timer.periodic(_currentUpdateInterval, (timer) {
      _updateLocationAndProcess();
    });
  }

  Future<void> _updateLocationAndProcess() async {
    try {
      final position = await Geolocator.getCurrentPosition(
          desiredAccuracy: LocationAccuracy.high);
      myLocation = LatLng(position.latitude, position.longitude);
      heading = position.heading;
      currentSpeed = position.speed * 3.6;

      _updateCarMarker();

      if (polylines.isNotEmpty && myLocation != null) {
        animateCameraToPosition(
          myLocation!,
          bearing: heading,
          zoom: 18.5,
        );

        _checkNavigationStep(myLocation!);
      }
      update();
    } catch (e) {
      print("Error in _updateLocationAndProcess: $e");
    }
  }

  void _adjustUpdateInterval() {
    if (currentStepIndex >= routeSteps.length) return;

    final currentStepDistance = routeSteps[currentStepIndex]['distance'];

    if (currentStepDistance > 1500) {
      _currentUpdateInterval = const Duration(seconds: 4);
    } else {
      _currentUpdateInterval = const Duration(seconds: 2);
    }

    _startLocationTimer();
  }

  void onMapCreated(GoogleMapController controller) {
    mapController = controller;
    if (myLocation != null) {
      animateCameraToPosition(myLocation!);
    }
  }

  void _updateCarMarker() {
    if (myLocation == null) return;
    markers.removeWhere((m) => m.markerId.value == 'myLocation');
    markers.add(
      Marker(
        markerId: const MarkerId('myLocation'),
        position: myLocation!,
        icon: carIcon,
        rotation: heading,
        anchor: const Offset(0.5, 0.5),
        flat: true,
      ),
    );
  }

  void animateCameraToPosition(LatLng position,
      {double zoom = 16.0, double bearing = 0.0}) {
    mapController?.animateCamera(
      CameraUpdate.newCameraPosition(
        CameraPosition(
            target: position, zoom: zoom, bearing: bearing, tilt: 45.0),
      ),
    );
  }

  // =======================================================================
  // ٢. الملاحة والتحقق من الخطوات
  // =======================================================================

  void _checkNavigationStep(LatLng currentPosition) {
    if (routeSteps.isEmpty ||
        currentStepIndex >= routeSteps.length ||
        _finalDestination == null) return;

    // <<<--- [تعديل] ---: استدعاء الدالة الجديدة والمحسنة
    _updateTraveledPolyline(currentPosition);

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

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

    distanceToNextStep = (distance > 1000)
        ? "${(distance / 1000).toStringAsFixed(1)} كم"
        : "${distance.toStringAsFixed(0)} متر";

    if (distance < 30 &&
        !_nextInstructionSpoken &&
        nextInstruction.isNotEmpty) {
      Get.find<TextToSpeechController>().speakText(nextInstruction);
      _nextInstructionSpoken = true;
    }

    if (distance < 20) {
      _advanceStep();
    }
  }

  void _advanceStep() {
    currentStepIndex++;
    if (currentStepIndex < routeSteps.length) {
      currentInstruction = routeSteps[currentStepIndex]['instruction_text'];
      nextInstruction = ((currentStepIndex + 1) < routeSteps.length)
          ? routeSteps[currentStepIndex + 1]['instruction_text']
          : "الوجهة النهائية";
      _nextInstructionSpoken = false;

      _adjustUpdateInterval();

      if (currentStepIndex < _stepBounds.length) {
        mapController?.animateCamera(
            CameraUpdate.newLatLngBounds(_stepBounds[currentStepIndex], 70.0));
      }
      update();
    } else {
      currentInstruction = "لقد وصلت إلى وجهتك.";
      nextInstruction = "";
      distanceToNextStep = "";
      _locationUpdateTimer?.cancel();
      Get.find<TextToSpeechController>().speakText(currentInstruction);
      update();
    }
  }

  // =======================================================================
  // ٣. تحسين خوارزمية البحث ورسم المسار المقطوع
  // =======================================================================

  // <<<--- [تعديل] ---: تم إعادة كتابة الدالة بالكامل
  void _updateTraveledPolyline(LatLng currentPosition) {
    // 1. التأكد من أن المسار الكامل محمل
    if (_fullRouteCoordinates.isEmpty) return;

    double minDistance = double.infinity;
    // 2. ابدأ البحث دائماً من النقطة الأخيرة التي تم الوصول إليها
    int newClosestIndex = _lastTraveledIndexInFullRoute;

    // 3. ابحث للأمام فقط (من آخر نقطة مسجلة إلى نهاية المسار)
    for (int i = _lastTraveledIndexInFullRoute;
        i < _fullRouteCoordinates.length;
        i++) {
      final point = _fullRouteCoordinates[i];
      final distance = Geolocator.distanceBetween(
        currentPosition.latitude,
        currentPosition.longitude,
        point.latitude,
        point.longitude,
      );

      if (distance < minDistance) {
        minDistance = distance;
        newClosestIndex = i;
      } else if (distance > minDistance + 50) {
        // 4. تحسين: إذا بدأت المسافة بالزيادة، فتوقف عن البحث
        // هذا يعني أننا تجاوزنا أقرب نقطة (50 متر هامش أمان)
        break;
      }
    }

    // 5. قم بتحديث آخر نقطة مسجلة
    _lastTraveledIndexInFullRoute = newClosestIndex;

    // 6. ارسم المسار المقطوع (من البداية إلى أقرب نقطة)
    List<LatLng> traveledPoints =
        _fullRouteCoordinates.sublist(0, newClosestIndex + 1);
    traveledPoints.add(currentPosition); // أضف الموقع الحالي لنعومة الخط

    // 7. ارسم المسار المتبقي (من أقرب نقطة إلى النهاية)
    List<LatLng> remainingPoints =
        _fullRouteCoordinates.sublist(newClosestIndex);
    remainingPoints.insert(0, currentPosition); // ابدأ من الموقع الحالي

    // 8. تحديث الخطوط على الخريطة
    polylines.removeWhere((p) => p.polylineId.value == 'traveled_route');
    polylines.add(Polyline(
      polylineId: const PolylineId('traveled_route'),
      points: traveledPoints,
      color: Colors.grey.shade600,
      width: 7,
    ));

    polylines.removeWhere((p) => p.polylineId.value == 'remaining_route');
    polylines.add(Polyline(
      polylineId: const PolylineId('remaining_route'),
      points: remainingPoints,
      color: const Color(0xFF4A80F0),
      width: 7,
    ));
  }

  // <<<--- [إلغاء] ---: لم نعد بحاجة لهذه الدالة المعقدة
  // int _getOverallIndex(int stepIndex, int pointInStepIndex) {
  //   int overallIndex = 0;
  //   for (int i = 0; i < stepIndex; i++) {
  //     overallIndex += _stepPolylines[i].length;
  //   }
  //   return overallIndex + pointInStepIndex;
  // }

  // =======================================================================
  // ٤. دوال مساعدة وتجهيز البيانات
  // =======================================================================

  Future<void> _prepareStepData() async {
    _stepBounds.clear();
    _stepPolylines.clear();
    if (routeSteps.isEmpty) return;
    for (final step in routeSteps) {
      final pointsString = step['geometry'];

      final List<LatLng> polylineCoordinates = await compute(
          decodePolylineIsolate as ComputeCallback<dynamic, List<LatLng>>,
          pointsString);

      _stepPolylines.add(polylineCoordinates);
      _stepBounds.add(_boundsFromLatLngList(polylineCoordinates));
    }
  }

  Future<void> selectDestination(dynamic place) async {
    placeDestinationController.clear();
    placesDestination = [];

    final double lat = double.parse(place['latitude'].toString());
    final double lng = double.parse(place['longitude'].toString());
    final LatLng destination = LatLng(lat, lng);

    await startNavigationTo(destination,
        infoWindowTitle: place['name'] ?? 'وجهة محددة');
  }

  Future<void> onMapLongPressed(LatLng tappedPoint) async {
    Get.dialog(
      AlertDialog(
        title: const Text('بدء الملاحة؟'),
        content: const Text('هل تريد الذهاب إلى هذا الموقع المحدد؟'),
        actionsAlignment: MainAxisAlignment.spaceBetween,
        actions: [
          TextButton(
            child: const Text('إلغاء', style: TextStyle(color: Colors.grey)),
            onPressed: () => Get.back(),
          ),
          TextButton(
            child: const Text('اذهب الآن'),
            onPressed: () {
              Get.back();
              startNavigationTo(tappedPoint, infoWindowTitle: 'الموقع المحدد');
            },
          ),
        ],
      ),
    );
  }

  Future<void> startNavigationTo(LatLng destination,
      {String infoWindowTitle = ''}) async {
    isLoading = true;
    update();

    try {
      _finalDestination = destination;
      clearRoute(isNewRoute: true);

      markers.add(
        Marker(
          markerId: const MarkerId('destination'),
          position: destination,
          icon: destinationIcon,
          infoWindow: InfoWindow(title: infoWindowTitle),
        ),
      );

      await getRoute(myLocation!, destination);
    } catch (e) {
      Get.snackbar('خطأ', 'حدث خطأ أثناء تحديد الوجهة.');
      print("Error starting navigation: $e");
    } finally {
      isLoading = false;
      update();
    }
  }

  // --- (تعديل) ---: تم تعديل الدالة لاستخدام http.get
  Future<void> getRoute(LatLng origin, LatLng destination) async {
    final url =
        '$_routeApiBaseUrl/route?origin=${origin.latitude},${origin.longitude}&destination=${destination.latitude},${destination.longitude}&steps=true&overview=full';

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

      if (response.statusCode != 200) {
        print("Error from route API: ${response.statusCode}");
        Get.snackbar('خطأ', 'لم يتم العثور على مسار.');
        return;
      }

      final responseData = jsonDecode(response.body);

      if (responseData == null || responseData['status'] != 'ok') {
        Get.snackbar('خطأ', 'لم يتم العثور على مسار.');
        return;
      }

      polylines.clear();
      final pointsString = responseData['polyline'];

      _fullRouteCoordinates = await compute(
          decodePolylineIsolate as ComputeCallback<dynamic, List<LatLng>>,
          pointsString);

      // <<<--- [تعديل] ---: تصفير الـ index عند بدء مسار جديد
      _lastTraveledIndexInFullRoute = 0;

      polylines.add(
        Polyline(
          polylineId: const PolylineId('remaining_route'),
          points: _fullRouteCoordinates,
          color: const Color(0xFF4A80F0),
          width: 7,
          startCap: Cap.roundCap,
          endCap: Cap.roundCap,
        ),
      );
      polylines.add(
        const Polyline(
          polylineId: PolylineId('traveled_route'),
          points: [],
          color: Colors.grey,
          width: 7,
        ),
      );

      routeSteps = List<Map<String, dynamic>>.from(responseData['steps']);
      await _prepareStepData();

      for (int i = 0; i < routeSteps.length; i++) {
        var step = routeSteps[i];
        if (i < _stepPolylines.length && _stepPolylines[i].isNotEmpty) {
          LatLng endLocation = _stepPolylines[i].last;
          step['end_location'] = {
            'lat': endLocation.latitude,
            'lng': endLocation.longitude
          };
        } else {
          var loc = step['maneuver']['location']; // [lng, lat]
          step['end_location'] = {'lat': loc[1], 'lng': loc[0]};
        }
        step['instruction_text'] = _createInstructionFromManeuver(step);
      }

      currentStepIndex = 0;
      _nextInstructionSpoken = false;
      if (routeSteps.isNotEmpty) {
        currentInstruction = routeSteps[0]['instruction_text'];
        nextInstruction = (routeSteps.length > 1)
            ? routeSteps[1]['instruction_text']
            : "الوجهة النهائية";
        Get.find<TextToSpeechController>().speakText(currentInstruction);
      }
      _adjustUpdateInterval();

      if (_fullRouteCoordinates.isNotEmpty) {
        final bounds = _boundsFromLatLngList(_fullRouteCoordinates);
        mapController
            ?.animateCamera(CameraUpdate.newLatLngBounds(bounds, 100.0));
      }
    } catch (e) {
      print("Exception in getRoute: $e");
      Get.snackbar('خطأ', 'حدث خطأ في الشبكة.');
    }
  }

  Future<void> recalculateRoute() async {
    if (myLocation == null || _finalDestination == null || isLoading) return;

    isLoading = true;
    update();

    Get.snackbar(
      'إعادة التوجيه',
      'جاري حساب مسار جديد من موقعك الحالي...',
      backgroundColor: AppColor.goldenBronze,
    );
    await getRoute(myLocation!, _finalDestination!);

    isLoading = false;
    update();
  }

  void clearRoute({bool isNewRoute = false}) {
    polylines.clear();
    if (!isNewRoute) {
      markers.removeWhere((m) => m.markerId.value == 'destination');
      _finalDestination = null;
    }
    routeSteps.clear();
    currentInstruction = "";
    nextInstruction = "";
    distanceToNextStep = "";
    currentSpeed = 0.0;
    _stepBounds.clear();
    _fullRouteCoordinates.clear();
    _stepPolylines.clear();
    _nextInstructionSpoken = false;
    _locationUpdateTimer?.cancel();

    // <<<--- [تعديل] ---: تصفير الـ index عند إلغاء المسار
    _lastTraveledIndexInFullRoute = 0;
    update();
  }

  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> _loadCustomIcons() async {
    carIcon = await BitmapDescriptor.fromAssetImage(
        const ImageConfiguration(size: Size(40, 40)), 'assets/images/car.png');
    destinationIcon = await BitmapDescriptor.fromAssetImage(
        const ImageConfiguration(size: Size(25, 25)), 'assets/images/b.png');
  }

  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 "اتجه";
    }
  }

  // =======================================================================
  // ٥. دالة البحث عن الأماكن المحدثة والدوال المساعدة لها
  // =======================================================================

  // --- (تعديل) ---: تم تعديل الدالة لاستخدام http.post
  Future<void> getPlaces() async {
    final q = placeDestinationController.text.trim();
    if (q.isEmpty || q.length < 3) {
      placesDestination = [];
      update();
      return;
    }

    if (myLocation == null) {
      print('myLocation is null, cannot search for places.');
      return;
    }

    final lat = myLocation!.latitude;
    final lng = myLocation!.longitude;
    const radiusKm = 200.0;
    final latDelta = _kmToLatDelta(radiusKm);
    final lngDelta = _kmToLngDelta(radiusKm, lat);
    final latMin = lat - latDelta;
    final latMax = lat + latDelta;
    final lngMin = lng - lngDelta;
    final lngMax = lng + lngDelta;

    // تجهيز البيانات لإرسالها كـ JSON
    final payload = {
      'query': q,
      'lat_min': latMin.toString(),
      'lat_max': latMax.toString(),
      'lng_min': lngMin.toString(),
      'lng_max': lngMax.toString(),
    };

    try {
      final response =
          await CRUD().post(link: AppLink.getPlacesSyria, payload: payload);
      // إرسال البيانات كـ JSON

      final responseData = (response);

      List list;
      if (responseData is Map) {
        if (responseData['status'] == 'success' &&
            responseData['message'] is List) {
          list = List.from(responseData['message'] as List);
        } else if (responseData['status'] == 'failure') {
          print('Server Error: ${responseData['message']}');
          return;
        } else {
          print('Unexpected Map shape from server');
          return;
        }
      } else if (responseData is List) {
        list = List.from(responseData);
      } else {
        print('Unexpected response shape from server');
        return;
      }

      String _bestName(Map p) {
        return (p['name_ar'] ?? p['name'] ?? p['name_en'] ?? '').toString();
      }

      for (final p in list) {
        final plat = double.tryParse(p['latitude']?.toString() ?? '0.0') ?? 0.0;
        final plng =
            double.tryParse(p['longitude']?.toString() ?? '0.0') ?? 0.0;

        final distance = _haversineKm(lat, lng, plat, plng);
        final relevance = _relevanceScore(_bestName(p), q);
        final score = (1.0 / (1.0 + distance)) * (1.0 + relevance);

        p['distanceKm'] = distance;
        p['relevance'] = relevance;
        p['score'] = score;
      }

      list.sort((a, b) {
        final sa = (a['score'] ?? 0.0) as double;
        final sb = (b['score'] ?? 0.0) as double;
        return sb.compareTo(sa);
      });

      placesDestination = list;
      Log.print('Updated places: $placesDestination');
      update();
    } catch (e) {
      print('Exception in getPlaces: $e');
    }
  }

  void onSearchChanged(String query) {
    if (_debounce?.isActive ?? false) _debounce!.cancel();
    _debounce = Timer(const Duration(milliseconds: 700), () => getPlaces());
  }

  // -----------------------------------------------------------------
  //                  --== دوال مساعدة (محدثة) ==--
  // -----------------------------------------------------------------

  double _haversineKm(double lat1, double lon1, double lat2, double lon2) {
    const R = 6371.0;
    final dLat = (lat2 - lat1) * (pi / 180.0);
    final dLon = (lon2 - lon1) * (pi / 180.0);
    final rLat1 = lat1 * (pi / 180.0);
    final rLat2 = lat2 * (pi / 180.0);

    final a = sin(dLat / 2) * sin(dLat / 2) +
        cos(rLat1) * cos(rLat2) * sin(dLon / 2) * sin(dLon / 2);
    final c = 2 * atan2(sqrt(a), sqrt(1 - a));
    return R * c;
  }

  double _relevanceScore(String placeName, String query) {
    if (placeName.isEmpty || query.isEmpty) return 0.0;
    final pLower = placeName.toLowerCase();
    final qLower = query.toLowerCase();
    if (pLower.startsWith(qLower)) return 1.0;
    if (pLower.contains(qLower)) return 0.5;
    return 0.0;
  }

  double _kmToLatDelta(double km) {
    const kmInDegree = 111.32;
    return km / kmInDegree;
  }

  double _kmToLngDelta(double km, double latitude) {
    const kmInDegree = 111.32;
    return km / (kmInDegree * cos(latitude * (pi / 180.0)));
  }
}
