import "mapbox-gl/dist/mapbox-gl.css";

import mapboxgl, { LngLatBounds } from "mapbox-gl";
import React, { Dispatch, SetStateAction, useCallback, useEffect, useRef, useState } from "react";
import { createRoot } from "react-dom/client";
import { GiObservatory } from "react-icons/gi";

import GeologicalEvent from "@appTypes/Event";
import getMagnitudeColor from "@utils/getMagnitudeColour";

interface MapComponentProps {
	events: GeologicalEvent[];
	selectedEventId: GeologicalEvent["id"];
	setSelectedEventId: Dispatch<SetStateAction<string>>;
	hoveredEventId: GeologicalEvent["id"];
	setHoveredEventId: Dispatch<SetStateAction<string>>;
	mapType: String;
}

function MapComponent({
	events,
	selectedEventId,
	setSelectedEventId,
	hoveredEventId,
	setHoveredEventId,
	mapType,
}: MapComponentProps) {
	const map = useRef<mapboxgl.Map | null>(null);
	const markers = useRef<{ [key: string]: mapboxgl.Marker }>({});
	const [mapBounds, setMapBounds] = useState<LngLatBounds>();

	const fitToBounds = useCallback((bounds: LngLatBounds, padding: number = 150) => {
		if (map.current && bounds) {
			map.current!.fitBounds(bounds, { padding, maxZoom: 10, zoom: 1 });
		}
	}, []);

	const showPopup = useCallback((eventId: string) => {
		const marker = markers.current[eventId];
		if (marker && map.current) {
			marker.getPopup()?.addTo(map.current);
		}
	}, []);

	const clearAllPopups = useCallback(() => {
		Object.values(markers.current).forEach((marker) => marker.getPopup()?.remove());
	}, []);

	const flyToEvent = useCallback((event: GeologicalEvent) => {
		if (map.current) {
			map.current.flyTo({
				center: [event.longitude, event.latitude],
				zoom: 4,
				speed: 1.5,
				curve: 1.42,
			});
		}
	}, []);

	useEffect(() => {
		mapboxgl.accessToken = "pk.eyJ1IjoidGltaG93aXR6IiwiYSI6ImNtOGtjcXA5bDA3Ym4ya3NnOWxjbjlxZG8ifQ.6u_KgXEdLTakz910QRAorQ";

		try {
			map.current = new mapboxgl.Map({
				container: "map-container",
				style: "mapbox://styles/mapbox/light-v10",
				center: [0, 0],
				zoom: 1,
			});
		} catch (error) {
			console.error("Map initialization failed:", error);
			return;
		}

		map.current.on("load", () => {
			const bounds = new mapboxgl.LngLatBounds();
			events.forEach((event) => {
				bounds.extend([event.longitude, event.latitude]);
			});
			fitToBounds(bounds, 0);
			setMapBounds(bounds);

			events.forEach((event) => {
				const quakeElement = document.createElement("div");
				const dotElement = document.createElement("div");
				const pulseElement = document.createElement("div");

				if (event.magnitude) {
					const color = getMagnitudeColor(event.magnitude);
					quakeElement.style.width = "50px";
					quakeElement.style.height = "50px";
					quakeElement.style.position = "absolute";
					quakeElement.style.display = "flex";
					quakeElement.style.alignItems = "center";
					quakeElement.style.justifyContent = "center";

					dotElement.style.width = "10px";
					dotElement.style.height = "10px";
					dotElement.style.backgroundColor = color;
					dotElement.style.borderRadius = "50%";
					dotElement.style.position = "absolute";
					dotElement.style.zIndex = "2";

					pulseElement.className = "location-pulse";
					pulseElement.style.width = "20px";
					pulseElement.style.height = "20px";
					pulseElement.style.backgroundColor = `${color}80`;
					pulseElement.style.borderRadius = "50%";
					pulseElement.style.position = "absolute";
					pulseElement.style.zIndex = "1";
				}

				const observatoryElement = document.createElement("div");
                const root = createRoot(observatoryElement);
                root.render(
                    <GiObservatory
                       className={`text-2xl drop-shadow-lg ${event.isFunctional === false ? "text-gray-400" : "text-blue-600"}`}
                    />
                );

				quakeElement.appendChild(pulseElement);
				quakeElement.appendChild(dotElement);

				const marker = new mapboxgl.Marker({ element: mapType === "observatories" ? observatoryElement : quakeElement })
					.setLngLat([event.longitude, event.latitude])
					.addTo(map.current!);

				const popup = new mapboxgl.Popup({ offset: 25, closeButton: false, anchor: "bottom" }).setHTML(`
          <div>
            <h3>${event.title}</h3>
            ${mapType === "observatories" ? `<p>${event.text1}</p>` : `<p>Magnitude: ${event.magnitude}</p>`}
            <p>${event.text2}</p>
          </div>
        `);

				marker.setPopup(popup);
				markers.current[event.id] = marker;

				const markerDomElement = marker.getElement();
				markerDomElement.style.cursor = "pointer";

				markerDomElement.addEventListener("mouseenter", () => setHoveredEventId(event.id));
				markerDomElement.addEventListener("mouseleave", () => setHoveredEventId(""));
				markerDomElement.addEventListener("click", () =>
					setSelectedEventId((prevEventId) => (prevEventId === event.id ? "" : event.id))
				);

				markerDomElement.dataset.listenersAdded = "true";
			});
		});

		// Add map click handler to deselect when clicking outside markers
		map.current.on("click", (e) => {
			const target = e.originalEvent.target as HTMLElement;
			// Check if the click target is not a marker element
			if (!target.closest(".mapboxgl-marker")) {
				setSelectedEventId("");
			}
		});

		map.current.on("error", (e) => {
			console.error("Mapbox error:", e);
		});

		return () => {
			map.current?.remove();
		};
	}, [events, setSelectedEventId, setHoveredEventId, fitToBounds]);

	useEffect(() => {
		const event = events.find((x) => x.id === selectedEventId);
		if (event) flyToEvent(event);
		else if (!selectedEventId && mapBounds) {
			fitToBounds(mapBounds, 0);
		}
	}, [events, selectedEventId, mapBounds, fitToBounds, flyToEvent]);

	useEffect(() => {
		clearAllPopups();
		if (hoveredEventId && selectedEventId && hoveredEventId !== selectedEventId) {
			showPopup(hoveredEventId);
			showPopup(selectedEventId);
		} else if (hoveredEventId || selectedEventId) {
			showPopup(hoveredEventId || selectedEventId);
		}
	}, [hoveredEventId, selectedEventId, clearAllPopups, showPopup]);

	return (
		<div className="w-full h-full">
			<div id="map-container" className="w-full h-full" />
		</div>
	);
}

const pulseStyles = `
  .location-pulse {
    animation: locationPulse 2s infinite;
  }

  @keyframes locationPulse {
    0% {
      transform: scale(0.5);
      opacity: 0.8;
    }
    70% {
      transform: scale(2);
      opacity: 0;
    }
    100% {
      transform: scale(2);
      opacity: 0;
    }
  }
`;

export default function Map(props: MapComponentProps) {
	return (
		<>
			<style>{pulseStyles}</style>
			<MapComponent {...props} />
		</>
	);
}
