'use client';

import { useState, useCallback, useRef, useEffect, useMemo } from 'react';
import { Box, type SxProps, type Theme } from '@mui/material';
import { MyLocation as MyLocationIcon } from '@mui/icons-material';
import { GoogleMap, useJsApiLoader, DirectionsRenderer } from '@react-google-maps/api';
import { CircuitMap, RouteMarker, MarkerType, MapType } from '@/shared/types/Maps';
import MapMarker from './map-marker';
import { MapPolyline } from './map-polyline';
import { StatusColor } from '@/shared/sections/chauffeur/planingChauffeur/utils/schedule-utils';
import { MapPolylineDriver } from './map-polyline-driver';
import VehicleMarker from './vehicle-marker';
import { GeolocStatus } from '@/shared/types/geoloc';
import { predefinedVehiclePositions } from '@/shared/_mock/_geolocData';

const DEFAULT_CENTER = {
  lat: 43.2965,
  lng: 5.3798,
};

const DEFAULT_ZOOM = 18;
const VISIT_THRESHOLD = 10;
const VELOCITY = 0.000005;
const GOOGLE_MAPS_LIBRARIES: ("places" | "geometry")[] = ["places", "geometry"];

interface VehiclePositionsMap {
  [key: string]: {
    vehicleType: string;
    position: { lat: number; lng: number };
    rotation: number;
  };
}

interface GoogleMapsViewProps {
  sx?: SxProps<Theme>;
  height?: string;
  routes: CircuitMap[];
  Submodule: MapType;
  activeDepartement?: string;
  onDepartementChange?: (departement: string) => void;
  isGeoloc?: boolean;
  vehiclePositions?: VehiclePositionsMap;
  selectedVehicule?: string;
  selectedChauffeur?: string;
  onVehiclePositionsUpdate?: (positions: VehiclePositionsMap) => void;
  onDirectionsUpdate?: (responses: Record<string, google.maps.DirectionsResult>) => void;
  onMapLoaded?: () => void;
}

