import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_overlay_window/flutter_overlay_window.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart';
import 'package:geolocator/geolocator.dart';
import 'package:http/http.dart' as http;
import 'package:just_audio/just_audio.dart';
import 'package:siro_driver/views/widgets/error_snakbar.dart';
import 'dart:math' as math;

import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../env/env.dart';
import '../../../main.dart';
import '../../../print.dart';
import '../../../views/home/Captin/driver_map_page.dart';
import '../../../views/home/Captin/orderCaptin/marker_generator.dart';
import '../../../views/widgets/mydialoug.dart';
import '../../firebase/local_notification.dart';
import '../../functions/crud.dart';
import '../../functions/location_controller.dart';
import '../../home/captin/home_captain_controller.dart';

class OrderRequestController extends GetxController
    with WidgetsBindingObserver {
  // --- متغيرات التايمر ---
  double progress = 1.0;
  int duration = 15;
  int remainingTime = 15;
  Timer? _timer;

  bool applied = false;
  final locationController = Get.put(LocationController());

  // 🔥 متغير لمنع تكرار القبول
  bool _isRideTakenHandled = false;

  // --- الأيقونات والماركرز ---
  InlqBitmap? driverIcon;
  Map<MarkerId, Marker> markersMap = {};
  Set<Marker> get markers => markersMap.values.toSet();

  // --- البيانات والتحكم ---
  // 🔥 تم إضافة myMapData لدعم السوكيت الجديد
  List<dynamic>? myList;
  Map<dynamic, dynamic>? myMapData;

  IntaleqMapController? mapController;

  // الإحداثيات (أزلنا late لتجنب الأخطاء القاتلة)
  double latPassenger = 0.0;
  double lngPassenger = 0.0;
  double latDestination = 0.0;
  double lngDestination = 0.0;

  // --- متغيرات العرض ---
  String passengerRating = "5.0";
  String tripType = "Standard";
  String totalTripDistance = "--";
  String totalTripDuration = "--";
  String tripPrice = "--";

  String timeToPassenger = "Calculating...".tr;
  String distanceToPassenger = "--";

  String apiStartName = "";
  String apiEndName = "";

  // --- الخريطة ---
  Set<Polyline> polylines = {};
  bool _hasCalculatedFullJourney = false;

  // حالة التطبيق والصوت
  bool isInBackground = false;
  bool isAccepting = false;
  final AudioPlayer audioPlayer = AudioPlayer();

  @override
  Future<void> onInit() async {
    // 🛑 حماية من الفتح المتكرر لنفس الطلب
    if (Get.arguments == null) {
      print("❌ OrderController Error: No arguments received.");
      Get.back(); // إغلاق الصفحة فوراً
      return;
    }
    super.onInit();
    WidgetsBinding.instance.addObserver(this);

    _checkOverlay();

    // 🔥 تهيئة البيانات هي الخطوة الأولى والأهم
    _initializeData();
    _parseExtraData();

    // 1. تجهيز أيقونة السائق
    await _prepareDriverIcon();

    // 2. وضع الماركرز المبدئية
    _updateMarkers(
        paxTime: "...",
        paxDist: "",
        destTime: totalTripDuration,
        destDist: totalTripDistance);

    // 3. رسم مبدئي
    _initialMapSetup();

    // 4. الاستماع للسوكيت
    _listenForRideTaken();

    // 5. حساب المسارين
    await _calculateFullJourney();

    // 6. تشغيل التايمر
    startTimer();
  }

  // ----------------------------------------------------------------------
  // 🔥🔥🔥 Smart Data Handling (List & Map Support) 🔥🔥🔥
  // ----------------------------------------------------------------------

  void _initializeData() {
    var args = Get.arguments;
    print("📦 Order Controller Received Type: ${args.runtimeType}");
    print("📦 Order Controller Data: $args");

    if (args != null) {
      // الحالة 1: قائمة مباشرة (Legacy / Some Firebase formats)
      if (args is List) {
        myList = args;
      }
      // الحالة 2: خريطة (Map)
      else if (args is Map) {
        // أ) هل هي قادمة من Firebase وتحتوي على DriverList؟
        if (args.containsKey('DriverList')) {
          var listData = args['DriverList'];
          if (listData is List) {
            myList = listData;
          } else if (listData is String) {
            // أحياناً تصل كنص مشفر داخل الـ Map
            try {
              myList = jsonDecode(listData);
            } catch (e) {
              print("Error decoding DriverList: $e");
            }
          }
        }
        // ب) هل هي قادمة من Socket بالمفاتيح الرقمية ("0", "1", ...)؟
        else {
          myMapData = args;
        }
      }
    }

    // تعبئة الإحداثيات باستخدام الدالة الذكية _getValueAt
    latPassenger = _parseCoord(_getValueAt(0));
    lngPassenger = _parseCoord(_getValueAt(1));
    latDestination = _parseCoord(_getValueAt(3));
    lngDestination = _parseCoord(_getValueAt(4));

    print(
        "📍 Parsed Coordinates: Pax($latPassenger, $lngPassenger) -> Dest($latDestination, $lngDestination)");
  }

  /// 🔥 دالة ذكية تجلب القيمة سواء كانت البيانات في List أو Map
  dynamic _getValueAt(int index) {
    // الأولوية للقائمة
    if (myList != null && index < myList!.length) {
      return myList![index];
    }
    // ثم الخريطة (السوكيت) - المفاتيح عبارة عن String
    if (myMapData != null && myMapData!.containsKey(index.toString())) {
      return myMapData![index.toString()];
    }
    return null;
  }

  /// الدالة التي يستخدمها باقي الكود لجلب البيانات كنصوص
  String _safeGet(int index) {
    var val = _getValueAt(index);
    if (val != null) {
      return val.toString();
    }
    return "";
  }

  double _parseCoord(dynamic val) {
    if (val == null) return 0.0;
    String s = val.toString().replaceAll(',', '').trim();
    if (s.contains(' ')) s = s.split(' ')[0];
    return double.tryParse(s) ?? 0.0;
  }

  void _parseExtraData() {
    passengerRating = _safeGet(33).isEmpty ? "5.0" : _safeGet(33);
    tripType = _safeGet(31);

    // Format numbers to avoid many decimal places
    String rawDist = _safeGet(5);
    if (rawDist.isNotEmpty) {
      double? d = double.tryParse(rawDist);
      totalTripDistance = d != null ? "${d.toStringAsFixed(1)} km" : rawDist;
    }

    String rawDur = _safeGet(19);
    if (rawDur.isNotEmpty) {
      double? d = double.tryParse(rawDur);
      totalTripDuration = d != null ? "${d.toStringAsFixed(0)} min" : rawDur;
    }

    String rawPrice = _safeGet(2);
    if (rawPrice.isNotEmpty) {
      double? p = double.tryParse(rawPrice);
      tripPrice = p != null ? p.toStringAsFixed(0) : rawPrice;
    }
  }

  // ----------------------------------------------------------------------
  // 🔥🔥🔥 Core Logic: Concurrent API Calls & Bounds 🔥🔥🔥
  // ----------------------------------------------------------------------

  Future<void> _calculateFullJourney() async {
    if (_hasCalculatedFullJourney) {
      if (mapController != null) zoomToFitRide();
      return;
    }
    _hasCalculatedFullJourney = true;
    // Don't block on mapController being null - we'll draw routes
    // and markers first, then zoom when controller is ready
    bool canZoom = mapController != null;

    try {
      // Reuse stored location from LocationController instead of
      // making a duplicate GPS hardware call (already fetched in
      // _initialMapSetup).
      LatLng driverLatLng;
      double driverHeading = 0.0;
      if (Get.isRegistered<LocationController>()) {
        final locCtrl = Get.find<LocationController>();
        if (locCtrl.myLocation.latitude != 0 ||
            locCtrl.myLocation.longitude != 0) {
          driverLatLng = locCtrl.myLocation;
          driverHeading = locCtrl.heading;
        } else {
          Position driverPos = await Geolocator.getCurrentPosition(
              desiredAccuracy: LocationAccuracy.high);
          driverLatLng = LatLng(driverPos.latitude, driverPos.longitude);
          driverHeading = driverPos.heading;
        }
      } else {
        Position driverPos = await Geolocator.getCurrentPosition(
            desiredAccuracy: LocationAccuracy.high);
        driverLatLng = LatLng(driverPos.latitude, driverPos.longitude);
        driverHeading = driverPos.heading;
      }

      updateDriverLocation(driverLatLng, driverHeading);

      // Clear old polylines to avoid "ghost lines"
      polylines.clear();

      var pickupFuture = _fetchRouteData(
          start: driverLatLng,
          end: LatLng(latPassenger, lngPassenger),
          color: Colors.amber,
          id: 'pickup_route');

      var tripFuture = _fetchRouteData(
          start: LatLng(latPassenger, lngPassenger),
          end: LatLng(latDestination, lngDestination),
          color: Colors.black,
          id: 'trip_route',
          getSteps: true); // 🔥 نطلب الخطوات للمسار

      var results = await Future.wait([pickupFuture, tripFuture]);

      var pickupResult = results[0];
      var tripResult = results[1];

      if (pickupResult != null) {
        distanceToPassenger = pickupResult['distance_text'];
        timeToPassenger = pickupResult['duration_text'];
        polylines.add(pickupResult['polyline']);
      }

      if (tripResult != null) {
        totalTripDistance = tripResult['distance_text'];
        totalTripDuration = tripResult['duration_text'];
        polylines.add(tripResult['polyline']);

        if (tripResult['start_name'] != null &&
            tripResult['start_name'].toString().isNotEmpty) {
          apiStartName = tripResult['start_name'].toString();
        }
        if (tripResult['end_name'] != null &&
            tripResult['end_name'].toString().isNotEmpty) {
          apiEndName = tripResult['end_name'].toString();
        }

        // 🔥 تخزين استجابة السيرفر كاملة (بما فيها الـ points والـ instructions)
        if (tripResult['raw_response'] != null) {
          box.write('cached_trip_route', tripResult['raw_response']);
        }
      }

      await _updateMarkers(
          paxTime: timeToPassenger,
          paxDist: distanceToPassenger,
          destTime: totalTripDuration,
          destDist: totalTripDistance);

      // Now zoom to fit all polylines and markers (if controller available)
      if (canZoom) {
        zoomToFitRide();
      }

      update();
    } catch (e) {
      print("❌ Error in Journey Calculation: $e");
    }
  }

  String _formatDistance(dynamic rawDist) {
    if (rawDist == null || rawDist.toString().isEmpty) return "--";
    double dist = double.tryParse(rawDist.toString()) ?? 0.0;
    if (dist <= 0) return "--";
    if (dist < 1000) return "${dist.toStringAsFixed(0)} m";
    return "${(dist / 1000).toStringAsFixed(1)} km";
  }

  String _formatDuration(dynamic rawDur) {
    if (rawDur == null || rawDur.toString().isEmpty) return "--";
    double dur = double.tryParse(rawDur.toString()) ?? 0.0;
    if (dur <= 0) return "1 min"; // Minimum 1 min for UI
    if (dur < 60) return "${dur.toStringAsFixed(0)} sec";
    return "${(dur / 60).toStringAsFixed(0)} min";
  }

  Future<Map<String, dynamic>?> _fetchRouteData(
      {required LatLng start,
      required LatLng end,
      required Color color,
      required String id,
      bool getSteps = false}) async {
    try {
      if (start.latitude == 0 || end.latitude == 0) return null;
      // Don't block on mapController — route data fetch is independent

      final saasUrl = Uri.parse(AppLink.mapSaasRoute).replace(queryParameters: {
        'fromLat': start.latitude.toString(),
        'fromLng': start.longitude.toString(),
        'toLat': end.latitude.toString(),
        'toLng': end.longitude.toString(),
        'steps': getSteps ? 'true' : 'false',
        'alternatives': 'false',
        'locale': 'ar',
      });

      final response = await http.get(saasUrl, headers: {
        'x-api-key': Env.mapSaasKey,
        'Content-Type': 'application/json',
      });

      if (response.statusCode != 200) {
        throw Exception("Routing request failed: ${response.statusCode}");
      }

      final data = jsonDecode(response.body);
      print("🛣️ Route API Response [$id]: ${data}");

      // The map-saas API returns the route data directly at the root,
      // with 'points' being an encoded polyline string.
      final String? encodedPoints = data['points']?.toString();

      if (encodedPoints != null && encodedPoints.isNotEmpty) {
        List<LatLng> path = controllerDecodePolyline(encodedPoints);
        print("📍 Path for [$id] has ${path.length} points.");

        final num? rawDist = data['distance'] is num ? data['distance'] : null;
        final num? rawDur = data['duration'] is num ? data['duration'] : null;

        final distanceText = data['distance_text'] ?? _formatDistance(rawDist);
        final durationText = data['duration_text'] ?? _formatDuration(rawDur);

        Polyline polyline = Polyline(
          polylineId: PolylineId(id),
          color: color,
          width: 5,
          points: path,
        );

        return {
          'distance_text': distanceText,
          'duration_text': durationText,
          'polyline': polyline,
          'encoded_polyline': encodedPoints,
          'start_name': data['startName']?.toString(),
          'end_name': data['endName']?.toString(),
          'raw_response': response.body, // 🔥 نمرر الـ JSON كاملاً
        };
      }
    } catch (e) {
      print("Route Fetch Error: $e");
    }
    return null;
  }

  void zoomToFitRide() {
    if (mapController == null) return;

    List<LatLng> allPoints = [];

    // Add all polyline points to the bounds calculation
    for (var polyline in polylines) {
      allPoints.addAll(polyline.points);
    }

    // Fallback to basic markers if polylines are empty
    if (allPoints.isEmpty) {
      allPoints.addAll([
        LatLng(latPassenger, lngPassenger),
        LatLng(latDestination, lngDestination),
      ]);
    }

    if (allPoints.isEmpty) return;

    double minLat = allPoints.first.latitude;
    double maxLat = allPoints.first.latitude;
    double minLng = allPoints.first.longitude;
    double maxLng = allPoints.first.longitude;

    for (var p in allPoints) {
      if (p.latitude < minLat) minLat = p.latitude;
      if (p.latitude > maxLat) maxLat = p.latitude;
      if (p.longitude < minLng) minLng = p.longitude;
      if (p.longitude > maxLng) maxLng = p.longitude;
    }

    // Add some padding to the bounds
    double latPad = (maxLat - minLat) * 0.25;
    double lngPad = (maxLng - minLng) * 0.2;

    mapController!.animateCamera(CameraUpdate.newLatLngBounds(
      LatLngBounds(
        southwest: LatLng(minLat - latPad, minLng - lngPad),
        northeast: LatLng(maxLat + latPad, maxLng + lngPad),
      ),
    ));
  }

  // ----------------------------------------------------------------------
  // Markers & Setup
  // ----------------------------------------------------------------------

  Future<void> _prepareDriverIcon() async {
    driverIcon = await MarkerGenerator.createDriverMarker();
  }

  Future<void> _updateMarkers(
      {required String paxTime,
      required String paxDist,
      String? destTime,
      String? destDist}) async {
    // حماية إذا لم يتم جلب الإحداثيات
    if (latPassenger == 0 || latDestination == 0) return;

    final InlqBitmap pickupIcon =
        await MarkerGenerator.createCustomMarkerBitmap(
      title: paxTime,
      subtitle: paxDist,
      color: Colors.amber.shade900, // Matching the amber pickup line
      iconData: Icons.person_pin_circle,
    );

    final InlqBitmap dropoffIcon =
        await MarkerGenerator.createCustomMarkerBitmap(
      title: destTime ?? totalTripDuration,
      subtitle: destDist ?? totalTripDistance,
      color: Colors.red.shade800,
      iconData: Icons.flag,
    );

    markersMap[const MarkerId('pax')] = Marker(
      markerId: const MarkerId('pax'),
      position: LatLng(latPassenger, lngPassenger),
      icon: pickupIcon,
      anchor: const Offset(0.5, 0.85),
    );

    markersMap[const MarkerId('dest')] = Marker(
      markerId: const MarkerId('dest'),
      position: LatLng(latDestination, lngDestination),
      icon: dropoffIcon,
      anchor: const Offset(0.5, 0.85),
    );

    update();
  }

  void _initialMapSetup() async {
    Position driverPos = await Geolocator.getCurrentPosition();
    LatLng driverLatLng = LatLng(driverPos.latitude, driverPos.longitude);

    if (driverIcon != null) {
      markersMap[const MarkerId('driver')] = Marker(
          markerId: const MarkerId('driver'),
          position: driverLatLng,
          icon: driverIcon!,
          rotation: driverPos.heading,
          anchor: const Offset(0.5, 0.5),
          flat: true,
          zIndex: 10);
    }

    if (latPassenger != 0 && lngPassenger != 0) {
      polylines.add(Polyline(
        polylineId: const PolylineId('temp_line'),
        points: [driverLatLng, LatLng(latPassenger, lngPassenger)],
        color: Colors.grey,
        width: 2,
      ));

      zoomToFitRide();
    }

    update();
  }

  void updateDriverLocation(LatLng newPos, double heading) {
    if (driverIcon != null) {
      markersMap[const MarkerId('driver')] = Marker(
        markerId: const MarkerId('driver'),
        position: newPos,
        icon: driverIcon!,
        rotation: heading,
        anchor: const Offset(0.5, 0.5),
        flat: true,
        zIndex: 10,
      );
      update();
    }
  }

  void onMapCreated(IntaleqMapController controller) {
    mapController = controller;
    _calculateFullJourney();
  }

  // --- قبول الطلب وإدارة التايمر ---
  void startTimer() {
    _timer?.cancel();
    remainingTime = duration;
    _playAudio();
    _timer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (remainingTime <= 0) {
        timer.cancel();
        _stopAudio();
        if (!applied) Get.back();
      } else {
        remainingTime--;
        progress = remainingTime / duration;
        update();
      }
    });
  }

  void endTimer() => _timer?.cancel();
  void changeApplied() => applied = true;

  void _playAudio() async {
    try {
      await audioPlayer.setAsset('assets/order.mp3', preload: true);
      await audioPlayer.setLoopMode(LoopMode.one);
      await audioPlayer.play();
    } catch (e) {
      print(e);
    }
  }

  void _stopAudio() => audioPlayer.stop();

  void _listenForRideTaken() {
    if (locationController.socket != null) {
      locationController.socket!.off('ride_taken');
      locationController.socket!.on('ride_taken', (data) {
        if (_isRideTakenHandled) return;
        String takenRideId = data['ride_id'].toString();
        String myCurrentRideId = _safeGet(16);
        String whoTookIt = data['taken_by_driver_id'].toString();
        String myDriverId = box.read(BoxName.driverID).toString();

        if (takenRideId == myCurrentRideId && whoTookIt != myDriverId) {
          _isRideTakenHandled = true;
          endTimer();
          // 1. حذف الإشعار من شريط التنبيهات فوراً
          NotificationController().cancelOrderNotification();
          if (Get.isSnackbarOpen) Get.closeCurrentSnackbar();

          // إغلاق أي ديالوج مفتوح قسرياً
          if (Get.isDialogOpen ?? false) {
            navigatorKey.currentState?.pop();
          }
          Get.back();
          mySnackbarInfo("The order has been accepted by another driver.".tr);
        }
      });
    }
  }

  // Lifecycle
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    super.didChangeAppLifecycleState(state);
    if (state == AppLifecycleState.paused ||
        state == AppLifecycleState.detached) {
      isInBackground = true;
    } else if (state == AppLifecycleState.resumed) {
      isInBackground = false;
      FlutterOverlayWindow.closeOverlay();
    }
  }

  void _checkOverlay() async {
    if (Platform.isAndroid && await FlutterOverlayWindow.isActive()) {
      await FlutterOverlayWindow.closeOverlay();
    }
  }

