import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:flutter_foreground_task/flutter_foreground_task.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:latlong2/latlong.dart';
import 'package:share_plus/share_plus.dart';
import 'package:terepi_seged/services/app_logger.dart';
import 'package:terepi_seged/services/firebase_logger.dart';
import 'package:terepi_seged/services/project_service.dart';
import 'package:terepi_seged/services/track_sync_service.dart';

import '../../../../services/location_source.dart';
import '../../../../services/phone_gps_source.dart';
import '../../../../services/track_database.dart';
import '../../../../services/gpx_exporter.dart';
import '../../../../models/track.dart';

// ─── Foreground task handler ─────────────────────────────────────────────────
// Ez a függvény a háttér-isolate-ban fut. Csak a meglevő pozíció-streamet
// tartja fenn, a tényleges adatfeldolgozás a főszálban történik.

@pragma('vm:entry-point')
void startTrackingCallback() {
  FlutterForegroundTask.setTaskHandler(_TrackingTaskHandler());
}

class _TrackingTaskHandler extends TaskHandler {
  @override
  Future<void> onStart(DateTime timestamp, TaskStarter starter) async {}

  @override
  void onRepeatEvent(DateTime timestamp) {
    // A Geolocator stream a főizoláton folyik tovább, itt nincs teendő.
  }

  @override
  Future<void> onDestroy(DateTime timestamp, bool isTimeout) async {}
}

// ─── TrackingController ──────────────────────────────────────────────────────

class TrackingController extends GetxController {
  static TrackingController get to => Get.find();

  // ── Állapot ────────────────────────────────────────────────────────────────
  final isRecording = false.obs;
  final isPaused = false.obs;
  final currentTrack = Rxn<Track>();

  /// Aktuális session pontjai a térképhez (csak a memóriában).
  final livePoints = <LatLng>[].obs;

  /// Aktuális sebesség [km/h].
  final currentSpeedKmh = 0.0.obs;

  /// Megtett távolság [m] az aktuális sessionben.
  final sessionDistance = 0.0.obs;

  /// Eltelt idő formátuma óó:pp:mm.
  final elapsedFormatted = '00:00:00'.obs;

  /// Előtér/háttér állapot jelzése.
  final isInBackground = false.obs;

  // ── Mentett track-ek listája ────────────────────────────────────────────────
  final savedTracks = <Track>[].obs;

  // Melyik track-ek látszanak overlay-ként a térképen
  final overlayTrackIds = <int>[].obs;

  // Betöltött koordináták cache
  final Map<int, List<LatLng>> _trackCoords = {};

  int get livePointCount => livePoints.length;

  // ── Belső állapot ──────────────────────────────────────────────────────────
  LocationSource? _source;
  StreamSubscription<SourcePosition>? _positionSub;
  Timer? _elapsedTimer;

  TrackPoint? _lastPoint;
  DateTime? _sessionStart;
  double _accumulatedDistance = 0;

  final _db = TrackDatabase.instance;
  final _exporter = GpxExporter();

  // Timer
  DateTime? _lastPositionTime;
  Timer? _watchdogTimer;
  final gpsSignalLost = false.obs;

  static const _gpsTimeoutSec = 10;

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

  @override
  void onInit() {
    super.onInit();
    _initForegroundTask();
    loadSavedTracks();
  }

  void _initForegroundTask() {
    FlutterForegroundTask.init(
      androidNotificationOptions: AndroidNotificationOptions(
        channelId: 'tracking_channel',
        channelName: 'Nyomvonal rögzítés',
        channelDescription: 'Aktív track rögzítése folyamatban',
        channelImportance: NotificationChannelImportance.LOW,
        priority: NotificationPriority.LOW,
      ),
      iosNotificationOptions: const IOSNotificationOptions(
        showNotification: true,
        playSound: false,
      ),
      foregroundTaskOptions: ForegroundTaskOptions(
        eventAction: ForegroundTaskEventAction.repeat(1000),
        autoRunOnBoot: false,
        allowWakeLock: true,
        allowWifiLock: false,
      ),
    );
  }

