import 'dart:async';
import 'dart:math';
import 'dart:math' as math;
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:sefer_driver/constant/colors.dart';

// استخدام نفس مسارات الاستيراد التي قدمتها
import '../../../constant/api_key.dart';
import '../../../constant/links.dart';
import '../../../print.dart';
import '../../functions/crud.dart';
import '../../functions/tts.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;
  double currentSpeed = 0.0;
  String distanceToNextStep = "";
  final List<LatLngBounds> _stepBounds = [];

  @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();
  }

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

  Future<void> _getCurrentLocationAndStartUpdates() async {
    try {
      Position position = await Geolocator.getCurrentPosition(
          desiredAccuracy: LocationAccuracy.high);
      myLocation = LatLng(position.latitude, position.longitude);
      update();
      animateCameraToPosition(myLocation!);
      // بدء التحديثات باستخدام المؤقت بدلاً من الـ Stream
      _startLocationTimer();
    } catch (e) {
      print("Error getting location: $e");
    }
  }

  // --- تم استبدال الـ Stream بمؤقت للتحكم الكامل ---
  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']['value'];

    // إذا كانت الخطوة الحالية طويلة (شارع سريع > 1.5 كم)
    if (currentStepDistance > 1500) {
      _currentUpdateInterval = const Duration(seconds: 4);
    }
    // إذا كانت الخطوة قصيرة (منعطفات داخل المدينة < 1.5 كم)
    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 =
          _parseInstruction(routeSteps[currentStepIndex]['html_instructions']);
      nextInstruction = ((currentStepIndex + 1) < routeSteps.length)
          ? _parseInstruction(
              routeSteps[currentStepIndex + 1]['html_instructions'])
          : "الوجهة النهائية";
      _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) {
    // **التحسين:** البحث فقط في الخطوة الحالية والخطوة التالية
    int searchEndIndex = (currentStepIndex + 1 < _stepPolylines.length)
        ? currentStepIndex + 1
        : currentStepIndex;

    int overallClosestIndex = -1;
    double minDistance = double.infinity;

    // البحث في نقاط الخطوة الحالية والتالية فقط
    for (int i = currentStepIndex; i <= searchEndIndex; i++) {
      for (int j = 0; j < _stepPolylines[i].length; j++) {
        final distance = Geolocator.distanceBetween(
            currentPosition.latitude,
            currentPosition.longitude,
            _stepPolylines[i][j].latitude,
            _stepPolylines[i][j].longitude);
        if (distance < minDistance) {
          minDistance = distance;
          // نحتاج إلى حساب الفهرس العام في القائمة الكاملة
          overallClosestIndex = _getOverallIndex(i, j);
        }
      }
    }

    if (overallClosestIndex == -1) return;

    List<LatLng> traveledPoints =
        _fullRouteCoordinates.sublist(0, overallClosestIndex + 1);
    traveledPoints.add(currentPosition);

    List<LatLng> remainingPoints =
        _fullRouteCoordinates.sublist(overallClosestIndex);
    remainingPoints.insert(0, currentPosition);

    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;
  }

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

  void _prepareStepData() {
    _stepBounds.clear();
    _stepPolylines.clear();
    if (routeSteps.isEmpty) return;
    for (final step in routeSteps) {
      final pointsString = step['polyline']['points'];
      final List<List<num>> points =
          decodePolyline(pointsString).cast<List<num>>();
      final polylineCoordinates = points
          .map((point) => LatLng(point[0].toDouble(), point[1].toDouble()))
          .toList();
      _stepPolylines.add(polylineCoordinates); // تخزين نقاط الخطوة
      _stepBounds.add(_boundsFromLatLngList(polylineCoordinates));
    }
  }

  // ... باقي دوال الكنترولر بدون تغيير ...
  // (selectDestination, onMapLongPressed, startNavigationTo, getRoute, etc.)
  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();
    }
  }

  Future<void> getRoute(LatLng origin, LatLng destination) async {
    final 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) {
      Get.snackbar('خطأ', 'لم يتم العثور على مسار.');
      return;
    }

    polylines.clear();
    final pointsString = response['routes'][0]['overview_polyline']['points'];
    final List<List<num>> points =
        decodePolyline(pointsString).cast<List<num>>();
    _fullRouteCoordinates = points
        .map((point) => LatLng(point[0].toDouble(), point[1].toDouble()))
        .toList();

    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(
        response['routes'][0]['legs'][0]['steps']);
    _prepareStepData();

    currentStepIndex = 0;
    _nextInstructionSpoken = false;
    if (routeSteps.isNotEmpty) {
      currentInstruction =
          _parseInstruction(routeSteps[0]['html_instructions']);
      nextInstruction = (routeSteps.length > 1)
          ? _parseInstruction(routeSteps[1]['html_instructions'])
          : "الوجهة النهائية";
      Get.find<TextToSpeechController>().speakText(currentInstruction);
    }
    _adjustUpdateInterval(); // تحديد سرعة التحديث لأول مرة

    final boundsData = response['routes'][0]['bounds'];
    mapController?.animateCamera(CameraUpdate.newLatLngBounds(
      LatLngBounds(
        northeast: LatLng(
            boundsData['northeast']['lat'], boundsData['northeast']['lng']),
        southwest: LatLng(
            boundsData['southwest']['lat'], boundsData['southwest']['lng']),
      ),
      100.0,
    ));
  }

  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(); // إيقاف التحديثات عند إلغاء المسار
    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 _parseInstruction(String html) =>
      html.replaceAll(RegExp(r'<[^>]*>'), ' ');

  double _haversineKm(double lat1, double lon1, double lat2, double lon2) {
    const R = 6371.0; // km
    final dLat = (lat2 - lat1) * math.pi / 180.0;
    final dLon = (lon2 - lon1) * math.pi / 180.0;
    final a = math.sin(dLat / 2) * math.sin(dLat / 2) +
        math.cos(lat1 * math.pi / 180.0) *
            math.cos(lat2 * math.pi / 180.0) *
            math.sin(dLon / 2) *
            math.sin(dLon / 2);
    final c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a));
    return R * c;
  }

  /// تحويل نصف قطر بالكيلومتر إلى دلتا درجات عرض
  double _kmToLatDelta(double km) => km / 111.0;

  /// تحويل نصف قطر بالكيلومتر إلى دلتا درجات طول (تعتمد على خط العرض)
  double _kmToLngDelta(double km, double atLat) =>
      km / (111.320 * math.cos(atLat * math.pi / 180.0)).abs().clamp(1e-6, 1e9);

  /// حساب درجة التطابق النصي (كل كلمة تبدأ بها الاسم = 2 نقاط، يحتويها = 1 نقطة)
  double _relevanceScore(String name, String query) {
    final n = name.toLowerCase();
    final parts =
        query.toLowerCase().split(RegExp(r'\s+')).where((p) => p.length >= 2);
    double s = 0.0;
    for (final p in parts) {
      if (n.startsWith(p)) {
        s += 2.0;
      } else if (n.contains(p)) {
        s += 1.0;
      }
    }
    return s;
  }

  Future<void> getPlaces() async {
    final q = placeDestinationController.text.trim();
    if (q.isEmpty) {
      placesDestination = [];
      update();
      return;
    }

    final lat = myLocation!.latitude;
    final lng = myLocation!.longitude;

    // نصف قطر البحث بالكيلومتر (عدّل حسب رغبتك)
    const radiusKm = 200.0;

    // حساب الباوند الصحيح (درجات، وليس 2.2 درجة ثابتة)
    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;

    try {
      final response = await CRUD().post(
        link: AppLink.getPlacesSyria,
        payload: {
          'query': q,
          'lat_min': latMin.toString(),
          'lat_max': latMax.toString(),
          'lng_min': lngMin.toString(),
          'lng_max': lngMax.toString(),
        },
      );

      // يدعم شكلي استجابة: إما {"...","message":[...]} أو قائمة مباشرة [...]
      List list;
      if (response is Map && response['message'] is List) {
        list = List.from(response['message'] as List);
      } else if (response is List) {
        list = List.from(response);
      } else {
        print('Unexpected response shape');
        return;
      }

      // جهّز الحقول المحتملة للأسماء
      String _bestName(Map p) {
        return (p['name'] ?? p['name_ar'] ?? p['name_en'] ?? '').toString();
      }

      // احسب المسافة ودرجة التطابق والنقاط
      for (final p in list) {
        final plat = double.tryParse(p['latitude']?.toString() ?? '') ?? 0.0;
        final plng = double.tryParse(p['longitude']?.toString() ?? '') ?? 0.0;

        final d = _haversineKm(lat, lng, plat, plng);
        final rel = _relevanceScore(_bestName(p), q);

        // معادلة ترتيب ذكية: مسافة أقل + تطابق أعلى = نقاط أعلى
        // تضيف +1 لضمان عدم وصول الوزن للصفر عند عدم وجود تطابق
        final score = (1.0 / (1.0 + d)) * (1.0 + rel);

        p['distanceKm'] = d;
        p['relevance'] = rel;
        p['score'] = score;
      }

      // رتّب حسب score تنازليًا، ثم المسافة تصاعديًا كحسم
      list.sort((a, b) {
        final sa = (a['score'] ?? 0.0) as double;
        final sb = (b['score'] ?? 0.0) as double;
        final cmp = sb.compareTo(sa);
        if (cmp != 0) return cmp;
        final da = (a['distanceKm'] ?? 1e9) as double;
        final db = (b['distanceKm'] ?? 1e9) as double;
        return da.compareTo(db);
      });

      // خذ أول 10–15 للعرض (اختياري)، أو اعرض الكل
      placesDestination = list.take(15).toList();
      Log.print('placesDestination: $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());
  }
}
