import 'dart:async';
import 'dart:math';

import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';
import 'package:location/location.dart';
import 'package:sefer_driver/constant/table_names.dart';
import 'package:sefer_driver/controller/home/captin/map_driver_controller.dart';

import '../../constant/box_name.dart';
import '../../constant/links.dart';
import '../../main.dart';
import '../../print.dart';
import '../home/captin/home_captain_controller.dart';
import '../home/payment/captain_wallet_controller.dart';
import 'battery_status.dart';
import 'crud.dart';
import 'encrypt_decrypt.dart';

class LocationController extends GetxController {
  LocationData? _currentLocation;
  late Location location = Location();
  bool isLoading = false;
  late double heading = 0;
  late double previousTime = 0;
  late double latitude;
  late double totalDistance = 0;
  late double longitude;
  late DateTime time;
  late double speed = 0;
  bool isActive = false;
  late LatLng myLocation = LatLng(0, 0);
  String totalPoints = '0';
  LocationData? get currentLocation => _currentLocation;
  Timer? _locationTimer;

  LatLng? _lastSavedPosition;

  @override
  void onInit() async {
    super.onInit();
    location = Location();
    await location.changeSettings(
        accuracy: LocationAccuracy.high, interval: 5000, distanceFilter: 0);
    location.enableBackgroundMode(enable: true);
    await getLocation();
    await startLocationUpdates();

    totalPoints = Get.put(CaptainWalletController()).totalPoints.toString();
    isActive = Get.put(HomeCaptainController()).isActive;
  }

  String getLocationArea(double latitude, double longitude) {
    final locations = box.read(BoxName.locationName) ?? [];
    for (final location in locations) {
      final locationData = location as Map<String, dynamic>;
      final minLatitude =
          double.tryParse(locationData['min_latitude'].toString()) ?? 0.0;
      final maxLatitude =
          double.tryParse(locationData['max_latitude'].toString()) ?? 0.0;
      final minLongitude =
          double.tryParse(locationData['min_longitude'].toString()) ?? 0.0;
      final maxLongitude =
          double.tryParse(locationData['max_longitude'].toString()) ?? 0.0;

      if (latitude >= minLatitude &&
          latitude <= maxLatitude &&
          longitude >= minLongitude &&
          longitude <= maxLongitude) {
        box.write(BoxName.serverChosen, (locationData['server_link']));
        return locationData['name'];
      }
    }

    box.write(BoxName.serverChosen, AppLink.seferCairoServer);
    return 'Cairo';
  }

  int _insertCounter = 0;
  double? _lastSpeed;
  DateTime? _lastSpeedTime;