  // ── Publikus API ───────────────────────────────────────────────────────────

  /// Elindítja a rögzítést a megadott forrással (alapértelmezett: telefon GPS).
  Future<void> startRecording(
      {LocationSource? source,
      String? name,
      TrackSyncMode syncMode = TrackSyncMode.online}) async {
    if (isRecording.value) return;

    _source = source ?? PhoneGpsSource(intervalMs: 1000, distanceFilter: 2.0);

    // Track létrehozása az adatbázisban
    final now = DateTime.now();
    final trackName = name ?? DateFormat('yyyy-MM-dd HH:mm').format(now);
    final trackId = await _db.insertTrack(Track(
        name: trackName,
        startTime: now,
        source: _source!.displayName,
        projectId: ProjectService.to.activeProjectId,
        syncMode: syncMode));
    currentTrack.value = await _db.getTrack(trackId);

    // Állapot reset
    livePoints.clear();
    sessionDistance.value = 0;
    _accumulatedDistance = 0;
    _lastPoint = null;
    _sessionStart = now;

    // Foreground service indítása (háttér-működéshez)
    await FlutterForegroundTask.startService(
      notificationTitle: 'Track rögzítése',
      notificationText: trackName,
      callback: startTrackingCallback,
    );

    if (!(currentTrack.value?.isLocalOnly ?? true)) {
      TrackSyncService.to
          .createRemoteTrack(currentTrack.value!)
          .then((supabaseId) async {
        if (supabaseId == null) return;
        final updated = currentTrack.value!.copyWith(supabaseId: supabaseId);
        await _db.updateTrack(updated);
        currentTrack.value = updated;
        TrackSyncService.to.startSession(updated);
      });
    } else {
      TrackSyncService.to.startSession(currentTrack.value!);
    }

    // GPS stream feliratkozás
    _positionSub = _source!.positionStream.listen(
      _onPosition,
      onError: (e) {
        AppLogger.e('positionStream', 'Stream hiba', error: e);
        FirebaseLogger.e('positionStream', 'Stream hiba', error: e);
        if (e is LocationServiceDisabledException ||
            e is PermissionDeniedException) {
          Get.snackbar('GPS hiba', e.toString(),
              backgroundColor: Colors.red, colorText: Colors.white);
          stopRecording();
        }
      },
    );
    AppLogger.e('track_started', 'name:$trackName');
    FirebaseLogger.event('track_started', {'name': trackName});

    // Időmérő
    _startElapsedTimer();

    isRecording.value = true;
    isPaused.value = false;
    _startWatchdog();
  }

  void pauseRecording() {
    if (!isRecording.value || isPaused.value) return;
    _positionSub?.pause();
    _elapsedTimer?.cancel();
    isPaused.value = true;
    FlutterForegroundTask.updateService(
      notificationTitle: 'Track szüneteltetve',
      notificationText: currentTrack.value?.name ?? '',
    );
  }

  void resumeRecording() {
    if (!isPaused.value) return;
    _positionSub?.resume();
    _startElapsedTimer();
    isPaused.value = false;
    FlutterForegroundTask.updateService(
      notificationTitle: 'Track rögzítése',
      notificationText: currentTrack.value?.name ?? '',
    );
  }

  Future<void> stopRecording() async {
    if (!isRecording.value) return;

    await _positionSub?.cancel();
    _positionSub = null;
    _elapsedTimer?.cancel();
    _elapsedTimer = null;

    // Track lezárása az adatbázisban
    final track = currentTrack.value;
    if (track?.id != null) {
      final finished = track!.copyWith(
        status: TrackStatus.finished,
        endTime: DateTime.now(),
        distanceMeters: _accumulatedDistance,
        pointCount: livePoints.length,
      );
      await _db.updateTrack(finished);
      await TrackSyncService.to.stopSession(finished);
      currentTrack.value = finished;
    }

    await FlutterForegroundTask.stopService();
    await _source?.dispose();
    _source = null;

    isRecording.value = false;
    isPaused.value = false;

    await loadSavedTracks();

    _watchdogTimer?.cancel();
    _watchdogTimer = null;
    gpsSignalLost.value = false;
  }

