import 'dart:convert';
import 'dart:math';

import 'package:flutter/cupertino.dart';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart'; // Import for WidgetsBinding
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

import '../../constant/links.dart';
import '../functions/crud.dart';
import '../functions/location_controller.dart';

class RideAvailableController extends GetxController {
  bool isLoading = false;
  // FIX 1: Initialize the map with a default structure.
  // This prevents `rideAvailableMap['message']` from ever being null in the UI.
  Map rideAvailableMap = {'message': []};
  late LatLng southwest;
  late LatLng northeast;

  LatLngBounds calculateBounds(double lat, double lng, double radiusInMeters) {
    const double earthRadius = 6378137.0; // Earth's radius in meters

    double latDelta = (radiusInMeters / earthRadius) * (180 / pi);
    double lngDelta =
        (radiusInMeters / (earthRadius * cos(pi * lat / 180))) * (180 / pi);

    double minLat = lat - latDelta;
    double maxLat = lat + latDelta;
    double minLng = lng - lngDelta;
    double maxLng = lng + lngDelta;

    minLat = max(-90.0, minLat);
    maxLat = min(90.0, maxLat);

    minLng = (minLng + 180) % 360 - 180;
    maxLng = (maxLng + 180) % 360 - 180;

    if (minLng > maxLng) {
      double temp = minLng;
      minLng = maxLng;
      maxLng = temp;
    }

    return LatLngBounds(
      southwest: LatLng(minLat, minLng),
      northeast: LatLng(maxLat, maxLng),
    );
  }

  double calculateDistance(String startLocation) {
    List<String> startLocationParts = startLocation.split(',');
    double startLatitude = double.parse(startLocationParts[0]);
    double startLongitude = double.parse(startLocationParts[1]);

    double currentLatitude = Get.find<LocationController>().myLocation.latitude;
    double currentLongitude =
        Get.find<LocationController>().myLocation.longitude;

    return Geolocator.distanceBetween(
      currentLatitude,
      currentLongitude,
      startLatitude,
      startLongitude,
    );
  }

  // A helper function to safely show dialogs after the build cycle is complete.
  void _showDialogAfterBuild(Widget dialog) {
    // FIX 2: Use addPostFrameCallback to ensure dialogs are shown after the build process.
    // This resolves the "visitChildElements() called during build" error.
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Get.dialog(
        dialog,
        barrierDismissible: true,
        transitionCurve: Curves.easeOutBack,
        transitionDuration: const Duration(milliseconds: 200),
      );
    });
  }

  Future<void> getRideAvailable() async {
    try {
      isLoading = true;
      update();

      LatLngBounds bounds = calculateBounds(
          Get.find<LocationController>().myLocation.latitude,
          Get.find<LocationController>().myLocation.longitude,
          4000);

      var payload = {
        'southwestLat': bounds.southwest.latitude.toString(),
        'southwestLon': bounds.southwest.longitude.toString(),
        'northeastLat': bounds.northeast.latitude.toString(),
        'northeastLon': bounds.northeast.longitude.toString(),
      };

      var res =
          await CRUD().get(link: AppLink.getRideWaiting, payload: payload);

      isLoading = false; // Request is complete, stop loading indicator.

      if (res != 'failure') {
        final decodedResponse = jsonDecode(res);
        // Check for valid response structure
        if (decodedResponse is Map &&
            decodedResponse.containsKey('message') &&
            decodedResponse['message'] is List) {
          rideAvailableMap = decodedResponse;
          // If the list of rides is empty, show the "No Rides" dialog
          if ((rideAvailableMap['message'] as List).isEmpty) {
            _showDialogAfterBuild(_buildNoRidesDialog());
          }
        } else {
          // If response format is unexpected, treat as no rides and show dialog
          rideAvailableMap = {'message': []};
          _showDialogAfterBuild(_buildNoRidesDialog());
        }
        update(); // Update the UI with new data (or empty list)
      } else {
        // This block now handles network/server errors correctly
        HapticFeedback.lightImpact();
        update(); // Update UI to turn off loader
        // Show a proper error dialog instead of "No Rides"
        _showDialogAfterBuild(
            _buildErrorDialog("Failed to fetch rides. Please try again.".tr));
      }
    } catch (e) {
      isLoading = false;
      update();
      // This catches other exceptions like JSON parsing errors
      _showDialogAfterBuild(
          _buildErrorDialog("An unexpected error occurred.".tr));
    }
  }

  // Extracted dialogs into builder methods for cleanliness.
  Widget _buildNoRidesDialog() {
    return CupertinoAlertDialog(
      title: Column(
        mainAxisSize: MainAxisSize.min,
        children: [
          const Icon(
            CupertinoIcons.car,
            size: 44,
            color: CupertinoColors.systemGrey,
          ),
          const SizedBox(height: 12),
          Text(
            "No Rides Available".tr,
            style: const TextStyle(fontSize: 17, fontWeight: FontWeight.w600),
          ),
        ],
      ),
      content: Padding(
        padding: const EdgeInsets.only(top: 8),
        child: Text(
          "Please check back later for available rides.".tr,
          style:
              const TextStyle(fontSize: 13, color: CupertinoColors.systemGrey),
        ),
      ),
      actions: [
        CupertinoDialogAction(
          onPressed: () {
            Get.back(); // Close dialog
            Get.back(); // Go back from AvailableRidesPage
          },
          child: Text('OK'.tr),
        ),
      ],
    );
  }

  Widget _buildErrorDialog(String error) {
    // You can log the error here for debugging.
    // print("Error fetching rides: $error");
    return CupertinoAlertDialog(
      title: const Icon(
        CupertinoIcons.exclamationmark_triangle_fill,
        color: CupertinoColors.systemRed,
        size: 44,
      ),
      content: Text(
        error, // Display the specific error message passed to the function
        style: const TextStyle(fontSize: 14),
      ),
      actions: [
        CupertinoDialogAction(
          onPressed: () {
            Get.back(); // Close dialog
            Get.back(); // Go back from AvailableRidesPage
          },
          child: Text('OK'.tr),
        ),
      ],
    );
  }

  @override
  void onInit() {
    super.onInit();
    getRideAvailable();
  }
}