// Accept Order Logic
  Future<void> acceptOrder() async {
    if (isAccepting) return;
    isAccepting = true;
    update();

    endTimer();
    _stopAudio();

    try {
      // 1. إرسال الطلب
      var res = await CRUD()
          .post(link: "${AppLink.ride}/rides/acceptRide.php", payload: {
        'id': _safeGet(16),
        'rideTimeStart': DateTime.now().toString(),
        'status': 'Apply',
        'passengerToken': _safeGet(9),
        'driver_id': box.read(BoxName.driverID),
      });

      Log.print('res from orderrequestpage: ${res}');

      // ============================================================
      // تصحيح: فحص الرد بدقة (Map أو String)
      // ============================================================
      bool isFailure = false;

      if (res is Map && res['status'] == 'failure') {
        isFailure = true;
      } else if (res == 'failure') {
        isFailure = true;
      }

      if (isFailure) {
        // ⛔ حالة الفشل: الطلب مأخوذ
        MyDialog().getDialog(
            "Sorry, the order was taken by another driver.".tr, '', () {
          // بما أن MyDialog يغلق نفسه الآن، نحتاج Get.back() واحدة فقط لإغلاق صفحة الطلب
          Get.back();
        });
      } else {
        // ✅ حالة النجاح

        // حماية من الكراش: التأكد من وجود HomeCaptainController قبل استخدامه
        if (!Get.isRegistered<HomeCaptainController>()) {
          Get.put(HomeCaptainController());
        } else {
          Get.find<HomeCaptainController>().changeRideId();
        }

        box.write(BoxName.statusDriverLocation, 'on');
        changeApplied();

        var rideArgs = {
          'passengerLocation': '${_safeGet(0)},${_safeGet(1)}',
          'passengerDestination': '${_safeGet(3)},${_safeGet(4)}',
          'Duration': totalTripDuration,
          'totalCost': _safeGet(26),
          'Distance': totalTripDistance,
          'name': _safeGet(8),
          'phone': _safeGet(10),
          'email': _safeGet(28),
          'WalletChecked': _safeGet(13),
          'tokenPassenger': _safeGet(9),
          'direction':
              'https://www.google.com/maps/dir/${_safeGet(0)}/${_safeGet(1)}/',
          'DurationToPassenger': timeToPassenger,
          'rideId': _safeGet(16),
          'passengerId': _safeGet(7),
          'driverId': _safeGet(18),
          'durationOfRideValue': totalTripDuration,
          'paymentAmount': _safeGet(2),
          'paymentMethod': _safeGet(13) == 'true' ? 'visa' : 'cash',
          'isHaveSteps': _safeGet(20),
          'step0': _safeGet(21),
          'step1': _safeGet(22),
          'step2': _safeGet(23),
          'step3': _safeGet(24),
          'step4': _safeGet(25),
          'passengerWalletBurc': _safeGet(26),
          'timeOfOrder': DateTime.now().toString(),
          'totalPassenger': _safeGet(2),
          'carType': _safeGet(31),
          'kazan': _safeGet(32),
          'startNameLocation': _safeGet(29),
          'endNameLocation': _safeGet(30),
        };

        box.write(BoxName.rideArguments, rideArgs);

        // الانتقال النهائي
        Get.off(() => PassengerLocationMapPage(), arguments: rideArgs);
      }
    } catch (e) {
      Log.print("Error in acceptOrder: $e");
    } finally {
      isAccepting = false;
      update();
    }
  }

  @override
  void onClose() {
    locationController.socket?.off('ride_taken');
    audioPlayer.dispose();
    WidgetsBinding.instance.removeObserver(this);
    _timer?.cancel();
    // mapController?.dispose();
    super.onClose();
  }

  List<LatLng> controllerDecodePolyline(String encoded) {
    List<LatLng> points = [];
    int index = 0, len = encoded.length;
    int lat = 0, lng = 0;

    while (index < len) {
      int b, shift = 0, result = 0;
      do {
        b = encoded.codeUnitAt(index++) - 63;
        result |= (b & 0x1f) << shift;
        shift += 5;
      } while (b >= 0x20);
      int dlat = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
      lat += dlat;

      shift = 0;
      result = 0;
      do {
        b = encoded.codeUnitAt(index++) - 63;
        result |= (b & 0x1f) << shift;
        shift += 5;
      } while (b >= 0x20);
      int dlng = ((result & 1) != 0 ? ~(result >> 1) : (result >> 1));
      lng += dlng;

      points.add(LatLng(lat / 1E5, lng / 1E5));
    }
    return points;
  }
}
