import { useEffect, useId, useRef, useState } from 'react';

declare global {
    interface Window {
        __googleMapsApiPromise?: Promise<GoogleMapsNamespace>;
        google?: GoogleMapsNamespace;
    }
}

type GoogleMapsNamespace = {
    maps: {
        Circle: new (options: Record<string, unknown>) => GoogleMapsCircle;
        LatLngBounds: new (
            sw?: GoogleMapsLatLngLike,
            ne?: GoogleMapsLatLngLike,
        ) => GoogleMapsLatLngBounds;
        Map: new (
            element: HTMLElement,
            options: Record<string, unknown>,
        ) => GoogleMapsMap;
        Marker: new (options: Record<string, unknown>) => GoogleMapsMarker;
    };
};

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

type GoogleMapsMap = {
    fitBounds: (bounds: GoogleMapsLatLngBounds, padding?: number) => void;
};

type GoogleMapsMarker = {
    setMap: (map: GoogleMapsMap | null) => void;
};

type GoogleMapsCircle = {
    getBounds: () => GoogleMapsLatLngBounds | null;
    setMap: (map: GoogleMapsMap | null) => void;
};

type GoogleMapsLatLngBounds = {
    extend: (latLng: GoogleMapsLatLngLike) => void;
};

export type ServiceAreaFixedMapProps = {
    apiKey: string;
    name?: string;
    center: GoogleMapsLatLngLike;
    radiusKm: number;
    bounds?: {
        north: number;
        south: number;
        east: number;
        west: number;
    };
    className?: string;
};

const loadGoogleMapsApi = (apiKey: string): Promise<GoogleMapsNamespace> => {
    if (window.google?.maps) {
        return Promise.resolve(window.google);
    }

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

    window.__googleMapsApiPromise = new Promise<GoogleMapsNamespace>(
        (resolve, reject) => {
            const script = document.createElement('script');

            script.src = `https://maps.googleapis.com/maps/api/js?key=${apiKey}`;
            script.async = true;
            script.defer = true;
            script.onload = () => {
                if (window.google?.maps) {
                    resolve(window.google);
                    return;
                }

                reject(new Error('Google Maps API loaded without maps.'));
            };
            script.onerror = () =>
                reject(new Error('Failed to load Google Maps API.'));

            document.head.appendChild(script);
        },
    );

    return window.__googleMapsApiPromise;
};

export function ServiceAreaFixedMap({
    apiKey,
    name,
    center,
    radiusKm,
    bounds,
    className,
}: ServiceAreaFixedMapProps) {
    const mapElementId = useId();
    const mapElementRef = useRef<HTMLDivElement | null>(null);
    const circleRef = useRef<GoogleMapsCircle | null>(null);
    const markerRef = useRef<GoogleMapsMarker | null>(null);
    const [error, setError] = useState<string | null>(null);

    useEffect(() => {
        if (!apiKey) {
            setError('Google Maps API key is missing.');
            return;
        }

        const mapElement = mapElementRef.current;

        if (!mapElement) {
            return;
        }

        let disposed = false;

        loadGoogleMapsApi(apiKey)
            .then((googleMaps) => {
                if (disposed) {
                    return;
                }

                const map = new googleMaps.maps.Map(mapElement, {
                    center,
                    zoom: 11,
                    mapTypeControl: false,
                    streetViewControl: false,
                    fullscreenControl: false,
                });

                markerRef.current = new googleMaps.maps.Marker({
                    map,
                    position: center,
                    draggable: false,
                    clickable: false,
                    title: name ?? 'Service area center',
                });

                circleRef.current = new googleMaps.maps.Circle({
                    map,
                    center,
                    radius: radiusKm * 1000,
                    editable: false,
                    draggable: false,
                    clickable: false,
                    fillColor: '#f59e0b',
                    fillOpacity: 0.18,
                    strokeColor: '#d97706',
                    strokeOpacity: 0.95,
                    strokeWeight: 2,
                });

                if (bounds) {
                    const viewport = new googleMaps.maps.LatLngBounds(
                        { lat: bounds.south, lng: bounds.west },
                        { lat: bounds.north, lng: bounds.east },
                    );

                    map.fitBounds(viewport, 48);
                    return;
                }

                const circleBounds = circleRef.current.getBounds();

                if (circleBounds) {
                    map.fitBounds(circleBounds, 48);
                    return;
                }

                const fallbackBounds = new googleMaps.maps.LatLngBounds();
                fallbackBounds.extend(center);
                map.fitBounds(fallbackBounds, 48);
            })
            .catch((loadError: unknown) => {
                if (disposed) {
                    return;
                }

                const message =
                    loadError instanceof Error
                        ? loadError.message
                        : 'Unable to load Google Maps.';

                setError(message);
            });

        return () => {
            disposed = true;
            circleRef.current?.setMap(null);
            markerRef.current?.setMap(null);
        };
    }, [apiKey, bounds, center, name, radiusKm]);

    if (error) {
        return (
            <div
                className={
                    className ??
                    'rounded-xl border border-amber-200 bg-amber-50 px-4 py-3 text-sm text-amber-900'
                }
            >
                {error}
            </div>
        );
    }

    return (
        <div className={className ?? 'space-y-3'}>
            <div
                id={mapElementId}
                ref={mapElementRef}
                className="overflow-hidden rounded-2xl border border-gray-200 bg-white shadow-sm"
                style={{ height: '28rem' }}
            />
            <p className="text-sm text-gray-500">
                The map can be moved or zoomed, but the service area stays fixed
                to the API location and radius.
            </p>
        </div>
    );
}
