import 'dart:convert';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart'; // For environment variables
import 'package:flutter/services.dart'; // For loading assets
import 'package:http/http.dart' as http;
import 'package:mapbox_gl/mapbox_gl.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:geolocator/geolocator.dart'; // For getting the user's location

import '../classes/alert.dart'; // Assuming this contains your error dialog code.
import '../variable/globals.dart' as globals;
import '../classes/MyDrawer.dart';

void main() async {
  await dotenv.load(fileName: ".env"); // Load .env file
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Directions Example',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const MapboxPages(title: 'Event Location', place: "Flutter"),
    );
  }
}

class MapboxPages extends StatefulWidget {
  const MapboxPages({Key? key, required this.title, required this.place})
      : super(key: key);

  final String title;
  final String place;

  @override
  State<MapboxPages> createState() => _MapboxPagesState();
}

class _MapboxPagesState extends State<MapboxPages> with ShowAlertDialog {
  late MapboxMapController mapController;
  late String mapboxAccessToken;
  List<LatLng> routeCoordinates = [];
  String selectedMode = 'driving';
  double longitude = 0.0;
  double latitude = 0.0;
  bool isLoading = true;
  late LatLng userPosition;
  bool isUserPositionInitialized = false;
  Line? currentRouteLine;

  @override
  void initState() {
    super.initState();
    _getUserLocation();
  }

  void _initToken() {
    mapboxAccessToken = dotenv.env['MAPBOX_ACCESS_TOKEN'] ?? '';
    if (mapboxAccessToken.isEmpty) {
      showAlertDialog(
          context, "Erreur Mapbox", "Mapbox Access Token is not available.");
    }
  }

  Future<void> _getEventInfo() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    var accessToken = prefs.getString("access_token") ?? "";

