"use client";
import { Control, Icon, LatLngExpression } from "leaflet";
import { useEffect, useRef, useState } from "react";
import {
	LayerGroup,
	LayersControl,
	TileLayer,
	useMap,
	WMSTileLayer,
	GeoJSON,
	Circle,
	useMapEvent,
	FeatureGroup,
	Marker,
	Tooltip,
} from "react-leaflet";
// @ts-expect-error geojson hat keine Typen
import type { FeatureCollection, Geometry } from "geojson";
import L from "leaflet";
import LEITSTELLENBERECHE from "./_geojson/Leitstellen.json";
import WINDFARMS from "./_geojson/Windfarms.json";
import { createCustomMarker } from "_components/map/_components/createCustomMarker";
import { Station } from "@repo/db";
import { useQuery } from "@tanstack/react-query";
import { getStationsAPI } from "_querys/stations";
import "./darkMapStyles.css";

const RadioAreaLayer = () => {
	const getColor = (randint: number) => {
		switch (randint) {
			case 1:
				return "#2b6eff";
			case 2:
				return "#233ee5";
			case 3:
				return "#7BA5FF";
			case 4:
				return "#5087FF";
			default:
				return "#7f7f7f";
		}
	};

	return (
		<GeoJSON
			data={LEITSTELLENBERECHE as FeatureCollection<Geometry>}
			style={(feature) => {
				if (!feature || !feature.properties) return {}; // Early return if feature or its properties are undefined
				return {
					color: getColor(feature.properties.randint),
					weight: 1.5,
					className: "no-pointer",
				};
			}}
			onEachFeature={(feature, layer) => {
				if (feature && feature.properties && feature.properties.name) {
					layer.bindTooltip(
						new L.Tooltip({
							content: feature.properties.name,
							direction: "top",
							sticky: true,
						}),
					);
				}
			}}
		/>
	);
};

const StationsLayer = ({ attribution }: { attribution: Control.Attribution }) => {
	const { data: stations } = useQuery({
		queryKey: ["stations"],
		queryFn: () => getStationsAPI(),
	});

	const [selectedStations, setSelectedStations] = useState<Station["id"][]>([]);
	const attributionText = "";

	const resetSelection = () => {
		setSelectedStations([]);
	};

	useMapEvent("click", () => {
		resetSelection();
	});

	const handleMarkerClick = (stationId: number) => {
		if (selectedStations.includes(stationId)) {
			setSelectedStations((prevStations) => prevStations.filter((s) => s !== stationId));
		} else {
			setSelectedStations((prevStations) => [...prevStations, stationId]);
		}
	};

	const [stationsWithIcon, setStationsWithIcon] = useState<(Station & { icon: string })[]>([]);

	useEffect(() => {
		if (!stations) {
			setStationsWithIcon([]);
			return;
		}
		const fetchIcons = async () => {
			const urls = await Promise.all(
				stations.map(async (station) => {
					return await createCustomMarker(station.operator);
				}),
			);
			setStationsWithIcon(stations.map((station, index) => ({ ...station, icon: urls[index]! })));
		};
		fetchIcons();
	}, [stations]);

	return (
		<FeatureGroup>
			{stationsWithIcon?.map((station) => {
				const coordinates: LatLngExpression = [station.latitude, station.longitude];
				const typeLabel = station.bosUse?.charAt(0).toUpperCase();

				return (
					<Marker
						key={`marker-${station.id}`}
						position={coordinates}
						icon={
							new Icon({
								iconUrl: station.icon,
								iconSize: [30, 30],
								iconAnchor: [15, 15],
								tooltipAnchor: [0, 20],
								className: station.hideRangeRings ? "no-pointer" : "pointer",
							})
						}
						eventHandlers={{
							click: () => {
								if (!station.hideRangeRings) handleMarkerClick(station.id);
							},
							add: () => attribution.addAttribution(attributionText),
							remove: () => attribution.removeAttribution(attributionText),
						}}
					>
						<Tooltip direction="top" sticky>
							<div style={{ textAlign: "center" }}>
								<strong>{station.bosCallsign}</strong>
								<small style={{ fontWeight: "bold", fontSize: "0.7em" }}>{` (${typeLabel})`}</small>
								<br />
								<small>
									{[
										station.hasWinch ? "W" : null,
										station.is24h ? "24h" : null,
										station.hasNvg ? "N" : null,
									]
										.filter(Boolean)
										.join(", ")}
								</small>
							</div>
						</Tooltip>
					</Marker>
				);
			})}

			{selectedStations.map((stationId) => {
				const station = stations?.find((s) => s.id === stationId);

				if (!station) return null;

				const center: LatLngExpression = [station.latitude, station.longitude];
				return (
					<div key={`marker-${stationId}`}>
						<Circle
							center={center}
							radius={(station.aircraftSpeed * 1000) / 6}
							color="#0e0ecf"
							fillOpacity={0}
							weight={2}
						/>
						<Circle
							center={center}
							radius={(station.aircraftSpeed * 1000) / 3}
							color="navy"
							fillOpacity={0}
							weight={2}
						/>
						{(station.bosUse === "SECONDARY" || station.bosUse === "DUAL_USE") && (
							<Circle
								center={center}
								radius={station.aircraftSpeed * 1000}
								color="maroon"
								fillOpacity={0}
								weight={1}
								dashArray="40,30"
							/>
						)}
					</div>
				);
			})}
		</FeatureGroup>
	);
};

