"use client";

import { useEffect, useMemo, useRef, useState } from "react";
import { Loader2, LocateFixed, MapPin } from "lucide-react";

type AddressLocationMapProps = {
  latitude: number | null;
  longitude: number | null;
  status: string;
  isLocating: boolean;
  onRefresh: () => void;
  onPointSelect: (latitude: number, longitude: number) => void | Promise<void>;
};

type GoogleMapsApi = {
  maps: {
    Animation: {
      DROP: number;
    };
    LatLng: new (latitude: number, longitude: number) => GoogleLatLng;
    Map: new (
      element: HTMLElement,
      options: {
        center: GoogleLatLngLiteral;
        clickableIcons?: boolean;
        fullscreenControl?: boolean;
        mapTypeControl?: boolean;
        streetViewControl?: boolean;
        zoom: number;
      },
    ) => GoogleMap;
    Marker: new (options: {
      animation?: number;
      draggable?: boolean;
      map: GoogleMap;
      position: GoogleLatLngLiteral;
      title?: string;
    }) => GoogleMarker;
  };
};

type GoogleLatLngLiteral = {
  lat: number;
  lng: number;
};

type GoogleLatLng = {
  lat: () => number;
  lng: () => number;
};

type GoogleMap = {
  addListener: (
    eventName: "click",
    handler: (event: { latLng: GoogleLatLng | null }) => void,
  ) => void;
  panTo: (position: GoogleLatLngLiteral) => void;
  setZoom: (zoom: number) => void;
};

type GoogleMarker = {
  addListener: (eventName: "dragend", handler: () => void) => void;
  getPosition: () => GoogleLatLng | null;
  setMap: (map: GoogleMap | null) => void;
  setPosition: (position: GoogleLatLngLiteral) => void;
};

declare global {
  interface Window {
    google?: GoogleMapsApi;
    rideNReviveGoogleMapsPromise?: Promise<GoogleMapsApi>;
    rideNReviveInitGoogleMaps?: () => void;
    gm_authFailure?: () => void;
  }
}

const defaultLatitude = 28.4595;
const defaultLongitude = 77.0266;
const googleMapsApiKey = process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY ?? "";

