import 'package:flutter/foundation.dart';
import 'package:geocoding/geocoding.dart';
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:logger/logger.dart';
import 'package:sigap/src/utils/exceptions/exceptions.dart';

class LocationService extends GetxService {
  static LocationService get instance => Get.find<LocationService>();

  final RxBool isLocationServiceEnabled = false.obs;
  final RxBool isPermissionGranted = false.obs;
  final Rx<Position?> currentPosition = Rx<Position?>(null);
  final RxString currentCity = ''.obs;
  final RxBool isMockedLocation = false.obs;

  // Jember's center coordinate (approximate)
  static const double jemberLatitude = -8.168333;
  static const double jemberLongitude = 113.702778;

  // Max distance from Jember in meters (approximately 30km radius)
  static const double maxDistanceFromJember = 30000;

  late LocationSettings locationSettings;

  // Add count for spoofing attempts
  final RxInt spoofingAttempts = 0.obs;

  // Add more detailed location status
  final RxString locationStatus = 'unknown'.obs;

  // Add last check timestamp
  final Rx<DateTime?> lastLocationCheckTime = Rx<DateTime?>(null);

  LocationService() {
    if (defaultTargetPlatform == TargetPlatform.android) {
      locationSettings = AndroidSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 100,
        forceLocationManager: true,
        intervalDuration: const Duration(seconds: 10),
        //(Optional) Set foreground notification config to keep the app alive
        //when going to the background
        foregroundNotificationConfig: const ForegroundNotificationConfig(
          notificationText:
              "Sigap app will continue to receive your location even when you aren't using it",
          notificationTitle: "Running in Background",
          enableWakeLock: true,
        ),
      );
    } else if (defaultTargetPlatform == TargetPlatform.iOS ||
        defaultTargetPlatform == TargetPlatform.macOS) {
      locationSettings = AppleSettings(
        accuracy: LocationAccuracy.high,
        activityType: ActivityType.fitness,
        distanceFilter: 100,
        pauseLocationUpdatesAutomatically: true,
        // Only set to true if our app will be started up in the background.
        showBackgroundLocationIndicator: false,
      );
    } else if (kIsWeb) {
      locationSettings = WebSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 100,
        maximumAge: Duration(minutes: 5),
      );
    } else {
      locationSettings = LocationSettings(
        accuracy: LocationAccuracy.high,
        distanceFilter: 100,
      );
    }
  }

  // Initialize the service
  Future<LocationService> init() async {
    await _checkLocationService();
    return this;
  }

  // Check if location service is enabled and permission is granted
  Future<bool> _checkLocationService() async {
    try {
      // Check if location service is enabled
      isLocationServiceEnabled.value =
          await Geolocator.isLocationServiceEnabled();
      if (!isLocationServiceEnabled.value) {
        return false;
      }

      // Check location permission
      var permission = await Geolocator.checkPermission();

      if (permission == LocationPermission.denied) {
        permission = await Geolocator.requestPermission();
        if (permission == LocationPermission.denied) {
          isPermissionGranted.value = false;
          return false;
        }
      }

      if (permission == LocationPermission.deniedForever) {
        isPermissionGranted.value = false;
        return false;
      }

      isPermissionGranted.value = true;
      return true;
    } catch (e) {
      isLocationServiceEnabled.value = false;
      isPermissionGranted.value = false;
      return false;
    }
  }

  // Request location permission
  Future<bool> requestLocationPermission() async {
    try {
      var permission = await Geolocator.requestPermission();
      isPermissionGranted.value =
          permission == LocationPermission.always ||
          permission == LocationPermission.whileInUse;
      return isPermissionGranted.value;
    } catch (e) {
      isPermissionGranted.value = false;
      return false;
    }
  }

  // Get current position
  Future<Position?> getCurrentPosition() async {
    try {
      if (!isPermissionGranted.value) {
        bool hasPermission = await _checkLocationService();
        if (!hasPermission) return null;
      }

      currentPosition.value = await Geolocator.getCurrentPosition(
        locationSettings: locationSettings,
      );

      // Check if location is mocked
      isMockedLocation.value = currentPosition.value?.isMocked ?? false;

      // Get city name from coordinates
      if (currentPosition.value != null) {

        await _updateCityName();
      }

      return currentPosition.value;
    } catch (e) {
      throw TExceptions('Failed to get location: ${e.toString()}');
    }
  }

  Future<Position?> getLastKnownPosition() async {
    try {
      if (!isPermissionGranted.value) {
        bool hasPermission = await _checkLocationService();
        if (!hasPermission) return null;
      }

      currentPosition.value = await Geolocator.getLastKnownPosition();

      // Check if location is mocked
      isMockedLocation.value = currentPosition.value?.isMocked ?? false;

      // Get city name from coordinates
      if (currentPosition.value != null) {
        await _updateCityName();
      }

      return currentPosition.value;
    } catch (e) {
      throw TExceptions('Failed to get last known location: ${e.toString()}');
    }
  }

  // Start listening to location updates
  void startListeningToLocationUpdates() {
    Geolocator.getPositionStream(locationSettings: locationSettings).listen((
      Position position,
    ) {
      currentPosition.value = position;

      // Check if location is mocked
      isMockedLocation.value = position.isMocked;

      // Update city name based on new position
      _updateCityName();
    });
  }

  // Update the city name based on current position
  Future<void> _updateCityName() async {
    if (currentPosition.value == null) return;

    try {
      List<Placemark> placemarks = await placemarkFromCoordinates(
        currentPosition.value!.latitude,
        currentPosition.value!.longitude,
      );

      if (placemarks.isNotEmpty) {
        currentCity.value = placemarks.first.locality ?? '';
      }

      Logger().i('Current city: ${currentCity.value}');
    } catch (e) {
      currentCity.value = '';
    }
  }

  // Check if the user is in Jember
  bool isInJember() {
    if (currentPosition.value == null) return false;

    // First check by city name if available
    if (currentCity.value.toLowerCase().contains('jember')) {
      return true;
    }

    // Then check by distance from Jember's center
    double distanceInMeters = Geolocator.distanceBetween(
      currentPosition.value!.latitude,
      currentPosition.value!.longitude,
      jemberLatitude,
      jemberLongitude,
    );

    return distanceInMeters <= maxDistanceFromJember;
  }

  // Check if location is valid for registration or panic button
  Future<bool> isLocationValidForFeature() async {
    await getCurrentPosition();

    if (currentPosition.value == null) return false;

    // Check if location is mocked
    if (isMockedLocation.value) return false;

    // Check if in Jember
    return isInJember();
  }

  // Enhanced location validation for panic button
  Future<Map<String, dynamic>> validateLocationForPanicButton() async {
    lastLocationCheckTime.value = DateTime.now();

    try {
      // First check if location service is available
      if (!isLocationServiceEnabled.value) {
        bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
        if (!serviceEnabled) {
          locationStatus.value = 'service_disabled';
          return {
            'valid': false,
            'reason': 'location_service_disabled',
            'message': 'Location service is disabled. Please enable GPS.',
          };
        }
        isLocationServiceEnabled.value = true;
      }

      // Then check permission
      if (!isPermissionGranted.value) {
        bool hasPermission = await _checkLocationService();
        if (!hasPermission) {
          locationStatus.value = 'permission_denied';
          return {
            'valid': false,
            'reason': 'location_permission_denied',
            'message':
                'Location permission denied. Please grant permission in settings.',
          };
        }
      }

      // Get current position with timeout
      Position? position;
      try {
        position = await Geolocator.getCurrentPosition(
          locationSettings: locationSettings,
          timeLimit: const Duration(seconds: 10),
        );
        currentPosition.value = position;
      } catch (e) {
        locationStatus.value = 'timeout';
        return {
          'valid': false,
          'reason': 'location_timeout',
          'message': 'Could not determine your location. Please try again.',
        };
      }

      // Check for mock location
      if (position.isMocked) {
        isMockedLocation.value = true;
        spoofingAttempts.value++;
        locationStatus.value = 'mocked';

        return {
          'valid': false,
          'reason': 'mock_location',
          'message':
              'Mock location detected. The panic button requires your real location.',
          'spoofingAttempts': spoofingAttempts.value,
        };
      }

      // Update city name from coordinates
      await _updateCityName();

      // Check if in Jember
      if (!isInJember()) {
        locationStatus.value = 'outside_area';
        return {
          'valid': false,
          'reason': 'outside_jember',
          'message':
              'Your location is outside of Jember area. Panic button is only available within Jember.',
        };
      }

      // All checks passed
      locationStatus.value = 'valid';
      return {
        'valid': true,
        'city': currentCity.value,
        'position': {
          'latitude': position.latitude,
          'longitude': position.longitude,
          'accuracy': position.accuracy,
        },
      };
    } catch (e) {
      locationStatus.value = 'error';
      return {
        'valid': false,
        'reason': 'unknown_error',
        'message': 'An error occurred while checking location: ${e.toString()}',
      };
    }
  }

  // Get address from coordinates
  Future<String> getAddressFromCoordinates() async {
    if (currentPosition.value == null) {
      await getCurrentPosition();
      if (currentPosition.value == null) return '';
    }

    try {
      List<Placemark> placemarks = await placemarkFromCoordinates(
        currentPosition.value!.latitude,
        currentPosition.value!.longitude,
      );

      if (placemarks.isNotEmpty) {
        final placemark = placemarks.first;

        // Create a formatted address
        final List<String> addressComponents =
            [
              placemark.street ?? '',
              placemark.subLocality ?? '',
              placemark.locality ?? '',
              placemark.subAdministrativeArea ?? '',
              placemark.administrativeArea ?? '',
              placemark.postalCode ?? '',
            ].where((component) => component.isNotEmpty).toList();

        return addressComponents.join(', ');
      }

      return '';
    } catch (e) {
      return '';
    }
  }

  // Calculate distance between two points in kilometers
  double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
    return Geolocator.distanceBetween(lat1, lon1, lat2, lon2) / 1000;
  }

  // Reset spoofing attempts counter
  void resetSpoofingAttempts() {
    spoofingAttempts.value = 0;
  }

  // Check if location is mocked without getting new location
  bool isLocationMocked() {
    return currentPosition.value?.isMocked ?? false;
  }
}
