import 'dart:async';
import 'dart:convert';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:intl/intl.dart';
import 'WebSocketManager.dart';
import 'SharedpreferencesService.dart';

class BackgroundLocationService {
  static bool? _lastNetworkStatus; // Track last network status
  static DateTime? _lastNotificationTime; // Track last notification time
  static const Duration _notificationDebounceDuration = Duration(seconds: 30);
  static const String customChannelId = 'GEN ERP flutter';
  static const String customChannelName = 'GEN ERP flutter';
  static const String customChannelDescription = 'GEN ERP flutter';
  static const int notificationId = 0;
  static Timer? _locationTimer;
  static final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
  FlutterLocalNotificationsPlugin();
  static StreamSubscription<Position>? _positionStream;

  WebSocketManager webSocketManager = WebSocketManager(
    onConnectSuccess: () {},
    onMessage: (message) {},
    onClose: () {},
    onConnectFailed: () {},
  );

  String? empId;
  String? sessionId;

  static Future<void> init() async {
    try {
      const InitializationSettings initializationSettings =
      InitializationSettings(
        android: AndroidInitializationSettings('@mipmap/ic_launcher'),
        iOS: DarwinInitializationSettings(
          requestAlertPermission: false,
          requestBadgePermission: false,
          requestSoundPermission: false,
        ),
      );
      await flutterLocalNotificationsPlugin.initialize(initializationSettings);

      const AndroidNotificationChannel androidChannel = AndroidNotificationChannel(
        customChannelId,
        customChannelName,
        description: customChannelDescription,
        importance: Importance.max,
        playSound: false,
      );
      await flutterLocalNotificationsPlugin
          .resolvePlatformSpecificImplementation<
          AndroidFlutterLocalNotificationsPlugin>()
          ?.createNotificationChannel(androidChannel);
    } catch (e) {
      print("Error initializing notifications: $e");
    }
  }