const EsriSatellite = () => {
	const accessToken = process.env.NEXT_PUBLIC_ESRI_ACCESS;
	return (
		<>
			{/* Satellite Imagery Layer; API KEY PROVIDED BY VAR0002 */}
			<TileLayer
				attribution="Sources: Esri, TomTom, Garmin, FAO, NOAA, USGS"
				url={`https://services.arcgisonline.com/arcgis/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}?token=${accessToken}`}
				tileSize={256}
			/>
		</>
	);
};

const StrassentexteEsri = () => {
	return (
		<WMSTileLayer
			url="https://tiledbasemaps.arcgis.com/arcgis/rest/services/Reference/World_Transportation/MapServer/tile/{z}/{y}/{x}"
			format="image/png"
			transparent
		/>
	);
};

const OpenAIP = () => {
	const ref = useRef<L.TileLayer | null>(null);

	return (
		<TileLayer
			eventHandlers={{
				add: () => {
					if (ref.current) {
						ref.current.bringToFront();
					}
				},
			}}
			ref={ref}
			attribution='© <a href="https://www.openaip.net" target="_blank">OpenAIP</a>'
			url={`https://api.tiles.openaip.net/api/data/openaip/{z}/{x}/{y}.png?apiKey=${process.env.NEXT_PUBLIC_OPENAIP_ACCESS}`}
		/>
	);
};

const NiederschlagOverlay = () => {
	const tileLayerRef = useRef<L.TileLayer.WMS | null>(null);

	return (
		<WMSTileLayer
			ref={tileLayerRef}
			eventHandlers={{
				add: () => {
					tileLayerRef.current?.bringToFront();
				},
			}}
			attribution="Quelle: Deutscher Wetterdienst"
			url="https://maps.dwd.de/geoserver/wms?"
			format="image/png"
			layers="dwd:Niederschlagsradar"
			transparent
			opacity={0.7}
		/>
	);
};

const WindfarmOutlineLayer = () => {
	const map = useMap();
	const [isVisible, setIsVisible] = useState(false);

	useEffect(() => {
		const handleZoom = () => {
			setIsVisible(map.getZoom() < 13);
		};

		// Initial check and event listener
		handleZoom();
		map.on("zoomend", handleZoom);

		// Cleanup on unmount
		return () => {
			map.off("zoomend", handleZoom);
		};
	}, [map]);

	return isVisible ? (
		<GeoJSON
			data={WINDFARMS as FeatureCollection<Geometry>}
			style={() => ({
				color: "#233EE5",
				weight: 1.5,
				dashArray: "7, 10",
				className: "no-pointer",
			})}
			onEachFeature={(feature, layer) => {
				layer.bindTooltip(
					new L.Tooltip({
						content: feature.properties.Cluster_NR,
						direction: "top",
						sticky: true,
					}),
				);
			}}
		/>
	) : null;
};

export const BaseMaps = () => {
	const map = useMap();
	return (
		<LayersControl position="topleft">
			<LayersControl.Overlay name={"Leitstellenbereiche"}>
				<RadioAreaLayer />
			</LayersControl.Overlay>
			<LayersControl.Overlay name={"Niederschlag"}>
				<NiederschlagOverlay />
			</LayersControl.Overlay>

			<LayersControl.Overlay name={"Windkraftanlagen offshore"}>
				<WindfarmOutlineLayer />
			</LayersControl.Overlay>

			<LayersControl.Overlay name={"LRZs"}>
				<StationsLayer attribution={map.attributionControl} />
			</LayersControl.Overlay>
			<LayersControl.Overlay name={"OpenAIP"}>
				<OpenAIP />
			</LayersControl.Overlay>

			<LayersControl.BaseLayer name="OpenStreetMap Dark" checked>
				<TileLayer
					zIndex={-1}
					attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
					url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
					className="invert-100 grayscale"
				/>
			</LayersControl.BaseLayer>
			<LayersControl.BaseLayer name="OpenStreetMap">
				<TileLayer
					zIndex={-1}
					attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
					url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
				/>
			</LayersControl.BaseLayer>
			<LayersControl.BaseLayer name="OpenTopoMap">
				<TileLayer
					attribution='map data: © <a href="https://opentopomap.org/copyright">OpenStreetMap</a> contributors, <a href="http://viewfinderpanoramas.org">SRTM</a> | map style: © <a href="https://opentopomap.org">OpenTopoMap</a> (<a href="https://creativecommons.org/licenses/by-sa/3.0/">CC-BY-SA</a>)'
					url="https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png"
				/>
			</LayersControl.BaseLayer>
			<LayersControl.BaseLayer name="ESRI Satellite">
				<LayerGroup>
					<EsriSatellite />
					<StrassentexteEsri />
				</LayerGroup>
			</LayersControl.BaseLayer>
		</LayersControl>
	);
};
