// lib/services/ntrip_service.dart

import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:shared_preferences/shared_preferences.dart';

/// NTRIP kapcsolatot kezelő singleton service.
///
/// Felelőssége:
///  - Socket kapcsolat az NTRIP casterhez
///  - RTCM adatok fogadása és továbbítása a GNSS vevőnek
///  - GGA mondatok küldése a casternek (5 másodpercenként)
///  - Beállítások tárolása SharedPreferences-ben
///
/// Használat:
/// ```dart
/// // Csatlakozás előtt add meg a callback-et:
/// NtripService.to.onRtcmData = (data) => connection.output.add(data);
/// await NtripService.to.connect();
/// ```
class NtripService extends GetxService {
  static NtripService get to => Get.find();

  // ── Reaktív állapot ───────────────────────────────────────────────
  final isConnected = false.obs;
  final receivedBytes = 0.obs;
  final packetCount = 0.obs;
  final ggaSentCount = 0.obs;
  final ggaLastSentTime = ''.obs;

  // ── Beállítások ───────────────────────────────────────────────────
  final host = '84.206.45.44'.obs; // gnssnet.hu IP
  final port = 2101.obs;
  final mountpoint = 'SGO_RTK3.2'.obs;
  final username = 'elgi03'.obs;
  final password = 'StEfan14'.obs;

  // ── UI controllerek (beállítás dialóghoz) ────────────────────────
  final hostController = TextEditingController();
  final portController = TextEditingController();
  final mountpointController = TextEditingController();
  final usernameController = TextEditingController();
  final passwordController = TextEditingController();

  // ── Belső állapot ────────────────────────────────────────────────
  Socket? _socket;
  StreamSubscription? _socketSub;
  String _lastGgaMessage = '';
  DateTime _lastGgaSentTime =
      DateTime.now().subtract(const Duration(seconds: 30));

  /// Callback: RTCM adat érkezett → a controller továbbítja a GNSS vevőnek.
  /// Beállítás: `NtripService.to.onRtcmData = (data) => connection.output.add(data);`
  Function(Uint8List)? onRtcmData;

  // ── Inicializálás ────────────────────────────────────────────────

  @override
  Future<void> onInit() async {
    super.onInit();
    await _loadSettings();
    _syncControllersFromValues();
  }

  @override
  void onClose() {
    disconnect();
    hostController.dispose();
    portController.dispose();
    mountpointController.dispose();
    usernameController.dispose();
    passwordController.dispose();
    super.onClose();
  }

  // ── Kapcsolat ────────────────────────────────────────────────────

  Future<void> connect() async {
    if (isConnected.value) return;

    try {
      _socket = await Socket.connect(
        InternetAddress(host.value),
        port.value,
        timeout: const Duration(seconds: 5),
      );

      _socket!.encoding = ascii;
      isConnected.value = true;
      receivedBytes.value = 0;
      packetCount.value = 0;

      // HTTP fejléc összeállítása
      final header = _buildNtripHeader();
      _socket!.add(_toUint8List(header));

      // Adatfogadás
      _socketSub = _socket!.listen(
        _onData,
        onError: _onError,
        onDone: _onDone,
      );
    } catch (e) {
      isConnected.value = false;
      Get.snackbar(
        'NTRIP hiba',
        'Nem sikerült csatlakozni: $e',
        backgroundColor: const Color(0xFFB71C1C),
        colorText: const Color(0xFFFFFFFF),
      );
    }
  }

  Future<void> disconnect() async {
    if (!isConnected.value && _socket == null) return;
    await _socketSub?.cancel();
    await _socket?.flush();
    _socket?.close();
    _socket?.destroy();
    _socket = null;
    isConnected.value = false;
    receivedBytes.value = 0;
  }

  void reconnect() async {
    await disconnect();
    await Future.delayed(const Duration(seconds: 1));
    await connect();
  }

  // ── GGA küldés ───────────────────────────────────────────────────