  Future<void> loadSavedTracks() async {
    savedTracks.value = await _db.listTracks();
  }

  Future<void> deleteTrack(int id) async {
    await _db.deleteTrack(id);
    overlayTrackIds.remove(id);
    _trackCoords.remove(id);
    await loadSavedTracks();
  }

  /// GPX export + rendszer megosztás dialóg.
  Future<void> exportTrack(Track track) async {
    try {
      final path = await _exporter.export(track);
      await Share.shareXFiles([XFile(path)],
          subject: 'Nyomvonal: ${track.name}.gpx');
    } catch (e) {
      Get.snackbar('Export hiba', e.toString(),
          backgroundColor: Colors.red, colorText: Colors.white);
    }
  }

  /// Visszatölt egy mentett track pontjait a térképre (megtekintés).
  Future<List<LatLng>> loadTrackPoints(int trackId) async {
    final pts = await _db.getLatLons(trackId);
    return pts.map((p) => LatLng(p.lat, p.lon)).toList();
  }

  // ── Belső logika ───────────────────────────────────────────────────────────

  Future<void> _onPosition(SourcePosition pos) async {
    try {
      if (isPaused.value) return;
      _lastPositionTime = DateTime.now();
      gpsSignalLost.value = false;
      final sw = Stopwatch()..start();

      final trackId = currentTrack.value?.id;
      if (trackId == null) return;

      final point = TrackPoint(
        trackId: trackId,
        latitude: pos.latitude,
        longitude: pos.longitude,
        altitude: pos.altitude,
        accuracy: pos.accuracy,
        speed: pos.speed,
        heading: pos.heading,
        timestamp: pos.timestamp,
      );

      // Távolság a legutóbbi ponttól
      double segmentDist = 0;
      if (_lastPoint != null) {
        segmentDist = haversineMeters(
          _lastPoint!.latitude,
          _lastPoint!.longitude,
          pos.latitude,
          pos.longitude,
        );
        // Szűrés: ugrásszerű változás (pl. GPS lock elvesztése) ignorálása
        if (segmentDist > 100) {
          AppLogger.w(
              '_onPosition',
              'GPS ugrás kiszűrve: ${segmentDist.toStringAsFixed(0)}m '
                  '(pts: ${livePoints.length})');
          _lastPoint = point; // reset — következő pont ettől mér
          return;
        }
      }

      _accumulatedDistance += segmentDist;
      sessionDistance.value = _accumulatedDistance;

      // Sebesség km/h-ban
      currentSpeedKmh.value = (pos.speed ?? 0) * 3.6;

      // Pont mentése
      await _db.addPoint(point, _accumulatedDistance);
      sw.stop();

      if (sw.elapsedMilliseconds > 300) {
        AppLogger.w(
            'on_Position',
            'Lassú addPoint: ${sw.elapsedMilliseconds}ms, '
                'pts: ${livePoints.length}');
        FirebaseLogger.w(
            'on_Position',
            'Lassú addPoint: ${sw.elapsedMilliseconds}ms, '
                'pts: ${livePoints.length}');
      }

      TrackSyncService.to.onNewPoint(point);

      _lastPoint = point;

      // UI frissítés
      livePoints.add(LatLng(pos.latitude, pos.longitude));

      // Értesítés frissítése
      if (livePoints.length % 10 == 0) {
        final dist = _formatDistance(_accumulatedDistance);
        FlutterForegroundTask.updateService(
          notificationTitle: 'Track rögzítése – $dist',
          notificationText: elapsedFormatted.value,
        );
      }
    } catch (e, stack) {
      AppLogger.e('_onPosition', 'pont feldolgozási hiba - track folytatódik',
          error: e, stack: stack);
    }
  }