  static Future<void> checkAndRequestLocationPermissions(BuildContext context) async {
    try {
      bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
      if (!serviceEnabled) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text("Please enable GPS")),
        );
        await Geolocator.openLocationSettings();
        return;
      }

      LocationPermission permission = await Geolocator.checkPermission();
      if (permission == LocationPermission.denied) {
        permission = await Geolocator.requestPermission();
        if (permission == LocationPermission.denied) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text("Location permission denied")),
          );
          return;
        }
      }

      if (permission == LocationPermission.deniedForever) {
        ScaffoldMessenger.of(context).showSnackBar(
          SnackBar(content: Text("Location permission permanently denied")),
        );
        await Geolocator.openAppSettings();
        return;
      }
    } catch (e) {
      print("Error requesting location permissions: $e");
    }
  }

  static Future<void> showNotification(String title, String message) async {
    const AndroidNotificationDetails androidPlatformChannelSpecifics =
    AndroidNotificationDetails(
      customChannelId,
      customChannelName,
      importance: Importance.defaultImportance,
      ticker: 'ticker',
      ongoing: true,
      playSound: false,
    );

    const DarwinNotificationDetails darwinNotificationDetails =
    DarwinNotificationDetails(presentSound: false);

    const NotificationDetails platformChannelSpecifics = NotificationDetails(
      android: androidPlatformChannelSpecifics,
      iOS: darwinNotificationDetails,
    );

    final pendingNotifications =
    await flutterLocalNotificationsPlugin.pendingNotificationRequests();
    final notificationAlreadyExists =
    pendingNotifications.any((notification) => notification.id == notificationId);

    await flutterLocalNotificationsPlugin.show(
      notificationId,
      title,
      message,
      platformChannelSpecifics,
    );
  }

  static Future<void> hideNotification() async {
    await flutterLocalNotificationsPlugin.cancel(notificationId);
  }

  void initWebSocket() {
    Future.delayed(Duration.zero, () {
      webSocketManager.connect();
    });
  }

  static Future<bool> checkGpsStatus() async {
    return await Geolocator.isLocationServiceEnabled();
  }

  static Future<bool> checkNetworkStatus() async {
    var connectivityResult = await Connectivity().checkConnectivity();
    return connectivityResult != ConnectivityResult.none;
  }

  static Future<void> startLocationService(BuildContext context) async {
    await init();
    bool isGpsEnabled = await checkGpsStatus();
    bool isNetworkAvailable = await checkNetworkStatus();

    if (isGpsEnabled && isNetworkAvailable) {
      await checkAndRequestLocationPermissions(context);
      showNotification("GEN ERP", "You're Online!");
      _positionStream?.cancel(); // Cancel any existing stream
      _positionStream = Geolocator.getPositionStream(
        locationSettings: LocationSettings(
          accuracy: LocationAccuracy.high,
          distanceFilter: 50,
        ),
      ).listen((Position position) async {
        final location = Location(
          latitude: position.latitude,
          longitude: position.longitude,
          altitude: position.altitude,
          accuracy: position.accuracy,
          bearing: position.heading,
          speed: position.speed,
          time: position.timestamp.millisecondsSinceEpoch.toDouble(),
          isMock: position.isMocked,
        );
        await handleLocationUpdate(location, context);
      }, onError: (e) {
        print("Location stream error: $e");
        showNotification("GEN ERP", "Location error: $e");
      });
    } else {
      if (!isGpsEnabled) {
        showNotification("GEN ERP", "You're Offline! Check your GPS connection.");
        await checkAndRequestLocationPermissions(context);
      }
      if (!isNetworkAvailable) {
        showNotification("GEN ERP", "You're Offline! Check your network connection.");
      }
    }

    _locationTimer?.cancel();
    _locationTimer = Timer.periodic(Duration(seconds: 30), (timer) async {
      if (await checkGpsStatus() && await checkNetworkStatus()) {
        final position = await Geolocator.getCurrentPosition(
          desiredAccuracy: LocationAccuracy.high,
        );
        final location = Location(
          latitude: position.latitude,
          longitude: position.longitude,
          altitude: position.altitude,
          accuracy: position.accuracy,
          bearing: position.heading,
          speed: position.speed,
          time: position.timestamp.millisecondsSinceEpoch.toDouble(),
          isMock: position.isMocked,
        );
        await handleLocationUpdate(location, context);
      }
    });
  }

  static Future<void> stopLocationService() async {
    print("Background location service stopped");
    await hideNotification();
    _locationTimer?.cancel();
    _positionStream?.cancel();
  }

  static Future<bool> isServiceRunning() async {
    return _positionStream != null && _positionStream!.isPaused == false;
  }

  Future<Location> getCurrentLocation(BuildContext context) async {
    empId = await SharedpreferencesService().getString("UserId");
    sessionId = await SharedpreferencesService().getString("Session_id");
    saveLastLocationTime();

    try {
      final position = await Geolocator.getCurrentPosition(
        desiredAccuracy: LocationAccuracy.high,
      );
      return Location(
        latitude: position.latitude,
        longitude: position.longitude,
        altitude: position.altitude,
        accuracy: position.accuracy,
        bearing: position.heading,
        speed: position.speed,
        time: position.timestamp.millisecondsSinceEpoch.toDouble(),
        isMock: position.isMocked,
      );
    } catch (e) {
      print("Error getting current location: $e");
      showNotification("GEN ERP", "Error getting location: $e");
      rethrow;
    }
  }

  static Future<void> handleLocationUpdate(Location location, BuildContext context) async {
    final empId = await SharedpreferencesService().getString("UserId");
    final sessionId = await SharedpreferencesService().getString("Session_id");
    final webSocketManager = WebSocketManager(
      onConnectSuccess: () {},
      onMessage: (message) {},
      onClose: () {},
      onConnectFailed: () {},
    );
    bool isNetworkAvailable = await webSocketManager.isNetworkAvailable();
    DateTime now = DateTime.now();

    // Check if enough time has passed since the last notification (debouncing)
    if (_lastNotificationTime != null &&
        now.difference(_lastNotificationTime!).inSeconds < _notificationDebounceDuration.inSeconds) {
      return; // Skip notification if within debounce duration
    }

    if (_lastNetworkStatus != isNetworkAvailable) {
      if (isNetworkAvailable) {
        webSocketManager.sendMessage(jsonEncode({
          "command": "server_request",
          "route": "attendenece_live_location_update",
          "session_id": sessionId,
          "ref_data": {
            "session_id": sessionId,
            "location": "${location.latitude},${location.longitude}",
            "speed": location.speed,
            "altitude": location.altitude,
            "direction": location.bearing,
            "direction_accuracy": 0.0,
            // Geolocator doesn't provide this directly
            "altitude_accuracy": 0.0,
            // Geolocator doesn't provide this directly
            "speed_accuracy": 0.0,
            // Geolocator doesn't provide this directly
            "location_accuracy": location.accuracy,
            "location_provider": "",
          }
        }));
        showNotification("GEN ERP", "You're Online!");
      }else {
        showNotification("GEN ERP", "You're Offline!");
      }
      _lastNetworkStatus = isNetworkAvailable;
      _lastNotificationTime = now;
    }

    saveLastLocationTime();
  }
}

class Location {
  double? latitude;
  double? longitude;
  double? altitude;
  double? bearing;
  double? accuracy;
  double? speed;
  double? time;
  bool? isMock;

  Location({
    required this.latitude,
    required this.longitude,
    required this.altitude,
    required this.accuracy,
    required this.bearing,
    required this.speed,
    required this.time,
    required this.isMock,
  });

  Map<String, dynamic> toMap() {
    return {
      'latitude': latitude,
      'longitude': longitude,
      'altitude': altitude,
      'bearing': bearing,
      'accuracy': accuracy,
      'speed': speed,
      'time': time,
      'is_mock': isMock,
    };
  }
}

void saveLastLocationTime() {
  var currentTime = DateTime.now();
  var formatter = DateFormat('HH:mm:ss').format(currentTime);
  SharedpreferencesService().saveString("lastLocationTime", formatter);
}