import 'dart:async';
import 'dart:convert';
import 'dart:math';
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/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: 1);
  LatLng? _lastRecordedLocation;

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

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

  List<LatLng> _fullRouteCoordinates = [];
  int _lastTraveledIndexInFullRoute = 0;

  bool _nextInstructionSpoken = false;
  String currentInstruction = "";
  String nextInstruction = "";
  int currentStepIndex = 0;

  double currentSpeed = 0.0;
  String distanceToNextStep = "";

  // الرابط الجديد
  static const String _routeApiBaseUrl =
      "https://routesjo.intaleq.xyz/route/v1/driving";

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

  // =======================================================================
  // ✅ دوال الخريطة الأساسية
  // =======================================================================

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

  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: 'الموقع المحدد');
            },
          ),
        ],
      ),
    );
  }

  // =======================================================================
  // ١. النظام الذكي للموقع (Optimized)
  // =======================================================================

  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) {
      Log.print("Error getting initial 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);

      final newLoc = LatLng(position.latitude, position.longitude);

      // فلتر الاهتزاز (Jitter Filter)
      if (_lastRecordedLocation != null) {
        double dist = Geolocator.distanceBetween(
            newLoc.latitude,
            newLoc.longitude,
            _lastRecordedLocation!.latitude,
            _lastRecordedLocation!.longitude);
        if (dist < 2.0) return; // تجاهل الحركة الأقل من 2 متر
      }

      myLocation = newLoc;
      _lastRecordedLocation = newLoc;
      heading = position.heading;
      currentSpeed = position.speed * 3.6;

      _updateCarMarker();

      if (polylines.isNotEmpty && _fullRouteCoordinates.isNotEmpty) {
        animateCameraToPosition(
          myLocation!,
          bearing: heading,
          zoom: 18.0,
        );

        _updateTraveledPolylineSmart(myLocation!);
        _checkNavigationStep(myLocation!);
      }

      update();
    } catch (e) {
      // Log.print("Loc update error: $e");
    }
  }

  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,
        zIndex: 2,
      ),
    );
  }

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

  // =======================================================================
  // ٢. خوارزمية رسم المسار الذكية (Sliding Window) - مخففة للجهاز
  // =======================================================================

  void _updateTraveledPolylineSmart(LatLng currentPos) {
    if (_fullRouteCoordinates.isEmpty) return;

    // نبحث فقط في الـ 60 نقطة القادمة لتخفيف الحمل على المعالج
    int searchWindow = 60;
    int startIndex = _lastTraveledIndexInFullRoute;
    int endIndex = min(startIndex + searchWindow, _fullRouteCoordinates.length);

    double minDistance = double.infinity;
    int closestIndex = startIndex;
    bool foundCloser = false;

    for (int i = startIndex; i < endIndex; i++) {
      final point = _fullRouteCoordinates[i];
      final dist = Geolocator.distanceBetween(currentPos.latitude,
          currentPos.longitude, point.latitude, point.longitude);

      if (dist < minDistance) {
        minDistance = dist;
        closestIndex = i;
        foundCloser = true;
      }
    }

    // شرط التحديث: وجدنا نقطة أقرب، وهي أمامنا (وليست خلفنا)، والمسافة منطقية
    if (foundCloser &&
        minDistance < 50 &&
        closestIndex > _lastTraveledIndexInFullRoute) {
      _lastTraveledIndexInFullRoute = closestIndex;

      // استخدام sublist وهو سريع جداً في Dart
      final remaining =
          _fullRouteCoordinates.sublist(_lastTraveledIndexInFullRoute);
      final traveled =
          _fullRouteCoordinates.sublist(0, _lastTraveledIndexInFullRoute + 1);

      _updatePolylinesSets(traveled, remaining);
    }
  }

  void _updatePolylinesSets(List<LatLng> traveled, List<LatLng> remaining) {
    // إزالة الخطوط القديمة وإضافة الجديدة بدلاً من تحديث الكل
    polylines.removeWhere((p) => p.polylineId.value == 'remaining_route');
    polylines.removeWhere((p) => p.polylineId.value == 'traveled_route');

    // المسار المتبقي (أزرق واضح)
    polylines.add(Polyline(
      polylineId: const PolylineId('remaining_route'),
      points: remaining,
      color: const Color(0xFF0D47A1),
      width: 6,
      startCap: Cap.roundCap,
      endCap: Cap.roundCap,
      jointType: JointType.round,
    ));

    // المسار المقطوع (رمادي)
    polylines.add(Polyline(
      polylineId: const PolylineId('traveled_route'),
      points: traveled,
      color: Colors.grey.shade400,
      width: 6,
      jointType: JointType.round,
    ));
  }

  // =======================================================================
  // ٣. التحكم في التوجيهات
  // =======================================================================

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

    final step = routeSteps[currentStepIndex];
    // في OSRM، يجب التأكد من وجود maneuver location
    final maneuver = step['maneuver'];
    final List<dynamic> location = maneuver['location']; // [lng, lat]
    final endLatLng = LatLng(location[1], location[0]);

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

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

    // نطق التعليمات قبل 50 متر
    if (distance < 50 &&
        !_nextInstructionSpoken &&
        nextInstruction.isNotEmpty) {
      Get.find<TextToSpeechController>().speakText(nextInstruction);
      _nextInstructionSpoken = true;
    }

    // الانتقال للخطوة التالية عند الاقتراب (20 متر)
    if (distance < 20) {
      _advanceStep();
    }
  }

  void _advanceStep() {
    currentStepIndex++;
    if (currentStepIndex < routeSteps.length) {
      currentInstruction = routeSteps[currentStepIndex]['instruction_text'];

      // تجهيز التعليمات القادمة
      if ((currentStepIndex + 1) < routeSteps.length) {
        nextInstruction =
            "ثم ${routeSteps[currentStepIndex + 1]['instruction_text']}";
      } else {
        nextInstruction = "ستصل إلى وجهتك";
      }

      _nextInstructionSpoken = false;
      update();
    } else {
      _finishNavigation();
    }
  }

  void _finishNavigation() {
    currentInstruction = "لقد وصلت إلى وجهتك";
    nextInstruction = "";
    distanceToNextStep = "";
    Get.find<TextToSpeechController>().speakText(currentInstruction);
    update();
  }

  // =======================================================================
  // ٤. جلب المسار (🔥 تم التحديث للرابط الجديد 🔥)
  // =======================================================================

  Future<void> getRoute(LatLng origin, LatLng destination) async {
    // 🔥 بناء الرابط حسب التنسيق: /driving/lng,lat;lng,lat
    String coords =
        "${origin.longitude},${origin.latitude};${destination.longitude},${destination.latitude}";

    // 🔥 تفعيل steps=true لجلب الخطوات
    String url =
        "$_routeApiBaseUrl/$coords?steps=true&overview=full&geometries=polyline";

    try {
      // الرابط الجديد قد لا يحتاج API Key في الهيدر، ولكن نتركه إذا كان السيرفر يطلبه
      // إذا كان الرابط عام، يمكن إزالة الهيدر
      final response = await http.get(Uri.parse(url));

      if (response.statusCode != 200) {
        Get.snackbar('تنبيه', 'تعذر الاتصال بخدمة التوجيه.');
        return;
      }

      final responseData = jsonDecode(response.body);

      // التحقق حسب هيكلية OSRM
      if (responseData['code'] != 'Ok' ||
          (responseData['routes'] as List).isEmpty) {
        Get.snackbar('عذراً', 'لم يتم العثور على مسار.');
        return;
      }

      var route = responseData['routes'][0];

      // 1. فك تشفير Polyline
      final pointsString = route['geometry'];
      _fullRouteCoordinates = await compute(
          decodePolylineIsolate as ComputeCallback<dynamic, List<LatLng>>,
          pointsString);

      // تهيئة الرسم
      _lastTraveledIndexInFullRoute = 0;
      _updatePolylinesSets([], _fullRouteCoordinates);

      // 2. استخراج الخطوات (داخل Legs في OSRM)
      var legs = route['legs'] as List;
      if (legs.isNotEmpty) {
        var steps = legs[0]['steps'] as List;
        routeSteps = List<Map<String, dynamic>>.from(steps);
      } else {
        routeSteps = [];
      }

      // 3. معالجة النصوص العربية للخطوات
      for (var step in routeSteps) {
        step['instruction_text'] = _createInstructionFromManeuver(step);
      }

      // 4. بدء الملاحة
      currentStepIndex = 0;
      _nextInstructionSpoken = false;
      if (routeSteps.isNotEmpty) {
        currentInstruction = routeSteps[0]['instruction_text'];
        if (routeSteps.length > 1) {
          nextInstruction = "ثم ${routeSteps[1]['instruction_text']}";
        } else {
          nextInstruction = "الوجهة النهائية";
        }
        Get.find<TextToSpeechController>().speakText(currentInstruction);
      }

      // 5. ضبط الكاميرا
      if (_fullRouteCoordinates.isNotEmpty) {
        final bounds = _boundsFromLatLngList(_fullRouteCoordinates);
        mapController
            ?.animateCamera(CameraUpdate.newLatLngBounds(bounds, 80.0));
      }

      update();
    } catch (e) {
      Log.print("GetRoute Error: $e");
      Get.snackbar('خطأ', 'حدث خطأ غير متوقع.');
    }
  }

  // 🔥 تحويل تعليمات OSRM إلى العربية
  String _createInstructionFromManeuver(Map<String, dynamic> step) {
    if (step['maneuver'] == null) return "تابع المسير";

    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':
        return "لقد وصلت إلى وجهتك، $name";
      case 'turn':
      case 'fork':
      case 'roundabout':
      case 'merge':
      case 'on ramp':
      case 'off ramp':
      case 'end of road':
        instruction = _getTurnInstruction(modifier);
        break;
      case 'new name':
        instruction = "تابع المسير";
        break;
      default:
        instruction = "تابع المسير";
    }

    if (name.isNotEmpty) {
      if (type == 'new name' || type == 'continue') {
        instruction += " على $name";
      } else {
        instruction += " نحو $name";
      }
    }
    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 "اتجه";
    }
  }

  // =======================================================================
  // ٥. أدوات مساعدة
  // =======================================================================

  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);
    } finally {
      isLoading = false;
      update();
    }
  }

  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}) {
    if (!isNewRoute) {
      markers.removeWhere((m) => m.markerId.value == 'destination');
      _finalDestination = null;
      polylines.clear();
    }
    routeSteps.clear();
    _fullRouteCoordinates.clear();
    _lastTraveledIndexInFullRoute = 0;
    currentInstruction = "";
    nextInstruction = "";
    distanceToNextStep = "";
    update();
  }

  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(30, 30)), 'assets/images/b.png');
  }

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

    if (myLocation == null) return;

    final lat = myLocation!.latitude;
    final lng = myLocation!.longitude;
    const radiusKm = 200.0;
    final latDelta = _kmToLatDelta(radiusKm);
    final lngDelta = _kmToLngDelta(radiusKm, lat);

    final payload = {
      'query': q,
      'lat_min': (lat - latDelta).toString(),
      'lat_max': (lat + latDelta).toString(),
      'lng_min': (lng - lngDelta).toString(),
      'lng_max': (lng + lngDelta).toString(),
    };

    try {
      final response =
          await CRUD().post(link: AppLink.getPlacesSyria, payload: payload);
      final responseData = (response);

      List list;
      if (responseData is Map && responseData['status'] == 'success') {
        list = List.from(responseData['message'] as List);
      } else if (responseData is List) {
        list = List.from(responseData);
      } else {
        return;
      }

      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);
        p['distanceKm'] = distance;
      }

      list.sort((a, b) =>
          (a['distanceKm'] as double).compareTo(b['distanceKm'] as double));
      placesDestination = list;
      update();
    } catch (e) {
      print('Exception in getPlaces: $e');
    }
  }

  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());
    await startNavigationTo(LatLng(lat, lng),
        infoWindowTitle: place['name'] ?? 'وجهة');
  }

  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 a = sin(dLat / 2) * sin(dLat / 2) +
        cos(lat1 * pi / 180) *
            cos(lat2 * pi / 180) *
            sin(dLon / 2) *
            sin(dLon / 2);
    return R * 2 * atan2(sqrt(a), sqrt(1 - a));
  }

  double _kmToLatDelta(double km) => km / 111.32;
  double _kmToLngDelta(double km, double lat) =>
      km / (111.32 * cos(lat * pi / 180));

  LatLngBounds _boundsFromLatLngList(List<LatLng> list) {
    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!));
  }

  // --- دوال التحقق من الدولة ---
  String getLocationArea(double latitude, double longitude) {
    LatLng p = LatLng(latitude, longitude);
    if (isPointInPolygon(p, CountryPolygons.jordanBoundary)) {
      box.write(BoxName.countryCode, 'Jordan');
      return 'Jordan';
    }
    if (isPointInPolygon(p, CountryPolygons.syriaBoundary)) {
      box.write(BoxName.countryCode, 'Syria');
      return 'Syria';
    }
    if (isPointInPolygon(p, CountryPolygons.egyptBoundary)) {
      box.write(BoxName.countryCode, 'Egypt');
      return 'Egypt';
    }
    return 'Unknown';
  }

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

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

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