  void _startElapsedTimer() {
    _elapsedTimer = Timer.periodic(const Duration(seconds: 1), (_) {
      if (_sessionStart == null) return;
      final d = DateTime.now().difference(_sessionStart!);
      final h = d.inHours.toString().padLeft(2, '0');
      final m = (d.inMinutes % 60).toString().padLeft(2, '0');
      final s = (d.inSeconds % 60).toString().padLeft(2, '0');
      elapsedFormatted.value = '$h:$m:$s';
    });
  }

  String _formatDistance(double m) {
    if (m < 1000) return '${m.toStringAsFixed(0)} m';
    return '${(m / 1000).toStringAsFixed(2)} km';
  }

  @override
  void onClose() {
    stopRecording();
    if (!isRecording.value) {
      _positionSub?.cancel();
      _elapsedTimer?.cancel();
    }
    super.onClose();
  }

  // Overlay ki/be kapcsolása — ha bekapcsol, betölti a koordinátákat
  void toggleTrackOverlay(int trackId) {
    if (overlayTrackIds.contains(trackId)) {
      overlayTrackIds.remove(trackId);
      overlayTrackIds.refresh();
    } else {
      overlayTrackIds.add(trackId);
      overlayTrackIds.refresh();
      _loadTrackCoords(trackId);
    }
  }

  /// Koordináták visszaadása — üres lista ha még nincs betöltve
  List<LatLng> getCoordsFor(int trackId) => _trackCoords[trackId] ?? [];

  Future<void> _loadTrackCoords(int trackId) async {
    if (_trackCoords.containsKey(trackId)) return;
    final pts = await _db.getLatLons(trackId);
    _trackCoords[trackId] = pts.map((p) => LatLng(p.lat, p.lon)).toList();
    overlayTrackIds.refresh(); // térkép frissítés
  }

  void _startWatchdog() {
    _watchdogTimer = Timer.periodic(
      const Duration(seconds: 5),
      (_) => _checkGpsTimeout(),
    );
  }

  void _checkGpsTimeout() {
    if (!isRecording.value || isPaused.value) return;
    final last = _lastPositionTime;
    if (last == null) return;

    final elapsedSec = DateTime.now().difference(last).inSeconds;
    if (elapsedSec < _gpsTimeoutSec) return;

    if (!gpsSignalLost.value) {
      gpsSignalLost.value = true;

      AppLogger.w(
          'watchdog',
          'GPS jel elveszett: ${elapsedSec}s óta nincs pozíció '
              '(pts: ${livePoints.length}, '
              'táv: ${_formatDistance(_accumulatedDistance)})');

      AppLogger.w(
          'gps_signal_lost',
          'elapsed_sec:  ${elapsedSec}'
              'point_count:  ${livePoints.length}'
              'distance_m:   ${_accumulatedDistance.round()}');

      // Foreground notification frissítése
      FlutterForegroundTask.updateService(
        notificationTitle: '⚠ GPS jel elveszett',
        notificationText: 'Track szünetel — folytatódik ha visszajön a jel',
      );

      // Snackbar ha előtérben van az app
      if (!isInBackground.value) {
        Get.snackbar(
          'GPS jel elveszett',
          'Valószínűleg alagút vagy lefedettség hiány.\n'
              'A rögzítés automatikusan folytatódik.',
          duration: const Duration(seconds: 6),
          backgroundColor: Colors.orange.shade700,
          colorText: Colors.white,
          icon: const Icon(Icons.gps_off, color: Colors.white),
          snackPosition: SnackPosition.TOP,
        );
      }
    }

    // Folyamatosan logol amíg nincs jel (percenként egyszer)
    if (elapsedSec % 60 == 0) {
      AppLogger.w('watchdog', 'GPS még mindig hiányzik: ${elapsedSec}s');
    }
  }
}