    if (accessToken.isNotEmpty) {
      var urlGet = Uri.parse("${globals.api}/events/${widget.title}");

      var responseGet = await http.get(urlGet,
          headers: {HttpHeaders.cookieHeader: 'access_token=${accessToken}'});
      if (responseGet.statusCode == 200) {
        var events = jsonDecode(utf8.decode(responseGet.bodyBytes));
        latitude = events["latitude"];
        longitude = events["longitude"];

        setState(() {
          isLoading = false;
        });
      } else {
        _handleErrorResponse(responseGet.statusCode);
      }
    } else {
      showAlertDialog(context, "Erreur serveur", "Invalid cache.");
    }
  }

  void _handleErrorResponse(int statusCode) {
    String text;
    switch (statusCode) {
      case 400:
        text = "Bad Request.";
        break;
      case 406:
        text = "Incorrect Password.";
        break;
      case 404:
        text = "User Not Found.";
        break;
      case 403:
        text = "Action not permitted.";
        break;
      case 410:
        text = "Invalid Token.";
        break;
      case 500:
        text = "Internal Server Error.";
        break;
      default:
        text = "Unknown error.";
    }
    showAlertDialog(context, "Erreur serveur", text);
  }

  Future<void> _getUserLocation() async {
    try {
      bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
      if (!serviceEnabled) {
        showAlertDialog(
            context, "Erreur de service", "Location services are disabled.");
        return;
      }

      LocationPermission permission = await Geolocator.checkPermission();
      if (permission == LocationPermission.denied) {
        permission = await Geolocator.requestPermission();
        if (permission == LocationPermission.denied) {
          showAlertDialog(context, "Erreur de permission",
              "Location permissions are denied.");
          return;
        }
      }

      if (permission == LocationPermission.deniedForever) {
        showAlertDialog(context, "Erreur de permission",
            "Location permissions are permanently denied. Enable them in settings.");
        return;
      }
      const LocationSettings locationSettings = LocationSettings(
          accuracy: LocationAccuracy.medium, timeLimit: Duration(seconds: 5));
      Position? position;
      try {
        position = await Geolocator.getCurrentPosition(
            locationSettings: locationSettings);
      } on LocationServiceDisabledException {
        // Handle location services disabled
        print('Location services are disabled.');
        position = await Geolocator.getLastKnownPosition();
        if (position == null) {
          print('No last known position available.');
        }
      } catch (e) {
        // Handle other errors
        print('Failed to get location: $e');
        position = await Geolocator.getLastKnownPosition();
        if (position == null) {
          print('No last known position available.');
        }
      }
      if (position != null) {
        setState(() {
          userPosition = LatLng(position!.latitude, position!.longitude);
          isUserPositionInitialized = true;
        });
      }
      _initToken();
      _getEventInfo();
    } catch (e) {
      showAlertDialog(context, "Erreur geo", "Failed to get user location: $e");
    }
  }

  Future<void> _fetchRoute(
      LatLng origin, LatLng destination, String mode) async {
    final url = Uri.parse(
      'https://api.mapbox.com/directions/v5/mapbox/$mode/${origin.longitude},${origin.latitude};${destination.longitude},${destination.latitude}?geometries=geojson&access_token=$mapboxAccessToken',
    );

    final response = await http.get(url);
    if (response.statusCode == 200) {
      final data = jsonDecode(response.body);
      final geometry = data['routes'][0]['geometry']['coordinates'];
      setState(() {
        routeCoordinates = geometry.map<LatLng>((coord) {
          return LatLng(coord[1], coord[0]);
        }).toList();
      });
    } else {
      showAlertDialog(context, "Erreur serveur",
          "Failed to fetch the route: ${response.statusCode}");
    }
  }

  // Called when the map is created
  void _onStyleLoaded() async {
    // Log the map controller and coordinates
    print("Mapbox controller initialized: $mapController");
    print("lat - long : $latitude - $longitude");

    // Check if the mapController is really initialized
    if (mapController != null) {
      try {
        // Ensure the coordinates are valid
        if (latitude != 0.0 && longitude != 0.0) {
          // Load marker image as Uint8List
          final userMarkerImage = await _loadMarkerImage('images/marker.png');

          // Register the image with Mapbox
          await mapController.addImage('event-marker', userMarkerImage);

          final symbolOptions = SymbolOptions(
            geometry: LatLng(latitude, longitude),
            iconImage: "event-marker", // Use the registered custom marker
            iconSize: 0.4, // Optional: Adjust size
          );

          // Debugging symbol options
          print("Adding symbol with options: $symbolOptions");

          // Add symbol to map
          mapController!.addSymbol(symbolOptions);
        } else {
          print("Error: Invalid coordinates, cannot add symbol.");
        }
      } catch (e) {
        // Handle any exception that occurs when adding the symbol
        print("Error when adding symbol: $e");
      }
    } else {
      print(
          "Error: MapboxMapController is null at the time of symbol addition");
    }
  }

  Future<void> _drawRouteAndMarkers() async {
    // Remove previous route line if it exists
    if (currentRouteLine != null) {
      await mapController.removeLine(currentRouteLine!);
      currentRouteLine = null;
    }
    if (!isUserPositionInitialized) {
      showAlertDialog(context, "Erreur de position",
          "User position is not yet initialized. Try again.");
      return;
    }

    if (mapController != null &&
        userPosition != null &&
        latitude != 0.0 &&
        longitude != 0.0) {
      final destination = LatLng(latitude, longitude);

      // Register the custom images
      // Add event marker

      final eventMarkerImage = await _loadMarkerImage('images/marker-red.png');

      // Register the image with Mapbox
      await mapController.addImage('user-marker', eventMarkerImage);
      await mapController.addSymbol(SymbolOptions(
        geometry: userPosition,
        iconImage: 'user-marker', // Custom icon for event
        iconSize: 0.2,
      ));

      // Fetch and draw route
      await _fetchRoute(userPosition, destination, selectedMode);

      if (routeCoordinates.isNotEmpty) {
        currentRouteLine = await mapController.addLine(
          LineOptions(
            geometry: routeCoordinates,
            lineColor: '#3b9ddd',
            lineWidth: 5.0,
            lineOpacity: 0.8,
          ),
        );

        _zoomToFitRoute(routeCoordinates);
      }
    } else {
      showAlertDialog(context, "Erreur de coordonée",
          "Invalid coordinates or user position.");
    }
  }

  void _zoomToFitRoute(List<LatLng> coordinates) {
    // Calculate the bounding box
    double minLat = coordinates.first.latitude;
    double maxLat = coordinates.first.latitude;
    double minLng = coordinates.first.longitude;
    double maxLng = coordinates.first.longitude;

    for (LatLng coord in coordinates) {
      if (coord.latitude < minLat) minLat = coord.latitude;
      if (coord.latitude > maxLat) maxLat = coord.latitude;
      if (coord.longitude < minLng) minLng = coord.longitude;
      if (coord.longitude > maxLng) maxLng = coord.longitude;
    }

    // Define the bounds
    LatLng southwest = LatLng(minLat, minLng);
    LatLng northeast = LatLng(maxLat, maxLng);

    mapController.moveCamera(
      CameraUpdate.newLatLngBounds(
        LatLngBounds(southwest: southwest, northeast: northeast),
        left: 50, // Padding on the left
        top: 50, // Padding on the top
        right: 50, // Padding on the right
        bottom: 50, // Padding on the bottom
      ),
    );
  }

  // Load image from assets
  Future<Uint8List> _loadMarkerImage(String assetPath) async {
    final ByteData data = await rootBundle.load(assetPath);
    return data.buffer.asUint8List();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.place),
        actions: [
          DropdownButton<String>(
            value: selectedMode,
            items: [
              DropdownMenuItem(
                value: 'walking',
                child: Row(
                  children: [
                    Icon(Icons.directions_walk, color: Colors.blue),
                    SizedBox(width: 8),
                    Text('Walking'),
                  ],
                ),
              ),
              DropdownMenuItem(
                value: 'cycling',
                child: Row(
                  children: [
                    Icon(Icons.directions_bike, color: Colors.green),
                    SizedBox(width: 8),
                    Text('Cycling'),
                  ],
                ),
              ),
              DropdownMenuItem(
                value: 'driving',
                child: Row(
                  children: [
                    Icon(Icons.directions_car, color: Colors.red),
                    SizedBox(width: 8),
                    Text('Driving'),
                  ],
                ),
              ),
            ],
            onChanged: (mode) {
              setState(() {
                selectedMode = mode!;
              });
            },
          )
        ],
      ),
      drawer: MyDrawer(),
      body: Stack(
        children: [
          isLoading
              ? Center(child: CircularProgressIndicator())
              : MapboxMap(
                  accessToken: mapboxAccessToken,
                  onMapCreated: (controller) {
                    mapController = controller;
                  },
                  onStyleLoadedCallback: _onStyleLoaded,
                  initialCameraPosition: CameraPosition(
                    target: LatLng(latitude, longitude),
                    zoom: 14.0,
                  ),
                ),
          Positioned(
            bottom: 20,
            right: 20,
            child: FloatingActionButton(
              onPressed: _drawRouteAndMarkers,
              child: Icon(Icons.directions),
              tooltip: 'Get Directions and Markers',
            ),
          ),
        ],
      ),
    );
  }
}