export default function GoogleMapsView({
  sx,
  height = 'calc(100vh - 240px)',
  routes,
  Submodule,
  activeDepartement,
  isGeoloc = false,
  vehiclePositions = {},
  selectedVehicule = '',
  selectedChauffeur = '',
  onDirectionsUpdate,
  onMapLoaded,
}: GoogleMapsViewProps) {
  const mapContainerStyle = useMemo(() => ({
    height,
    width: '100%',
  }), [height]);

  const { isLoaded } = useJsApiLoader({
    id: 'google-map-script',
    googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY!,
    libraries: GOOGLE_MAPS_LIBRARIES,
  });

  const [map, setMap] = useState<google.maps.Map | null>(null);
  const [selectedMarker, setSelectedMarker] = useState<MarkerType | null>(null);
  const [internalRoutes, setInternalRoutes] = useState<CircuitMap[]>([]);
  const [visitedUsers, setVisitedUsers] = useState<Set<string>>(new Set());
  const [directionsResults, setDirectionsResults] = useState<Record<string, google.maps.DirectionsResult>>({});
  const [detailedPaths, setDetailedPaths] = useState<Record<string, google.maps.LatLng[]>>({});
  const [internalVehiclePositions, setInternalVehiclePositions] = useState<VehiclePositionsMap>({});

  const mapRef = useRef<google.maps.Map | null>(null);
  const startTimeRef = useRef<number>(Date.now());
  const animationFrameRef = useRef<number | null>(null);

  const routesToDisplayRef = useRef<CircuitMap[]>([]);

  const isDriver = Submodule === MapType.DRIVER;
  const isPassenger = Submodule === MapType.PASSENGER;
  const isGeolocation = Submodule === MapType.GEOLOC;

  const directionsService = useMemo(() => {
    if (!isLoaded) return null;
    return new window.google.maps.DirectionsService();
  }, [isLoaded]);
  const defaultCenter = useMemo(() => {
    if (!activeDepartement || !isGeolocation) return DEFAULT_CENTER;

    const departements = [
      { code: '13', center: { lat: 43.2965, lng: 5.3698 } },
      { code: '83', center: { lat: 43.1257, lng: 5.9304 } },
      { code: '84', center: { lat: 43.9493, lng: 4.8055 } },
      { code: '04', center: { lat: 44.0925, lng: 6.2356 } }
    ];

    const dept = departements.find(d => d.code === activeDepartement);
    return dept?.center || DEFAULT_CENTER;
  }, [activeDepartement, isGeolocation]);

  const routesToDisplay = useMemo(() => {
    if (!isGeolocation) return routes;

    return routes.filter(route => {
      if (activeDepartement && route.departement !== activeDepartement) return false;
      if (selectedVehicule && route.vehiculeId !== selectedVehicule) return false;
      if (selectedChauffeur && route.nomChauffeur && route.prenomChauffeur) {
        const fullName = `${route.prenomChauffeur} ${route.nomChauffeur}`;
        if (fullName !== selectedChauffeur) return false;
      }
      return true;
    });
  }, [routes, activeDepartement, selectedVehicule, selectedChauffeur, isGeolocation]);

  useEffect(() => {
    routesToDisplayRef.current = routesToDisplay;
  }, [routesToDisplay]);

  useEffect(() => {
    if (!isLoaded || !isGeolocation) return;

    const processRoutes = async () => {
      const newDirectionsResults: Record<string, google.maps.DirectionsResult> = {};
      const newDetailedPaths: Record<string, google.maps.LatLng[]> = {};
       
      for (const route of routesToDisplay) {
        if (!route.id || !route.startPoint || !route.endPoint) continue;

        try {
          const waypoints = route.users?.map(user => ({
            location: new google.maps.LatLng(user.position.lat, user.position.lng),
            stopover: true
          })) || [];

          const result = await new Promise<google.maps.DirectionsResult>((resolve, reject) => {
            if (!directionsService) return;
            directionsService.route({
              origin: new google.maps.LatLng(route.startPoint?.position.lat || 0, route.startPoint?.position.lng || 0),
              destination: new google.maps.LatLng(route.endPoint?.position.lat || 0, route.endPoint?.position.lng || 0),
              waypoints: waypoints,
              travelMode: google.maps.TravelMode.DRIVING,
              optimizeWaypoints: false
            }, (result: google.maps.DirectionsResult | PromiseLike<google.maps.DirectionsResult>, status: google.maps.DirectionsStatus) => {
              if (status === google.maps.DirectionsStatus.OK && result) {
                resolve(result);
              } else {
                reject(status);
              }
            });
          });

          newDirectionsResults[route.id] = result;

          const path: google.maps.LatLng[] = [];
          result.routes[0].legs.forEach(leg => {
            leg.steps.forEach(step => {
              step.path.forEach(point => {
                path.push(point);
              });
            });
          });

          newDetailedPaths[route.id] = path;
        } catch (error) {
          // Silently handle error
        }
      }

      setDirectionsResults(newDirectionsResults);
      setDetailedPaths(newDetailedPaths);
      if (onDirectionsUpdate) {
        onDirectionsUpdate(newDirectionsResults);
      }
    };

    processRoutes();
  }, [isLoaded, isGeolocation, routesToDisplay, directionsService, onDirectionsUpdate]);

  useEffect(() => {
    if (!isLoaded || !isGeolocation || !isGeoloc) return;

    startTimeRef.current = Date.now();

    const updateVehiclePositions = () => {
      const currentTime = Date.now();
      const newPositions: VehiclePositionsMap = {};

      routesToDisplayRef.current.forEach((route) => {
        if (!route.id || route.status !== GeolocStatus.IN_PROGRESS) return;

        const path = detailedPaths[route.id];

        if (path && path.length > 1) {
          try {

            const pathLength = path.length;
            const totalDistance = path.reduce((total, point, index) => {
              if (index === 0) return 0;
              return total + google.maps.geometry.spherical.computeDistanceBetween(path[index - 1], point);
            }, 0);

            const elapsedTime = (currentTime - startTimeRef.current) / 1000;
            const distanceTraveled = (elapsedTime * VELOCITY * totalDistance) % totalDistance;

            let accumulatedDistance = 0;
            let segmentIndex = 0;

            for (let i = 1; i < pathLength; i++) {
              const segmentDistance = google.maps.geometry.spherical.computeDistanceBetween(path[i - 1], path[i]);
              if (accumulatedDistance + segmentDistance > distanceTraveled) {
                segmentIndex = i - 1;
                break;
              }
              accumulatedDistance += segmentDistance;
            }

            const segmentDistance = google.maps.geometry.spherical.computeDistanceBetween(
              path[segmentIndex],
              path[segmentIndex + 1]
            );
            const segmentProgress = segmentDistance > 0
              ? (distanceTraveled - accumulatedDistance) / segmentDistance
              : 0;

            const position = google.maps.geometry.spherical.interpolate(
              path[segmentIndex],
              path[segmentIndex + 1],
              segmentProgress
            );

            const heading = google.maps.geometry.spherical.computeHeading(
              path[segmentIndex],
              path[segmentIndex + 1]
            );

            newPositions[route.id] = {
              position: {
                lat: position.lat(),
                lng: position.lng(),
              },
              rotation: heading + 90,
              vehicleType: route.vehicule?.toLowerCase().includes('bus') ? 'bus' : 'car'
            };
          } catch (error) {
            // Silently handle error
          }
        } else {
          const routePositions = predefinedVehiclePositions[route.id];
          if (!routePositions?.length) return;

          const totalDuration = routePositions[routePositions.length - 1].timestamp + 5000;
          const elapsedTime = (currentTime - startTimeRef.current) % totalDuration;

          let currentPosition = routePositions[0];
          let nextPosition = routePositions[0];

          for (let i = 0; i < routePositions.length - 1; i++) {
            if (elapsedTime >= routePositions[i].timestamp && elapsedTime < routePositions[i + 1].timestamp) {
              currentPosition = routePositions[i];
              nextPosition = routePositions[i + 1];
              break;
            }
          }

          const segmentDuration = nextPosition.timestamp - currentPosition.timestamp;
          const progress = segmentDuration > 0
            ? (elapsedTime - currentPosition.timestamp) / segmentDuration
            : 0;

          let rotation = 0;

          if (currentPosition !== nextPosition) {
            const start = new google.maps.LatLng(currentPosition.position.lat, currentPosition.position.lng);
            const end = new google.maps.LatLng(nextPosition.position.lat, nextPosition.position.lng);
            rotation = google.maps.geometry.spherical.computeHeading(start, end) + 90;
          }

          const interpolatedLat = currentPosition.position.lat + (nextPosition.position.lat - currentPosition.position.lat) * progress;
          const interpolatedLng = currentPosition.position.lng + (nextPosition.position.lng - currentPosition.position.lng) * progress;

          newPositions[route.id] = {
            position: {
              lat: interpolatedLat,
              lng: interpolatedLng,
            },
            rotation,
            vehicleType: route.vehicule?.toLowerCase().includes('bus') ? 'bus' : 'car'
          };
        }
      });

      setInternalVehiclePositions(newPositions);
      animationFrameRef.current = requestAnimationFrame(updateVehiclePositions);
    };

    updateVehiclePositions();

    return () => {
      if (animationFrameRef.current) {
        cancelAnimationFrame(animationFrameRef.current);
      }
    };
  }, [isLoaded, isGeolocation, isGeoloc, detailedPaths]);

  useEffect(() => {
    setInternalRoutes(routes);
  }, [routes]);

  useEffect(() => {
    if (!isLoaded || !isGeolocation || !window.google || Object.keys(internalVehiclePositions).length === 0) return;

    const newVisitedUsers = new Set<string>(visitedUsers);
    let hasChanges = false;

    Object.entries(internalVehiclePositions).forEach(([routeId, vehicle]) => {
      const vehiclePosition = vehicle.position;
      const route = routesToDisplay.find(r => r.id === routeId);
      if (!route || !route.users || !route.startPoint) return;
      const startPoint = route.startPoint.position;
      const distanceToStart = google.maps.geometry.spherical.computeDistanceBetween(
        new google.maps.LatLng(vehiclePosition.lat, vehiclePosition.lng),
        new google.maps.LatLng(startPoint.lat, startPoint.lng)
      );


      if (distanceToStart < VISIT_THRESHOLD && visitedUsers.size > 0) {
        newVisitedUsers.clear();
        hasChanges = true;
      }

      route.users.forEach((user, userIndex) => {
        const userId = `user-${routesToDisplay.indexOf(route)}-${userIndex}`;

        if (newVisitedUsers.has(userId)) return;

        const distance = google.maps.geometry.spherical.computeDistanceBetween(
          new google.maps.LatLng(vehiclePosition.lat, vehiclePosition.lng),
          new google.maps.LatLng(user.position.lat, user.position.lng)
        );

        if (distance < VISIT_THRESHOLD) {
          newVisitedUsers.add(userId);
          hasChanges = true;
        }
      });
    });

    if (hasChanges) {
      setVisitedUsers(newVisitedUsers);

    }
  }, [internalVehiclePositions, routesToDisplay, isLoaded, isGeolocation, visitedUsers]);

  const onLoad = useCallback((map: google.maps.Map) => {
    mapRef.current = map;
    setMap(map);

    if (internalRoutes.length > 0) {
      const bounds = new window.google.maps.LatLngBounds();
      internalRoutes.forEach((route) => {
        if (route.path) {
          route.path.forEach((point) => bounds.extend(point));
        }
      });

      if (!bounds.isEmpty()) {
        map.fitBounds(bounds);
      }
    }

    if (onMapLoaded) {
      onMapLoaded();
    }
  }, [internalRoutes, onMapLoaded]);

  const onUnmount = useCallback(() => {
    if (mapRef.current) {
      google.maps.event.clearInstanceListeners(mapRef.current);
      mapRef.current = null;
    }
    setMap(null);
  }, []);

  const handleLocationClick = useCallback(() => {
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        (position) => {
          const pos = {
            lat: position.coords.latitude,
            lng: position.coords.longitude,
          };
          mapRef.current?.panTo(pos);
          mapRef.current?.setZoom(DEFAULT_ZOOM);
        },
        () => {
          // Silently handle error
        }
      );
    }
  }, []);

  const renderVehicles = useCallback(() => {
    if (!isGeolocation || !isGeoloc) return null;

    return routesToDisplayRef.current
      .filter(route => route.status === GeolocStatus.IN_PROGRESS && route.id && internalVehiclePositions[route.id])
      .map((route, index) => {
        const position = internalVehiclePositions[route.id];
        if (!position) return null;

        return (
          <VehicleMarker
            key={`vehicle-${route.id || index}`}
            position={{
              lat: position.position.lat,
              lng: position.position.lng,
              rotation: position.rotation || 0,
              vehicleType: position.vehicleType || 'car',
            }}
            selected={selectedVehicule === route.vehiculeId}
            onClick={() => { }}
            departureDate={route.departureDate}
            chauffeurNom={route.nomChauffeur}
            chauffeurPrenom={route.prenomChauffeur}
          />
        );
      });
  }, [isGeolocation, isGeoloc, internalVehiclePositions, selectedVehicule]);

  const markers = useMemo(() => {
    const routeMarkers: RouteMarker[] = [];

    routesToDisplay.forEach((route, routeIndex) => {
      if (route.startPoint) {
        routeMarkers.push({
          id: `start-${routeIndex}`,
          position: route.startPoint.position,
          title: route.startPoint.title,
          color: route.color || StatusColor.PRIMARY,
          icon: '/assets/icons/maps/dynamic/circuit-start.svg',
          routeIndex,
        });
      }

      if (route.endPoint) {
        routeMarkers.push({
          id: `end-${routeIndex}`,
          position: route.endPoint.position,
          title: route.endPoint.title,
          color: route.color || StatusColor.PRIMARY,
          icon: '/assets/icons/maps/dynamic/circuit-end.svg',
          routeIndex,
        });
      }


      if (route.users) {
        route.users.forEach((user, userIndex) => {
          const userId = `user-${routeIndex}-${userIndex}`;
          const isVisited = visitedUsers.has(userId);

          routeMarkers.push({
            id: userId as MarkerType,
            position: user.position,
            title: user.title,
            color: isVisited ? StatusColor.SUCCESS : route.color,

            icon: isVisited
              ? '/assets/icons/maps/dynamic/user-visited.svg'
              : '/assets/icons/maps/dynamic/user.svg',
            routeIndex,
            isVisited: visitedUsers.has(userId)
          });
        });
      }

      if (route.tolls) {
        route.tolls.forEach((toll, tollIndex) => {
          routeMarkers.push({
            id: `toll-${routeIndex}-${tollIndex}`,
            position: toll.position,
            title: toll.title,
            color: route.color || StatusColor.PRIMARY,
            icon: '/assets/icons/maps/toll.svg',
            routeIndex,
          });
        });
      }
    });

    return routeMarkers;
  }, [routesToDisplay, visitedUsers]);

  return isLoaded ? (
    <Box sx={{
      position: 'relative',
      border: '1px solid grey',
      borderRadius: 2,
      overflow: 'hidden',
      ...mapContainerStyle,
      ...sx,
    }}>
      <GoogleMap
        mapContainerStyle={mapContainerStyle}
        center={defaultCenter}
        zoom={DEFAULT_ZOOM}
        onLoad={onLoad}
        onUnmount={onUnmount}
        options={{
          disableDefaultUI: false,
          zoomControl: true,
          streetViewControl: false,
          mapTypeControl: false,
        }}
      >
        {Object.entries(directionsResults).map(([routeId, result]) => (
          <DirectionsRenderer
            key={`directions-${routeId}`}
            directions={result}
            options={{
              suppressMarkers: true,
              preserveViewport: true,
              polylineOptions: {
                strokeColor: routesToDisplay.find(r => r.id === routeId)?.color || '#000000',
                strokeOpacity: 0.7,
                strokeWeight: 5
              }
            }}
          />
        ))}

        {internalRoutes.map((route, index) => (
          route.path && !directionsResults[route.id || ''] && (
            isDriver ? (
              <MapPolylineDriver
                key={`polyline-${index}`}
                path={route.path}
                color={route.color}
                index={index}
              />
            ) : (
              <MapPolyline
                key={`polyline-${index}`}
                path={route.path}
                color={route.color}
                index={index}
                onPolylineClick={() => { }}
              />
            )
          )
        ))}

        {markers.map((marker) => (
          <MapMarker
            key={marker.id}
            marker={marker}
            selectedMarker={selectedMarker}
            setSelectedMarker={setSelectedMarker}
            Submodule={Submodule}
          />
        ))}

        {renderVehicles()}
      </GoogleMap>

      <Box
        sx={{
          position: 'absolute',
          bottom: '180px',
          right: '10px',
        }}
      >
        <Box
          sx={{
            backgroundColor: 'background.paper',
            borderRadius: '50%',
            width: 40,
            height: 40,
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            boxShadow: 3,
            cursor: 'pointer',
          }}
          onClick={handleLocationClick}
        >
          <MyLocationIcon />
        </Box>
      </Box>
    </Box>
  ) : (
    <Box
      sx={{
        display: 'flex',
        justifyContent: 'center',
        alignItems: 'center',
        height,
        width: '100%',
      }}
    >
      Chargement de la carte...
    </Box>
  );
}