export function AddressLocationMap({
  latitude,
  longitude,
  status,
  isLocating,
  onRefresh,
  onPointSelect,
}: AddressLocationMapProps) {
  const mapElementRef = useRef<HTMLDivElement | null>(null);
  const mapRef = useRef<GoogleMap | null>(null);
  const markerRef = useRef<GoogleMarker | null>(null);
  const onPointSelectRef = useRef(onPointSelect);
  const [mapError, setMapError] = useState("");
  const hasLocation = latitude !== null && longitude !== null;
  const center = useMemo(
    () => ({
      lat: latitude ?? defaultLatitude,
      lng: longitude ?? defaultLongitude,
    }),
    [latitude, longitude],
  );

  useEffect(() => {
    onPointSelectRef.current = onPointSelect;
  }, [onPointSelect]);

  useEffect(() => {
    let isMounted = true;

    async function setupMap() {
      if (!mapElementRef.current) {
        return;
      }

      if (!googleMapsApiKey) {
        setMapError("Add NEXT_PUBLIC_GOOGLE_MAPS_API_KEY to show the Google pin picker.");
        return;
      }

      try {
        const google = await loadGoogleMaps();

        if (!isMounted || !mapElementRef.current) {
          return;
        }

        const map = new google.maps.Map(mapElementRef.current, {
          center,
          clickableIcons: false,
          fullscreenControl: false,
          mapTypeControl: false,
          streetViewControl: false,
          zoom: 17,
        });
        const marker = new google.maps.Marker({
          animation: google.maps.Animation.DROP,
          draggable: true,
          map,
          position: center,
          title: "Service location",
        });

        map.addListener("click", (event) => {
          if (!event.latLng) {
            return;
          }

          updatePin(event.latLng.lat(), event.latLng.lng(), map, marker);
        });

        marker.addListener("dragend", () => {
          const position = marker.getPosition();

          if (position) {
            updatePin(position.lat(), position.lng(), map, marker);
          }
        });

        mapRef.current = map;
        markerRef.current = marker;
        setMapError("");
      } catch (error) {
        setMapError(
          error instanceof Error
            ? error.message
            : "Google Maps could not be loaded. Check the browser map API key.",
        );
      }
    }

    setupMap();

    return () => {
      isMounted = false;
      markerRef.current?.setMap(null);
      markerRef.current = null;
      mapRef.current = null;
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  useEffect(() => {
    if (!mapRef.current || !markerRef.current || !hasLocation) {
      return;
    }

    markerRef.current.setPosition(center);
    mapRef.current.panTo(center);
    mapRef.current.setZoom(17);
  }, [center, hasLocation]);

  function updatePin(
    rawLatitude: number,
    rawLongitude: number,
    map = mapRef.current,
    marker = markerRef.current,
  ) {
    const nextPosition = {
      lat: Number(rawLatitude.toFixed(6)),
      lng: Number(rawLongitude.toFixed(6)),
    };

    marker?.setPosition(nextPosition);
    map?.panTo(nextPosition);
    void onPointSelectRef.current(nextPosition.lat, nextPosition.lng);
  }

  return (
    <div className="overflow-hidden rounded-md border border-primary/20 bg-white">
      <div className="flex flex-wrap items-center justify-between gap-3 bg-primary/5 p-3">
        <div className="flex min-w-0 items-start gap-3">
          <div className="grid h-9 w-9 shrink-0 place-items-center rounded-md bg-primary text-primary-foreground">
            {isLocating ? (
              <Loader2 className="h-4 w-4 animate-spin" />
            ) : (
              <MapPin className="h-4 w-4" />
            )}
          </div>
          <div className="min-w-0">
            <p className="text-sm font-black text-foreground">
              {hasLocation ? "Pinned service location" : "Click Google map to place pin"}
            </p>
            <p className="mt-1 text-xs font-bold leading-5 text-muted-foreground">{status}</p>
          </div>
        </div>
        <button
          type="button"
          onClick={onRefresh}
          disabled={isLocating}
          className="inline-flex h-10 items-center justify-center gap-2 rounded-md border border-primary/25 bg-white px-3 text-xs font-black text-primary transition hover:bg-primary/10 disabled:opacity-60"
        >
          {isLocating ? (
            <Loader2 className="h-4 w-4 animate-spin" />
          ) : (
            <LocateFixed className="h-4 w-4" />
          )}
          Current location
        </button>
      </div>

      <div className="relative h-64 overflow-hidden bg-slate-100">
        <div ref={mapElementRef} className="h-full w-full" />
        {mapError ? (
          <div className="absolute inset-0 grid place-items-center bg-slate-100 px-4 text-center">
            <div>
              <MapPin className="mx-auto h-8 w-8 text-muted-foreground" />
              <p className="mt-2 text-sm font-black text-foreground">Google map unavailable</p>
              <p className="mt-1 text-xs font-bold leading-5 text-muted-foreground">{mapError}</p>
            </div>
          </div>
        ) : null}
      </div>

      <div className="flex flex-wrap gap-2 border-t border-border bg-slate-50 p-3 text-xs font-black text-muted-foreground">
        {hasLocation ? (
          <>
            <span>Lat {latitude.toFixed(6)}</span>
            <span>Lng {longitude.toFixed(6)}</span>
          </>
        ) : (
          <span>Click the Google map or drag the pin to set the service location.</span>
        )}
      </div>
    </div>
  );
}

function loadGoogleMaps() {
  if (window.google?.maps) {
    return Promise.resolve(window.google);
  }

  if (window.rideNReviveGoogleMapsPromise) {
    return window.rideNReviveGoogleMapsPromise;
  }

  window.rideNReviveGoogleMapsPromise = new Promise((resolve, reject) => {
    const script = document.createElement("script");
    const callbackName = "rideNReviveInitGoogleMaps";
    const params = new URLSearchParams({
      callback: callbackName,
      key: googleMapsApiKey,
      libraries: "places",
      v: "weekly",
    });

    window.gm_authFailure = () => {
      window.rideNReviveGoogleMapsPromise = undefined;
      reject(
        new Error(
          "Google Maps rejected this browser key. Enable Maps JavaScript API and allow http://localhost:3000/* in key restrictions.",
        ),
      );
    };
    window[callbackName] = () => {
      if (window.google?.maps) {
        resolve(window.google);
        return;
      }

      reject(new Error("Google Maps loaded but did not initialize."));
    };
    script.src = `https://maps.googleapis.com/maps/api/js?${params}`;
    script.async = true;
    script.defer = true;
    script.onerror = () => {
      window.rideNReviveGoogleMapsPromise = undefined;
      reject(
        new Error("Google Maps script failed to load. Check network and API key restrictions."),
      );
    };
    document.head.appendChild(script);
  });

  return window.rideNReviveGoogleMapsPromise;
}
