// Kétszintű Supabase szinkronizáció:
//   1. Élő pozíció — minden 3 mp-ben UPSERT → device_positions
//   2. Track pontok — batch INSERT (10 pont vagy 8 mp) → terepi_track_points

import 'dart:async';

import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:get/get.dart';
import 'package:latlong2/latlong.dart';
import 'package:supabase_flutter/supabase_flutter.dart';

import 'app_database.dart';
import 'device_identity_service.dart';
import '../models/track.dart';

class TrackSyncService extends GetxService {
  static TrackSyncService get to => Get.find();

  final _supabase = Supabase.instance.client;

  // ── Konfiguráció ──────────────────────────────────────────────────
  static const _batchSize = 10;
  static const _batchIntervalSec = 8;
  static const _positionIntervalSec = 3;

  // ── Belső állapot ─────────────────────────────────────────────────
  final _buffer = <TrackPoint>[];
  Timer? _batchTimer;
  Timer? _posTimer;

  Track? _track;
  LatLng? _lastPos; // utoljára kapott pozíció (broadcasthoz)
  bool _online = false;

  // ── Publikus állapot ──────────────────────────────────────────────
  final isSyncing = false.obs;
  final pendingCount = 0.obs;

  // ── Lifecycle ─────────────────────────────────────────────────────

  @override
  Future<void> onInit() async {
    super.onInit();
    _online = await _checkOnline();
    _listenConnectivity();
  }

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

  // ── Session vezérlés (TrackingController hívja) ───────────────────

  void startSession(Track track) {
    _track = track;
    _buffer.clear();
    pendingCount.value = 0;

    if (track.isLocalOnly) return;

    _batchTimer = Timer.periodic(
      const Duration(seconds: _batchIntervalSec),
      (_) => _flush(),
    );
    _posTimer = Timer.periodic(
      const Duration(seconds: _positionIntervalSec),
      (_) => _broadcastPosition(),
    );
  }

  Future<void> stopSession(Track track) async {
    _stopTimers();
    if (track.isLocalOnly) return;

    await _flush(); // utolsó batch

    // Track fejléc lezárása Supabase-ben
    if (track.supabaseId != null) {
      await _supabase.from('terepi_seged_tracks').update({
        'end_time': track.endTime?.toIso8601String(),
        'status': 'finished',
        'distance_m': track.distanceMeters,
        'point_count': track.pointCount,
      }).eq('id', track.supabaseId!);
    }

    await _setInactive();
    _track = null;
    _lastPos = null;
  }

  // ── Pont pufferelés ───────────────────────────────────────────────

  /// TrackingController._onPosition() hívja minden pontnál
  void onNewPoint(TrackPoint point) {
    _lastPos = LatLng(point.latitude, point.longitude);

    if (_track == null || _track!.isLocalOnly) return;

    _buffer.add(point);
    pendingCount.value = _buffer.length;

    if (_buffer.length >= _batchSize) _flush();
  }

  // ── Supabase track létrehozása ────────────────────────────────────

  /// startRecording()-ban hívandó, visszaadja a Supabase UUID-t
  Future<String?> createRemoteTrack(Track track) async {
    if (!_online) return null;
    try {
      final res = await _supabase
          .from('terepi_seged_tracks')
          .insert({
            'device_id': DeviceIdentityService.to.deviceId,
            'name': track.name,
            'source': track.source,
            'start_time': track.startTime.toIso8601String(),
            'status': 'recording',
          })
          .select('id')
          .single();
      return res['id'] as String?;
    } catch (e) {
      return null;
    }
  }

  // ── Batch feltöltés ───────────────────────────────────────────────

  Future<void> _flush() async {
    if (_buffer.isEmpty || !_online) return;

    final supabaseId = _track?.supabaseId;
    if (supabaseId == null) return;

    final batch = List<TrackPoint>.from(_buffer);
    _buffer.clear();
    pendingCount.value = 0;

    try {
      isSyncing.value = true;
      await _supabase.from('terepi_seged_track_points').insert(
            batch
                .map((p) => {
                      'track_id': supabaseId,
                      'latitude': p.latitude,
                      'longitude': p.longitude,
                      'altitude': p.altitude,
                      'accuracy': p.accuracy,
                      'speed': p.speed,
                      'heading': p.heading,
                      'timestamp': p.timestamp.toIso8601String(),
                    })
                .toList(),
          );
    } catch (_) {
      // Hiba → visszateszi a bufferbe
      _buffer.insertAll(0, batch);
      pendingCount.value = _buffer.length;
    } finally {
      isSyncing.value = false;
    }
  }

  // ── Élő pozíció broadcast ─────────────────────────────────────────

  Future<void> _broadcastPosition() async {
    final pos = _lastPos;
    if (pos == null || !_online) return;

    final device = DeviceIdentityService.to;
    try {
      await _supabase.from('terepi_seged_device_positions').upsert({
        'device_id': device.deviceId,
        'user_name': device.deviceLabel.value,
        'latitude': pos.latitude,
        'longitude': pos.longitude,
        'track_id': _track?.supabaseId,
        'is_active': true,
        'updated_at': DateTime.now().toUtc().toIso8601String(),
      }, onConflict: 'device_id');
    } catch (_) {}
  }

  Future<void> _setInactive() async {
    final deviceId = DeviceIdentityService.to.deviceId;
    try {
      await _supabase.from('terepi_seged_device_positions').update(
          {'is_active': false, 'track_id': null}).eq('device_id', deviceId);
    } catch (_) {}
  }

  // ── Offline → Online szinkron ─────────────────────────────────────

  Future<void> syncTrack(Track track) async {
    if (!_online) return;

    String? supabaseId = track.supabaseId;
    if (supabaseId == null) {
      supabaseId = await createRemoteTrack(track);
      if (supabaseId == null) return;
      final updated = track.copyWith(supabaseId: supabaseId);
      await AppDatabase.instance.updateTrack(updated);
    }

    final points = await AppDatabase.instance.getPoints(track.id!);
    if (points.isEmpty) return;

    const chunk = 100;
    for (int i = 0; i < points.length; i += chunk) {
      final slice = points.sublist(i, (i + chunk).clamp(0, points.length));
      await _supabase.from('terepi_seged_track_points').insert(
            slice
                .map((p) => {
                      'track_id': supabaseId,
                      'latitude': p.latitude,
                      'longitude': p.longitude,
                      'altitude': p.altitude,
                      'accuracy': p.accuracy,
                      'speed': p.speed,
                      'heading': p.heading,
                      'timestamp': p.timestamp.toIso8601String(),
                    })
                .toList(),
          );
    }

    await AppDatabase.instance.updateTrack(
      track.copyWith(supabaseId: supabaseId),
    );
  }

  // ── Kapcsolat figyelés ────────────────────────────────────────────

  void _listenConnectivity() {
    Connectivity().onConnectivityChanged.listen((results) async {
      final wasOffline = !_online;
      _online = results.any((r) => r != ConnectivityResult.none);
      if (wasOffline && _online) await _flush();
    });
  }

  Future<bool> _checkOnline() async {
    final r = await Connectivity().checkConnectivity();
    return r.any((r) => r != ConnectivityResult.none);
  }

  void _stopTimers() {
    _batchTimer?.cancel();
    _posTimer?.cancel();
    _batchTimer = null;
    _posTimer = null;
  }
}
