import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:geolocator/geolocator.dart' as geo;
import 'package:intaleq_maps/intaleq_maps.dart';
import 'package:location/location.dart';
import 'package:battery_plus/battery_plus.dart';
import 'package:permission_handler/permission_handler.dart' as ph;
import 'package:socket_io_client/socket_io_client.dart' as IO;
import 'package:sefer_driver/constant/table_names.dart';
import 'package:trip_overlay_plugin/trip_overlay_plugin.dart';
import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import '../firebase/local_notification.dart';
import '../home/captin/home_captain_controller.dart';
import '../home/captin/map_driver_controller.dart';
import '../home/payment/captain_wallet_controller.dart';
import 'background_service.dart';
import 'crud.dart';

class LocationController extends GetxController with WidgetsBindingObserver {
  // ===================================================================
  // ====== Tunables ======
  // ===================================================================
  static const Duration recordIntervalNormal = Duration(seconds: 3);
  static const Duration uploadBatchIntervalNormal = Duration(minutes: 2);
  static const Duration recordIntervalPowerSave = Duration(seconds: 10);
  static const Duration uploadBatchIntervalPowerSave = Duration(minutes: 5);

  static const double lowWalletThreshold = -200;
  static const int powerSaveTriggerLevel = 20;
  static const int powerSaveExitLevel = 25;

  // ===================================================================
  // ====== Services & Variables ======
  // ===================================================================
  late final Location location = Location();
  final Battery _battery = Battery();

  IO.Socket? socket;
  bool isSocketConnected = false;
  Timer? _socketHeartbeat;

  StreamSubscription<LocationData>? _locSub;
  StreamSubscription<BatteryState>? _batterySub;

  Timer? _recordTimer;
  Timer? _uploadBatchTimer;

  late final HomeCaptainController _homeCtrl;
  late final CaptainWalletController _walletCtrl;

  LatLng myLocation = const LatLng(0, 0);
  double heading = 0.0;
  double speed = 0.0;
  double totalDistance = 0.0;
  bool _isReady = false;
  bool _isPowerSavingMode = false;

  final List<Map<String, dynamic>> _trackBuffer = [];
  final List<Map<String, dynamic>> _behaviorBuffer = [];

  LatLng? _lastPosForDistance;
  LatLng? _lastRecordedRealLoc;
  DateTime? _lastRecordedTime;

  LatLng? _lastSqlLoc;
  double? _lastSpeed;
  DateTime? _lastSpeedAt;

  @override
  Future<void> onInit() async {
    super.onInit();
    Log.print('🚀 LocationController Starting...');

    // 1. Register Lifecycle Observer
    WidgetsBinding.instance.addObserver(this);
    box.write(BoxName.isAppInForeground, true);

    // مراقب الحالة (Status Watcher)
    box.listenKey(BoxName.statusDriverLocation, (value) {
      if (value == 'blocked') {
        Log.print("⛔ Driver is Blocked: Force Stopping Location Updates.");
        stopLocationUpdates();
        if (socket != null && socket!.connected) {
          socket!.emit('update_location', {
            'driver_id': box.read(BoxName.driverID),
            'status': 'blocked',
            'lat': myLocation.latitude,
            'lng': myLocation.longitude,
            'heading': heading,
            'speed': speed * 3.6,
            'distance': totalDistance
          });
          socket!.disconnect();
        }
      }
    });

    bool deps = await _awaitDependencies();
    if (!deps) return;

    _isReady = true;

    initSocket();
    await _initLocationSettings();
    _listenToBatteryChanges();

    if (box.read(BoxName.statusDriverLocation) != 'blocked') {
      await startLocationUpdates();
    }

    Log.print('✅ LocationController Initialized.');
  }

  @override
  void onClose() {
    WidgetsBinding.instance.removeObserver(this);
    box.write(BoxName.isAppInForeground, false);
    stopLocationUpdates();
    _batterySub?.cancel();
    _stopHeartbeat();
    socket?.dispose();
    super.onClose();
  }

