import 'dart:async'; import 'package:socket_io_client/socket_io_client.dart' as io; import '../../../core/config.dart'; import '../../../core/storage/token_store.dart'; import 'trip_event_merger.dart'; /// خدمة WebSocket — تتبع رحلة عبر Socket.IO. /// /// الاتصال مُصادق عبر JWT (نفس توكن API). /// الأحداث الواردة تُمرّ عبر TripEventMerger. class TripSocketService { final TokenStore _tokenStore; final TripEventMerger _merger; io.Socket? _socket; bool _connected = false; String? _currentTripId; TripSocketService(this._tokenStore, this._merger); /// الاتصال بالخادم. Future connect() async { if (_connected) return; final token = await _tokenStore.access(); if (token == null || token.isEmpty) return; _socket = io.io( AppConfig.baseUrl.replaceAll('/api', ''), io.OptionBuilder() .setTransports(['websocket']) .enableAutoConnect() .enableReconnection() .setReconnectionDelay(2000) .setReconnectionAttempts(10) .setAuth({'token': token}) .build(), ); _socket!.onConnect((_) { _connected = true; }); _socket!.onDisconnect((_) { _connected = false; }); _socket!.onReconnect((_) { // عند إعادة الاتصال: أعد الانضمام لغرفة الرحلة. if (_currentTripId != null) { joinTrip(_currentTripId!); } }); // استقبال تحديثات الحالة. _socket!.on('trip:update', (data) { if (data is! Map) return; final map = Map.from(data); _merger.addUpdate(TripUpdate( tripId: map['tripId'] as String? ?? '', status: map['status'] as String? ?? '', driverId: map['driverId'] as String?, extra: map..remove('tripId')..remove('status')..remove('driverId'), )); }); // استقبال موقع السائق الحي. _socket!.on('driver:location', (data) { if (data is! Map) return; final map = Map.from(data); _merger.addLocation(DriverLocationUpdate( tripId: map['tripId'] as String? ?? '', lat: (map['lat'] as num?)?.toDouble() ?? 0, lng: (map['lng'] as num?)?.toDouble() ?? 0, heading: (map['heading'] as num?)?.toDouble() ?? 0, speed: (map['speed'] as num?)?.toDouble() ?? 0, )); }); _socket!.connect(); } /// الانضمام لغرفة الرحلة. void joinTrip(String tripId) { _currentTripId = tripId; _socket?.emit('trip:join', {'tripId': tripId}); } /// مغادرة غرفة الرحلة. void leaveTrip(String tripId) { _currentTripId = null; // Socket.IO لا يدعم مغادرة الغرفة بشكل صريح، // لكن عند رحلة جديدة سننضم للغرفة الجديدة. } /// قطع الاتصال. void disconnect() { _socket?.disconnect(); _socket?.dispose(); _socket = null; _connected = false; } bool get isConnected => _connected; }