import 'dart:async';
import 'dart:convert';
import 'package:get/get.dart';
import 'package:socket_io_client/socket_io_client.dart' as io_client;
import 'package:intaleq_maps/intaleq_maps.dart';

import '../../../constant/box_name.dart';
import '../../../constant/links.dart';
import '../../../main.dart'; // contains global 'box'
import '../../../print.dart';
import 'ride_lifecycle_controller.dart';
import 'nearby_drivers_controller.dart';
import 'map_engine_controller.dart';

class MapSocketController extends GetxController {
  late io_client.Socket socket;
  bool isSocketConnected = false;
  bool _isSocketInitialized = false;
  Timer? _heartbeatTimer;
  DateTime? _lastSocketLocationTime;
  int _socketLocationUpdatesCount = 0;
  Timer? _watchdogTimer;

  DateTime? get lastDriverLocationTime => _lastSocketLocationTime;
  int get socketLocationUpdatesCount => _socketLocationUpdatesCount;

  void initConnectionWithSocket() {
    if (isSocketConnected) return;

    String passengerId = box.read(BoxName.passengerID).toString();
    Log.print("🔌 Initializing Socket for Passenger: $passengerId");

    socket = io_client.io(
      AppLink.serverSocket,
      io_client.OptionBuilder()
          .setTransports(['websocket'])
          .disableAutoConnect()
          .setQuery({'id': passengerId})
          .setReconnectionAttempts(20)
          .setReconnectionDelay(2000)
          .setReconnectionDelayMax(10000)
          .enableReconnection()
          .setTimeout(20000)
          .setExtraHeaders({'Connection': 'Upgrade'})
          .build(),
    );
    _isSocketInitialized = true;

    socket.connect();

    socket.onConnect((_) {
      Log.print("✅ Socket Connected Successfully");
      isSocketConnected = true;
      _startHeartbeat();

      final rideLifecycle = Get.find<RideLifecycleController>();
      if (rideLifecycle.rideId != 'yet' && rideLifecycle.driverId.isNotEmpty) {
        socket.emit('subscribe_driver_location', {
          'ride_id': rideLifecycle.rideId,
          'driver_id': rideLifecycle.driverId,
        });
        Log.print("📡 Re-subscribed to driver location after connect");
      }
      update();
    });

    socket.onDisconnect((_) {
      Log.print("⚠️ Socket Disconnected — Auto-Reconnect will handle it");
      isSocketConnected = false;

      final rideLifecycle = Get.find<RideLifecycleController>();
      if (rideLifecycle.isActiveRideState()) {
        Log.print("🔄 Enabling Fast Polling Fallback (4s) until reconnect...");
        rideLifecycle.startMasterTimerWithInterval(4);
      }
      update();
    });

    socket.onReconnect((_) {
      Log.print("🔁 Socket Reconnected Successfully!");
      isSocketConnected = true;
      _startHeartbeat();

      final rideLifecycle = Get.find<RideLifecycleController>();
      if (rideLifecycle.rideId != 'yet' && rideLifecycle.driverId.isNotEmpty) {
        socket.emit('subscribe_driver_location', {
          'ride_id': rideLifecycle.rideId,
          'driver_id': rideLifecycle.driverId,
        });
        Log.print("📡 Re-subscribed to driver location after reconnect");
      }

      if (rideLifecycle.isActiveRideState()) {
        Log.print("✅ Socket back online — stopping Fast Polling Fallback");
        rideLifecycle.cancelMasterTimer();
      }
      update();
    });

    socket.onReconnectAttempt((attemptNumber) {
      Log.print("🔄 Socket Reconnect Attempt #$attemptNumber...");
    });

    socket.onError((error) {
      Log.print("❌ Socket Error: $error");
      isSocketConnected = false;
    });

    socket.on('connect_error', (error) {
      Log.print("❌ Socket Connect Error: $error");
      isSocketConnected = false;
      // في الإصدار 1.0.2 أحياناً auto-reconnect لا يعمل بعد connect_error
      // نتأكد يدوياً من إعادة الاتصال
      Future.delayed(const Duration(seconds: 3), () {
        if (!isSocketConnected && _isSocketInitialized) {
          Log.print("🔄 Manual reconnect after connect_error...");
          try {
            socket.connect();
          } catch (e) {
            Log.print("Manual reconnect error: $e");
          }
        }
      });
    });

    socket.on('ride_status_change', (data) {
      Log.print("📩 Socket Event: ride_status_change -> $data");
      _handleRideStatusChangeWithSocket(data);
    });

    socket.on('driver_location_update', (data) {
      handleDriverLocationUpdate(data);
    });
  }

  void _startHeartbeat() {
    _heartbeatTimer?.cancel();
    _heartbeatTimer = Timer.periodic(const Duration(seconds: 15), (timer) {
      if (isSocketConnected && socket.connected) {
        socket.emit('heartbeat',
            {'passenger_id': box.read(BoxName.passengerID).toString()});
      }
    });
  }

  bool isSocketHealthy() {
    if (!isSocketConnected) return false;
    if (_lastSocketLocationTime == null) return false;
    final diff = DateTime.now().difference(_lastSocketLocationTime!).inSeconds;
    return diff < 20;
  }