  // ===================================================================
  // 🔥 Lifecycle Manager (Fixes Freeze & Background issues)
  // ===================================================================
  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      Log.print("📱 Lifecycle: App is in FOREGROUND");
      box.write(BoxName.isAppInForeground, true);

      // إيقاف خدمة الخلفية
      BackgroundServiceHelper.stopService();
      
      if (socket == null || (!socket!.connected && !_isInitializingSocket)) {
        Log.print("🔄 Initializing Socket on resume...");
        initSocket();
      }
    } else if (state == AppLifecycleState.paused ||
        state == AppLifecycleState.detached) {
      Log.print("📱 Lifecycle: App is in BACKGROUND");
      box.write(BoxName.isAppInForeground, false);

      // تشغيل خدمة الخلفية للأندرويد لضمان بقاء التطبيق حياً
      if (!Platform.isIOS) {
        BackgroundServiceHelper.startService();
      }
    }
  }

  Future<bool> _awaitDependencies() async {
    int attempts = 0;
    while (attempts < 10) {
      if (Get.isRegistered<HomeCaptainController>() &&
          Get.isRegistered<CaptainWalletController>()) {
        _homeCtrl = Get.find<HomeCaptainController>();
        _walletCtrl = Get.find<CaptainWalletController>();
        return true;
      }
      await Future.delayed(const Duration(milliseconds: 500));
      attempts++;
    }
    return false;
  }

  // ===================================================================
  // ====== Socket Logic (Improved) ======
  // ===================================================================

  bool _isInitializingSocket = false;

  void initSocket() {
    // منع الاستدعاءات المتداخلة التي تسبب قتل الاتصال قبل اكتماله
    if (_isInitializingSocket) {
      Log.print("⏳ Socket is already initializing. Skipping redundant call.");
      return;
    }

    if (socket != null && socket!.connected) {
      Log.print("✅ Socket is already connected. No need to re-init.");
      return;
    }

    String driverId = box.read(BoxName.driverID).toString();
    String token = box.read(BoxName.tokenDriver).toString();
    String platform = Platform.isIOS ? 'ios' : 'android';

    _isInitializingSocket = true;

    // تنظيف السوكيت القديم فقط إذا كان موجوداً وغير متصل
    if (socket != null) {
      Log.print("🧹 Cleaning up old socket instance...");
      socket!.clearListeners();
      socket!.dispose();
      socket = null;
    }

    Log.print(
        "🟡 [LocationController] Initializing NEW Socket for Driver: $driverId");

    try {
      // العودة للـ Websocket حصراً لأنه الوحيد الذي ينجح في فتح القناة
      socket = IO.io(
          'https://location.intaleq.xyz',
          IO.OptionBuilder()
              .setTransports(['websocket'])
              .setQuery({'driver_id': driverId, 'token': token, 'EIO': '3'})
              .enableForceNew()
              .build());

      _setupSocketListeners();
      socket!.connect();
    } catch (e) {
      _isInitializingSocket = false;
      Log.print("❌ Socket Initialization Exception: $e");
    }
  }

  void _setupSocketListeners() {
    if (socket == null) return;

    socket!.off('connect');
    socket!.off('disconnect');
    socket!.off('connect_error');
    socket!.off('error');

    socket!.onConnect((_) {
      _isInitializingSocket = false;

      // ننتظر قليلاً للتأكد من تعبئة الـ IDs
      Future.delayed(const Duration(milliseconds: 1000), () {
        String? sid = socket?.id;
        String? eid = socket?.io.engine?.id;

        Log.print(
            '✅ Socket Connected! ID: ${sid ?? eid ?? 'N/A'} (SID: $sid, EID: $eid)');

        if (sid != null || eid != null) {
          isSocketConnected = true;
          _startHeartbeat();
        }
      });
    });

    socket!.onDisconnect((data) {
      _isInitializingSocket = false;
      Log.print('❌ Socket Disconnected: $data');
      isSocketConnected = false;
      _stopHeartbeat();
    });

    socket!.onConnectError((err) {
      _isInitializingSocket = false;
      Log.print('❌ Socket Connect Error: $err');
    });

    socket!.onConnectTimeout((data) {
      _isInitializingSocket = false;
      Log.print('❌ Socket Connect Timeout: $data');
    });

    socket!.onError((err) {
      _isInitializingSocket = false;
      Log.print('❌ Socket General Error: $err');
    });

    socket!.on('reconnect_attempt', (attempt) {
      Log.print('🔄 Socket Reconnecting... Attempt: $attempt');
    });

    // 🔥 الاستماع للطلبات الجديدة
    socket!.on('new_ride_request', (data) {
      Log.print("🔔 Socket: New Ride Request Arrived!");

      // نستخدم Future.microtask لضمان عدم حظر الـ UI Thread
      Future.microtask(() {
        if (data != null) {
          try {
            List<dynamic> rawList = [];
            if (data is String) {
              var decoded = jsonDecode(data);
              if (decoded is List) rawList = decoded;
            } else if (data is List) {
              if (data.isNotEmpty) {
                rawList = (data[0] is List) ? data[0] : data;
              }
            }

            if (rawList.isNotEmpty) {
              Map<String, dynamic> convertedMap = {};
              for (int i = 0; i < rawList.length; i++) {
                convertedMap[i.toString()] = rawList[i];
              }
              handleIncomingOrder(convertedMap, "Socket");
            }
          } catch (e) {
            Log.print("❌ Error processing socket data: $e");
          }
        }
      });
    });

    // 🔥 الاستماع للإلغاء
    socket!.on('cancel_ride', (data) {
      Log.print("🚫 Socket: Ride Cancelled Event Received");
      String reason = data['reason'] ?? 'No reason provided';
      if (Get.isRegistered<MapDriverController>()) {
        Get.find<MapDriverController>()
            .processRideCancelledByPassenger(reason, source: "Socket");
      }
    });
  }

  // داخل LocationController

  Future<void> handleIncomingOrder(
      Map<String, dynamic> rideData, String source) async {
    Log.print("📦 Socket Order Received from ($source)");

    // 🔴 1. التحقق من حالة التطبيق قبل أي شيء 🔴
    bool isAppInForeground = box.read(BoxName.isAppInForeground) ?? false;

    if (!isAppInForeground) {
      Log.print(
          "📱 [LocationController] Order received in background (iOS/Android). Source: $source");

      if (Platform.isIOS) {
        // على iOS، نقوم بإظهار إشعار محلي لأن الـ Overlay غير مدعوم
        NotificationController().showNotification(
            "طلب رحلة جديد 🚖",
            "لديك طلب رحلة جديد، افتح التطبيق للموافقة عليه",
            jsonEncode(rideData),
            'ding.wav');
      }
      return;
    }

    try {
      // 2. التحقق من صحة البيانات
      if (rideData.isEmpty || !rideData.containsKey('16')) {
        Log.print("❌ Socket Error: Invalid Ride Data.");
        return;
      }

      // 3. تجهيز البيانات (DriverList)
      List<dynamic> driverList = [];
      if (rideData.isNotEmpty) {
        var sortedKeys = rideData.keys
            .where((e) => int.tryParse(e) != null)
            .map((e) => int.parse(e))
            .toList()..sort();
        
        for (var key in sortedKeys) {
          driverList.add(rideData[key.toString()]);
        }
      }

      // الحماية ضد البنية غير المكتملة
      if (driverList.length <= 16) {
        Log.print("❌ Socket Error: Parsed driver list is incomplete.");
        return;
      }

      // 4. إغلاق النافذة (إن وجدت بالخطأ) والتنقل
      try {
        if (await TripOverlayPlugin.isOverlayActive()) {
          Log.print("📲 Closing Overlay because App took control via Socket");
          await TripOverlayPlugin.hideOverlay();
        }
      } catch (e) {
        Log.print("Overlay check error: $e");
      }

      if (Get.currentRoute != '/OrderRequestPage') {
        Log.print("🚀 Socket: Navigating to OrderRequestPage...");
        Get.toNamed('/OrderRequestPage', arguments: {
          'myListString': jsonEncode(driverList),
          'DriverList': driverList,
          'body': 'New Trip Request via Socket ⚡'
        });
      } else {
        Log.print(
            "⚠️ User is already on OrderRequestPage. Skipping navigation.");
      }
    } catch (e) {
      Log.print("❌ Socket Navigation Error: $e");
    }
  }

  void _startHeartbeat() {
    _socketHeartbeat?.cancel();
    _socketHeartbeat = Timer.periodic(const Duration(seconds: 25), (timer) {
      if (socket != null && isSocketConnected && myLocation.latitude != 0) {
        emitLocationToSocket(myLocation, heading, speed);
      }
    });
  }

  void _stopHeartbeat() {
    _socketHeartbeat?.cancel();
  }

  // In LocationController.dart

  void emitLocationToSocket(LatLng pos, double head, double spd) {
    String status = box.read(BoxName.statusDriverLocation) ?? 'on';
    String? currentRideStatus = box.read(BoxName.rideStatus);
    String? storedPassengerId = box.read(BoxName.passengerID);
    String? storedRideId = box.read(BoxName.rideId);

    // Basic payload
    var payload = {
      'driver_id': box.read(BoxName.driverID),
      'lat': pos.latitude,
      'lng': pos.longitude,
      'heading': head,
      'speed': spd * 3.6,
      'status': status,
      'distance': totalDistance,
    };

    // 🔥 القرار الذكي: حقن بيانات الراكب إذا كان هناك رحلة نشطة في الـ Box 🔥
    bool hasActiveRide = (currentRideStatus == 'Begin' ||
        currentRideStatus == 'Apply' ||
        currentRideStatus == 'Arrived');

    if (hasActiveRide && storedPassengerId != null) {
      payload['passenger_id'] = storedPassengerId;
      payload['ride_id'] = storedRideId;
    }

    // DebugLog.print to verify
    //Log.print('🚀 Emitting Location: $payload');

    if (socket != null && socket!.connected) {
      socket!.emit('update_location', payload);
    }
  }
  // ===================================================================
  // ====== Tracking Logic ======
  // ===================================================================

  Future<void> startLocationUpdates() async {
    _isReady = true;
    String currentStatus = box.read(BoxName.statusDriverLocation) ?? 'off';
    if (currentStatus == 'blocked') {
      stopLocationUpdates();
      return;
    }

    // Start background service
    await BackgroundServiceHelper.startService();

    if (socket == null || !socket!.connected) {
      initSocket();
    }

    if (_locSub != null) return;

    if (await _ensureServiceAndPermission()) {
      _subscribeLocationStream();
      _startBatchTimers();
    }
  }

  Future<void> _subscribeLocationStream() async {
    _locSub?.cancel();
    int interval = _isPowerSavingMode ? 10000 : 5000;
    await location.enableBackgroundMode(enable: true);
    location.changeSettings(
      accuracy: LocationAccuracy.navigation,
      interval: interval,
      distanceFilter: _isPowerSavingMode ? 20 : 10,
    );

    _locSub = location.onLocationChanged.listen((LocationData loc) async {
      if (loc.latitude == null || loc.longitude == null) return;

      final now = DateTime.now();
      final pos = LatLng(loc.latitude!, loc.longitude!);

      myLocation = pos;
      speed = loc.speed ?? 0.0;
      heading = loc.heading ?? 0.0;

      if (_lastPosForDistance != null) {
        final d = _calculateDistance(_lastPosForDistance!, pos);
        if (d > 5.0) totalDistance += d;
      }
      _lastPosForDistance = pos;

      update();
      emitLocationToSocket(pos, heading, speed);
      await _saveBehaviorIfMoved(pos, now, currentSpeed: speed);
    }, onError: (e) => Log.print('❌ Location Stream Error: $e'));
  }

  Future<void> stopLocationUpdates() async {
    Log.print("🛑 Stopping Location Updates...");

    _locSub?.cancel();
    _locSub = null;
    _recordTimer?.cancel();
    _uploadBatchTimer?.cancel();
    _socketHeartbeat?.cancel();

    if (socket != null) {
      socket!.clearListeners();
      socket!
          .dispose(); // استخدام dispose بدلاً من disconnect لضمان تحرير الموارد على iOS
    }

    if (!Platform.isIOS) {
      await BackgroundServiceHelper.stopService();
    }

    socket = null;
    isSocketConnected = false;
    _isReady = false;
  }

  // ===================================================================
  // ====== Batch Logic & Helpers ======
  // ===================================================================

  void _startBatchTimers() {
    _recordTimer?.cancel();
    _uploadBatchTimer?.cancel();

    final recDur =
        _isPowerSavingMode ? recordIntervalPowerSave : recordIntervalNormal;
    final upDur = _isPowerSavingMode
        ? uploadBatchIntervalPowerSave
        : uploadBatchIntervalNormal;

    _recordTimer =
        Timer.periodic(recDur, (_) => _recordCurrentLocationToBuffer());
    _uploadBatchTimer = Timer.periodic(upDur, (_) => _flushBufferToServer());
  }

  void _recordCurrentLocationToBuffer() {
    if (myLocation.latitude == 0) return;
    final now = DateTime.now();
    double distFromLast = 0.0;
    if (_lastRecordedRealLoc != null) {
      distFromLast = _calculateDistance(_lastRecordedRealLoc!, myLocation);
    }
    bool moved = distFromLast > 10.0;
    bool timeForced = _lastRecordedTime == null ||
        now.difference(_lastRecordedTime!).inSeconds >= 60;

    if ((moved && speed > 0.5) || timeForced) {
      _lastRecordedRealLoc = myLocation;
      _lastRecordedTime = now;
      final point = {
        'lat': double.parse(myLocation.latitude.toStringAsFixed(6)),
        'lng': double.parse(myLocation.longitude.toStringAsFixed(6)),
        'spd': double.parse((speed * 3.6).toStringAsFixed(1)),
        'head': int.parse(heading.toStringAsFixed(0)),
        'st': box.read(BoxName.statusDriverLocation) ?? 'off',
        'ts': now.toIso8601String(),
      };
      _trackBuffer.add(point);
    }
  }

  Future<void> _flushBufferToServer() async {
    if (_trackBuffer.isEmpty) return;
    
    int itemsToTake = _trackBuffer.length > 100 ? 100 : _trackBuffer.length;
    List<Map<String, dynamic>> batch = _trackBuffer.sublist(0, itemsToTake);
    
    final String driverId = (box.read(BoxName.driverID) ?? '').toString();
    try {
      var res = await CRUD().post(
        link: '${AppLink.locationServer}/add_batch.php',
        payload: {'driver_id': driverId, 'batch_data': jsonEncode(batch)},
      );
      if (res != 'failure') {
        _trackBuffer.removeRange(0, itemsToTake);
      } else {
        _enforceBufferLimit();
      }
    } catch (e) {
      Log.print('❌ Failed to upload batch: $e');
      _enforceBufferLimit();
    }
  }

  void _enforceBufferLimit() {
    if (_trackBuffer.length > 500) {
      _trackBuffer.removeRange(0, _trackBuffer.length - 500);
      Log.print("⚠️ Buffer limit enforced. Removed oldest entries.");
    }
  }

  void _listenToBatteryChanges() async {
    _battery.onBatteryStateChanged.listen((state) async {
      int level = await _battery.batteryLevel;
      bool previousMode = _isPowerSavingMode;
      if (level <= powerSaveTriggerLevel) _isPowerSavingMode = true;
      if (level >= powerSaveExitLevel) _isPowerSavingMode = false;
      if (previousMode != _isPowerSavingMode) {
        _startBatchTimers();
        _updateLocationSettings();
      }
    });
  }

  Future<void> _updateLocationSettings() async {
    if (_locSub == null) return;
    int interval = _isPowerSavingMode ? 10000 : 5000;
    try {
      await location.changeSettings(
        accuracy: LocationAccuracy.navigation,
        interval: interval,
        distanceFilter: _isPowerSavingMode ? 20 : 10,
      );
      Log.print("🔋 Location settings updated. Power Save: $_isPowerSavingMode");
    } catch (e) {
      Log.print("❌ Failed to update location settings: $e");
    }
  }

  Future<void> _saveBehaviorIfMoved(LatLng pos, DateTime now,
      {required double currentSpeed}) async {
    final dist =
        (_lastSqlLoc == null) ? 999.0 : _calculateDistance(_lastSqlLoc!, pos);
    if (dist < 15.0) return;
    
    final accel = _calcAcceleration(currentSpeed, now) ?? 0.0;
    _lastSqlLoc = pos;

    _behaviorBuffer.add({
      'driver_id': (box.read(BoxName.driverID) ?? '').toString(),
      'latitude': pos.latitude,
      'longitude': pos.longitude,
      'acceleration': accel,
      'created_at': now.toIso8601String(),
      'updated_at': now.toIso8601String(),
    });

    if (_behaviorBuffer.length >= 10) {
      _flushBehaviorBuffer();
    }
  }

  void _flushBehaviorBuffer() {
    if (_behaviorBuffer.isEmpty) return;
    List<Map<String, dynamic>> batch = List.from(_behaviorBuffer);
    _behaviorBuffer.clear();
    
    Future.microtask(() async {
      try {
        for (var data in batch) {
          await sql.insertData(data, TableName.behavior);
        }
      } catch (e) {
        Log.print('SQLite Batch Insert Error: $e');
      }
    });
  }

  // استبدال دالة Haversine اليدوية بـ Geolocator في باقي الكود أيضاً
  // لأنها تعتمد على C++ في الأندرويد و Obj-C في الآيفون (Native Speed)
  double _calculateDistance(LatLng a, LatLng b) {
    return geo.Geolocator.distanceBetween(
        a.latitude, a.longitude, b.latitude, b.longitude);
  }

  double? _calcAcceleration(double currentSpeed, DateTime now) {
    if (_lastSpeed != null && _lastSpeedAt != null) {
      final dt = now.difference(_lastSpeedAt!).inMilliseconds / 1000.0;
      if (dt > 0.5) {
        final a = (currentSpeed - _lastSpeed!) / dt;
        _lastSpeed = currentSpeed;
        _lastSpeedAt = now;
        return a;
      }
    }
    _lastSpeed = currentSpeed;
    _lastSpeedAt = now;
    return null;
  }

  Future<void> _initLocationSettings() async {
    if (await _ensureServiceAndPermission()) {
      try {
        await location.enableBackgroundMode(enable: true);
        location.changeSettings(
            accuracy: LocationAccuracy.navigation,
            interval: 1000,
            distanceFilter: 10);
      } catch (e) {
        Log.print("Warning: $e");
      }
    }
  }