  /// Az NMEA feldolgozó hívja minden GGA mondatnál.
  /// 5 másodpercenként küld egyet az NTRIP casternek.
  void onGgaReceived(String ggaLine, String utcTime) {
    _lastGgaMessage = ggaLine;

    if (!isConnected.value) return;
    if (ggaLine.isEmpty) return;

    final elapsed = DateTime.now().difference(_lastGgaSentTime).inSeconds;
    if (elapsed < 5) return;

    _sendGga(ggaLine);
    ggaSentCount.value++;
    ggaLastSentTime.value = utcTime;
    _lastGgaSentTime = DateTime.now();
  }

  void _sendGga(String ggaMessage) {
    if (_socket == null || !isConnected.value) return;
    _socket!.add(_toUint8List('$ggaMessage\r\n'));
  }

  // ── Belső adatfogadás ────────────────────────────────────────────

  void _onData(Uint8List data) {
    receivedBytes.value = data.length;
    packetCount.value++;

    // Csak RTCM adat (>14 byte) kerül a GNSS vevőhöz
    if (data.length > 14) {
      onRtcmData?.call(data);
    }
  }

  void _onError(dynamic error) {
    _socket?.destroy();
    isConnected.value = false;
    Get.snackbar(
      'NTRIP kapcsolat hiba',
      error.toString(),
      backgroundColor: const Color(0xFFB71C1C),
      colorText: const Color(0xFFFFFFFF),
    );
  }

  void _onDone() async {
    await _socketSub?.cancel();
    await _socket?.flush();
    _socket?.destroy();
    _socket = null;
    isConnected.value = false;
    receivedBytes.value = 0;
  }

  // ── HTTP fejléc összeállítás ─────────────────────────────────────

  String _buildNtripHeader() {
    final auth = _toBase64('${username.value}:${password.value}');
    final host = '${this.host.value}:${port.value}';

    return 'GET /${mountpoint.value} HTTP/1.1\r\n'
        'User-Agent: SharpGps iter.dk\r\n'
        'Accept: */*\r\n'
        'Connection: close\r\n'
        'Authorization: Basic $auth\r\n'
        'Host: $host\r\n'
        'Ntrip-Version: Ntrip/2.0\r\n'
        '\r\n';
  }

  // ── Beállítások mentése / betöltése ──────────────────────────────

  Future<void> saveSettings() async {
    // Szinkronizálás a controllerektől az Rx értékekbe
    host.value = hostController.text.trim();
    port.value = int.tryParse(portController.text) ?? 2101;
    mountpoint.value = mountpointController.text.trim();
    username.value = usernameController.text.trim();
    password.value = passwordController.text;

    final prefs = await SharedPreferences.getInstance();
    await prefs.setString('ntrip_host', host.value);
    await prefs.setInt('ntrip_port', port.value);
    await prefs.setString('ntrip_mountpoint', mountpoint.value);
    await prefs.setString('ntrip_username', username.value);
    await prefs.setString('ntrip_password', password.value);
  }

  Future<void> _loadSettings() async {
    final prefs = await SharedPreferences.getInstance();
    host.value = prefs.getString('ntrip_host') ?? '84.206.45.44';
    port.value = prefs.getInt('ntrip_port') ?? 2101;
    mountpoint.value = prefs.getString('ntrip_mountpoint') ?? 'SGO_RTK3.2';
    username.value = prefs.getString('ntrip_username') ?? 'elgi03';
    password.value = prefs.getString('ntrip_password') ?? 'StEfan14';
  }

  void _syncControllersFromValues() {
    hostController.text = host.value;
    portController.text = port.value.toString();
    mountpointController.text = mountpoint.value;
    usernameController.text = username.value;
    // Jelszót nem pre-töltjük biztonsági okokból
  }

  // ── Segédfüggvények ──────────────────────────────────────────────

  String _toBase64(String str) => base64.encode(ascii.encode(str));

  Uint8List _toUint8List(String str) => Uint8List.fromList(str.codeUnits);
}