  void _handleRideStatusChangeWithSocket(dynamic data) {
    if (data == null || data['status'] == null) return;

    String newStatus = data['status'].toString().toLowerCase();
    Log.print("🔔 Socket Status Update: $newStatus");

    final rideLifecycle = Get.find<RideLifecycleController>();

    Map<String, dynamic>? driverInfo;
    if (data['driver_info'] != null && data['driver_info'] is Map) {
      driverInfo = Map<String, dynamic>.from(data['driver_info']);
    }

    switch (newStatus) {
      case 'accepted':
      case 'apply':
      case 'applied':
        rideLifecycle.processRideAcceptance(
            driverData: driverInfo, source: "Socket");
        break;

      case 'arrived':
        rideLifecycle.processDriverArrival("Socket");
        break;

      case 'started':
      case 'begin':
        rideLifecycle.processRideBegin(source: "Socket");
        break;

      case 'finished':
      case 'ended':
        _onRideFinishedWithSocket(data);
        break;

      case 'cancelled':
        rideLifecycle.processRideCancelledByDriver(data, source: "Socket");
        break;

      case 'no_drivers_found':
        rideLifecycle.showNoDriverDialog();
        break;
    }
  }

  void _onRideFinishedWithSocket(dynamic data) {
    Log.print("🏁 Ride Finished (Socket)");
    final rideLifecycle = Get.find<RideLifecycleController>();

    var rawList = data['DriverList'];
    List<dynamic> listToSend = [];

    if (rawList != null) {
      if (rawList is List) {
        listToSend = rawList;
      } else if (rawList is String) {
        try {
          listToSend = jsonDecode(rawList);
        } catch (e) {
          Log.print("Error decoding DriverList: $e");
        }
      }
    }

    if (listToSend.isEmpty && data['price'] != null) {
      listToSend = [
        rideLifecycle.driverId,
        rideLifecycle.rideId,
        rideLifecycle.driverToken,
        data['price'].toString()
      ];
    }

    rideLifecycle.processRideFinished(listToSend, source: "Socket");
  }

  void handleDriverLocationUpdate(dynamic data) {
    if (!isSocketConnected || data == null) return;
    _lastSocketLocationTime = DateTime.now();
    _socketLocationUpdatesCount++;

    final rideLifecycle = Get.find<RideLifecycleController>();
    if (rideLifecycle.driverId.isEmpty &&
        (data['driver_id'] ?? data['driverId']) != null) {
      rideLifecycle.driverId =
          (data['driver_id'] ?? data['driverId']).toString();
    }

    if (_socketLocationUpdatesCount >= 3 &&
        rideLifecycle.locationPollingTimer != null) {
      Log.print("✅ Socket delivering locations reliably. Stopping polling.");
      rideLifecycle.stopDriverLocationPolling();
    }

    try {
      double lat = double.tryParse(
              (data['latitude'] ?? data['lat'])?.toString() ?? '0') ??
          0;
      double lng = double.tryParse(
              (data['longitude'] ?? data['lng'])?.toString() ?? '0') ??
          0;
      double heading = double.tryParse(data['heading']?.toString() ?? '0') ?? 0;

      if (lat == 0 || lng == 0) return;

      LatLng newPos = LatLng(lat, lng);

      final nearbyDrivers = Get.find<NearbyDriversController>();
      if (nearbyDrivers.driverCarsLocationToPassengerAfterApplied.isEmpty) {
        nearbyDrivers.driverCarsLocationToPassengerAfterApplied.add(newPos);
      } else {
        nearbyDrivers.driverCarsLocationToPassengerAfterApplied[0] = newPos;
      }

      double speed = double.tryParse(data['speed']?.toString() ?? '0') ?? 0;
      rideLifecycle.checkAndRecalculateIfDeviated(
        newPos,
        heading: heading,
        speed: speed,
      );

      final mapEngine = Get.find<MapEngineController>();
      if (mapEngine.mapController != null) {
        double zoom = 16.5;
        if (speed > 0) {
          zoom = 17.0 - ((speed - 10) / 70) * 2.5;
          zoom = zoom.clamp(14.5, 17.0);
        }
        mapEngine.mapController!
            .animateCamera(CameraUpdate.newLatLngZoom(newPos, zoom));
      }

      final dynamic distanceValue =
          data['distance_m'] ?? data['distance_meters'];
      final double? distanceMeters =
          double.tryParse(distanceValue?.toString() ?? '');
      final int? etaSeconds = data['eta_seconds'] == null
          ? null
          : int.tryParse(data['eta_seconds'].toString());
      final bool hasServerMetrics = (etaSeconds != null && etaSeconds > 0) ||
          (distanceMeters != null && distanceMeters > 0);
      if (hasServerMetrics) {
        rideLifecycle.updateDriverRouteMetrics(
          etaSeconds: etaSeconds != null && etaSeconds > 0 ? etaSeconds : null,
          distanceMeters: distanceMeters,
        );
      }

      rideLifecycle.updateDriverMarker(newPos, heading);
      rideLifecycle.updateRemainingRoute(newPos, updateEta: !hasServerMetrics);
      rideLifecycle.update();
    } catch (e) {
      Log.print('Error in handleDriverLocationUpdate: $e');
    }
  }

  void disposeRideSocket() {
    _heartbeatTimer?.cancel();
    _watchdogTimer?.cancel();
    if (_isSocketInitialized) {
      socket.disconnect();
      socket.dispose();
      isSocketConnected = false;
      _isSocketInitialized = false;
      Log.print("🔌 Socket Disposed");
    }
  }

  @override
  void onClose() {
    disposeRideSocket();
    super.onClose();
  }
}
