// lib/services/gnss/ble_gnss_connection.dart
import 'dart:async';
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:geolocator/geolocator.dart';
import 'gnss_connection.dart';
import 'gnss_device_service.dart';

// Nordic UART Service — a legelterjedtebb BLE UART profil
const _nusServiceUuid = '6e400001-b5b3-f393-e0a9-e50e24dcca9e';
const _nusTxCharUuid = '6e400003-b5b3-f393-e0a9-e50e24dcca9e'; // notify
const _nusRxCharUuid = '6e400002-b5b3-f393-e0a9-e50e24dcca9e'; // write

class BleGnssConnection implements GnssConnection {
  @override
  GnssConnectionType get type => GnssConnectionType.ble;

  final String serviceUuid;
  final String txCharUuid; // notify (eszköz → telefon)

  BleGnssConnection({
    this.serviceUuid = _nusServiceUuid,
    this.txCharUuid = _nusTxCharUuid,
  });

  BluetoothDevice? _device;
  StreamSubscription? _notifySub;
  String _lineBuffer = '';
  BluetoothCharacteristic? _rxChar;

  final _nmeaController = StreamController<String>.broadcast();
  final _stateController = StreamController<GnssConnectionState>.broadcast();

  @override
  Stream<String> get nmeaLines => _nmeaController.stream;

  @override
  Stream<GnssConnectionState> get connectionState => _stateController.stream;

  @override
  Future<void> connect(String address) async {
    _stateController.add(GnssConnectionState.connecting);

    try {
      _device = BluetoothDevice.fromId(address);

      await _device!.connect(
          timeout: const Duration(seconds: 10),
          autoConnect: false,
          license: License.free);

      if (Platform.isAndroid) {
        await _device!.requestMtu(512);
      }

      // Kapcsolat megszakadás figyelése
      _device!.connectionState.listen((state) {
        if (state == BluetoothConnectionState.disconnected) {
          _stateController.add(GnssConnectionState.disconnected);
        }
      });

      // Service discovery
      final services = await _device!.discoverServices();
      final gnssService = services.firstWhere(
        (s) => s.serviceUuid.str.toLowerCase() == serviceUuid.toLowerCase(),
        orElse: () =>
            throw Exception('GNSS service nem található: $serviceUuid'),
      );

      // TX karakterisztika (notify) — eszköztől jönnek az NMEA sorok
      final txChar = gnssService.characteristics.firstWhere(
        (c) =>
            c.characteristicUuid.str.toLowerCase() == txCharUuid.toLowerCase(),
        orElse: () => throw Exception('TX char nem található: $txCharUuid'),
      );

      _rxChar = gnssService.characteristics.firstWhere((c) =>
          c.characteristicUuid.str.toLowerCase() ==
          _nusRxCharUuid.toLowerCase());

      // Notify engedélyezése
      await txChar.setNotifyValue(true);

      _notifySub = txChar.onValueReceived.listen(_onBleData);

      _stateController.add(GnssConnectionState.connected);
    } catch (e) {
      _stateController.add(GnssConnectionState.error);
      rethrow;
    }
  }

  void _onBleData(List<int> bytes) {
    // BLE csomagok kis méretűek (≤20 byte MTU default),
    // az NMEA mondatokat össze kell fűzni
    final chunk = String.fromCharCodes(bytes);
    _lineBuffer += chunk;

    // Teljes sorok kibocsátása
    while (_lineBuffer.contains('\n')) {
      final idx = _lineBuffer.indexOf('\n');
      final line = _lineBuffer.substring(0, idx).trim();
      _lineBuffer = _lineBuffer.substring(idx + 1);
      if (line.isNotEmpty) _nmeaController.add(line);
    }
  }

  @override
  Future<void> disconnect() async {
    await _notifySub?.cancel();
    await _device?.disconnect();
    _stateController.add(GnssConnectionState.disconnected);
  }

  @override
  void dispose() {
    _notifySub?.cancel();
    _device?.disconnect();
    _nmeaController.close();
    _stateController.close();
  }

  @override
  void sendData(Uint8List data) {
    if (_rxChar == null) return;

    _rxChar!.write(data, withoutResponse: true);
  }

  @override
  Stream<Position> get positionStream => const Stream.empty();
}