// 🔥🔥 هذه هي الدالة المعدلة التي تستخدم ph.Permission 🔥🔥
  Future<bool> _ensureServiceAndPermission() async {
    // 1. طلب إذن الإشعارات أولاً باستخدام permission_handler
    if (Platform.isAndroid) {
      var notificationStatus = await ph.Permission.notification.status;
      if (!notificationStatus.isGranted) {
        await ph.Permission.notification.request();
      }
    }

    // 2. طلب تفعيل خدمة الموقع (GPS) من بكج location
    bool serviceEnabled = await location.serviceEnabled();
    if (!serviceEnabled) {
      serviceEnabled = await location.requestService();
      if (!serviceEnabled) return false;
    }

    // 3. طلب إذن الموقع الأساسي من بكج location
    PermissionStatus permissionGranted = await location.hasPermission();
    if (permissionGranted == PermissionStatus.denied) {
      permissionGranted = await location.requestPermission();
      if (permissionGranted != PermissionStatus.granted) return false;
    }

    return true;
  }
// ... (باقي الكود)

  Future<LocationData?> getLocation() async {
    try {
      if (await _ensureServiceAndPermission()) {
        return await location.getLocation();
      }
    } catch (e) {
      Log.print('❌ FAILED to get single location: $e');
    }
    return null;
  }
}