  Future<void> startLocationUpdates() async {
    if (box.read(BoxName.driverID) != null) {
      _locationTimer =
          Timer.periodic(const Duration(seconds: 5), (timer) async {
        try {
          await BatteryNotifier.checkBatteryAndNotify();
          totalPoints =
              Get.find<CaptainWalletController>().totalPoints.toString();
          isActive = Get.find<HomeCaptainController>().isActive;

          if (isActive && double.parse(totalPoints) > -30000) {
            await getLocation();
            if (myLocation.latitude == 0 && myLocation.longitude == 0) return;

            String area =
                getLocationArea(myLocation.latitude, myLocation.longitude);

            final payload = {
              'driver_id': box.read(BoxName.driverID).toString(),
              'latitude': myLocation.latitude.toString(),
              'longitude': myLocation.longitude.toString(),
              'heading': heading.toString(),
              'speed': (speed * 3.6).toStringAsFixed(1),
              'distance': totalDistance.toStringAsFixed(2),
              'status': box.read(BoxName.statusDriverLocation) ?? 'off',
            };

            // ✅ تحديث للسيرفر
            await CRUD().post(
              link: '${AppLink.server}/ride/location/update.php',
              payload: payload,
            );

            // ✅ تخزين في SQLite فقط إذا الرحلة On + تحرك أكثر من 10 متر
            if ((box.read(BoxName.statusDriverLocation) ?? 'off') == 'on') {
              if (_lastSavedPosition == null ||
                  _calculateDistanceInMeters(_lastSavedPosition!, myLocation) >=
                      10) {
                double currentSpeed = speed; // m/s
                double? acceleration = _calculateAcceleration(currentSpeed);

                await sql.insertData({
                  'driver_id': box.read(BoxName.driverID).toString(),
                  'latitude': myLocation.latitude,
                  'longitude': myLocation.longitude,
                  'acceleration': acceleration ?? 0.0,
                  'created_at': DateTime.now().toIso8601String(),
                  'updated_at': DateTime.now().toIso8601String(),
                }, TableName.behavior);

                _lastSavedPosition = myLocation;
              }
            }

            // ✅ إدخال للسيرفر كل دقيقة
            _insertCounter++;
            // Log.print('_insertCounter: ${_insertCounter}');
            if (_insertCounter == 12) {
              _insertCounter = 0;
              await CRUD().post(
                link: '${AppLink.server}/ride/location/add.php',
                payload: payload,
              );
            }

            // ✅ تحديث الكاميرا
            Get.find<HomeCaptainController>()
                .mapHomeCaptainController
                ?.animateCamera(
                  CameraUpdate.newCameraPosition(
                    CameraPosition(
                      bearing: Get.find<LocationController>().heading,
                      target: myLocation,
                      zoom: 17, // Adjust zoom level as needed
                    ),
                  ),
                );
            // if (Get.isRegistered()) {
            //   Get.find<MapDriverController>().mapController?.animateCamera(
            //         CameraUpdate.newCameraPosition(
            //           CameraPosition(
            //             bearing: Get.find<LocationController>().heading,
            //             target: myLocation,
            //             zoom: 17, // Adjust zoom level as needed
            //           ),
            //         ),
            //       );
            // }
          }
        } catch (e) {
          print('Location update error: $e');
        }
      });
    }
  }

  void stopLocationUpdates() {
    _locationTimer?.cancel();
  }

  Future<void> getLocation() async {
    bool serviceEnabled = await location.serviceEnabled();
    if (!serviceEnabled) {
      serviceEnabled = await location.requestService();
      if (!serviceEnabled) return;
    }

    PermissionStatus permissionGranted = await location.hasPermission();
    if (permissionGranted == PermissionStatus.denied) {
      permissionGranted = await location.requestPermission();
      if (permissionGranted != PermissionStatus.granted) return;
    }

    Future.delayed(Duration(milliseconds: 500), () async {
      try {
        LocationData _locationData = await location.getLocation();
        if (_locationData.latitude != null && _locationData.longitude != null) {
          myLocation =
              LatLng(_locationData.latitude!, _locationData.longitude!);
        } else {
          myLocation = LatLng(0, 0);
        }

        speed = _locationData.speed ?? 0;
        heading = _locationData.heading ?? 0;

        update();
      } catch (e) {
        print("Error getting location: $e");
      }
    });
  }

  double _calculateDistanceInMeters(LatLng start, LatLng end) {
    const p = 0.017453292519943295;
    final a = 0.5 -
        cos((end.latitude - start.latitude) * p) / 2 +
        cos(start.latitude * p) *
            cos(end.latitude * p) *
            (1 - cos((end.longitude - start.longitude) * p)) /
            2;
    return 12742 * 1000 * asin(sqrt(a)); // meters
  }

  double? _calculateAcceleration(double currentSpeed) {
    final now = DateTime.now();
    if (_lastSpeed != null && _lastSpeedTime != null) {
      final deltaTime =
          now.difference(_lastSpeedTime!).inMilliseconds / 1000.0; // seconds
      if (deltaTime > 0) {
        final acceleration = (currentSpeed - _lastSpeed!) / deltaTime;
        _lastSpeed = currentSpeed;
        _lastSpeedTime = now;
        return double.parse(acceleration.toStringAsFixed(2));
      }
    }

    _lastSpeed = currentSpeed;
    _lastSpeedTime = now;
    return null;
  }
}
