import { useCallback, useRef, useState } from 'react';
import type { MutableRefObject } from 'react';

import type { MapFlyToOptions } from '../../../types';
import type { MapFlyTo } from './types';

export interface PendingFlyTo {
  lat: number;
  lng: number;
  zoom: number;
}

/**
 * Mobile drawer / bottom sheet state plus the fly-to plumbing that keeps a
 * selected target visible above them. Fly-tos requested while the drawer panel
 * hasn't measured itself yet are parked in refs and consumed once the panel
 * rect arrives, so the camera lands in the area the drawer leaves uncovered.
 */
export function useMobileDrawer(isMobile: boolean, flyToRef: MutableRefObject<MapFlyTo | null>) {
  const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false);
  const [mobileBottomSheetHeight, setMobileBottomSheetHeight] = useState(0);
  const mobileDrawerPanelRectRef = useRef<DOMRectReadOnly | null>(null);
  const pendingCurrentLocationFlyToRef = useRef<{ lat: number; lng: number } | null>(null);
  const pendingLocationSearchFlyToRef = useRef<PendingFlyTo | null>(null);

  const consumePendingLocationSearchFlyTo = useCallback(
    (rect?: DOMRectReadOnly | null) => {
      const pending = pendingLocationSearchFlyToRef.current;
      const panelRect = rect ?? mobileDrawerPanelRectRef.current;
      if (!pending || !panelRect) return;

      const bottomInset = Math.max(0, window.innerHeight - panelRect.top);
      const flyTo = flyToRef.current;
      if (!flyTo) return;
      flyTo(pending.lat, pending.lng, pending.zoom, {
        visibleViewportArea: { bottom: bottomInset },
      });
      pendingLocationSearchFlyToRef.current = null;
    },
    [flyToRef]
  );

  const consumePendingCurrentLocationFlyTo = useCallback(
    (rect?: DOMRectReadOnly | null) => {
      const pending = pendingCurrentLocationFlyToRef.current;
      const panelRect = rect ?? mobileDrawerPanelRectRef.current;
      if (!pending || !panelRect) return;

      const bottomInset = Math.max(0, window.innerHeight - panelRect.top);
      const flyTo = flyToRef.current;
      if (!flyTo) return;
      flyTo(pending.lat, pending.lng, 17, {
        visibleViewportArea: { bottom: bottomInset },
      });
      pendingCurrentLocationFlyToRef.current = null;
    },
    [flyToRef]
  );

  const openMobileDrawer = useCallback(() => {
    setMobileDrawerOpen(true);
  }, []);

  /** Open the drawer and fly to the searched location once the panel rect is known. */
  const openMobileDrawerForLocationSearch = useCallback(
    (target: PendingFlyTo) => {
      pendingLocationSearchFlyToRef.current = target;
      setMobileDrawerOpen(true);
      consumePendingLocationSearchFlyTo();
    },
    [consumePendingLocationSearchFlyTo]
  );

  const clearPendingLocationSearchFlyTo = useCallback(() => {
    pendingLocationSearchFlyToRef.current = null;
  }, []);

  /** Park a current-location fly-to until the drawer panel has measured itself. */
  const queueCurrentLocationFlyTo = useCallback(
    (lat: number, lng: number) => {
      pendingCurrentLocationFlyToRef.current = { lat, lng };
      consumePendingCurrentLocationFlyTo();
    },
    [consumePendingCurrentLocationFlyTo]
  );

  const handleMobileDrawerPanelRectChange = useCallback(
    (rect: DOMRectReadOnly) => {
      mobileDrawerPanelRectRef.current = rect;
      consumePendingCurrentLocationFlyTo(rect);
      consumePendingLocationSearchFlyTo(rect);
    },
    [consumePendingCurrentLocationFlyTo, consumePendingLocationSearchFlyTo]
  );

  const handleMobileDrawerClose = useCallback(() => {
    pendingCurrentLocationFlyToRef.current = null;
    pendingLocationSearchFlyToRef.current = null;
    mobileDrawerPanelRectRef.current = null;
    setMobileDrawerOpen(false);
  }, []);

  const getMobileMapFlyToOptions = useCallback((): MapFlyToOptions | undefined => {
    if (!isMobile) return undefined;

    const panelRect = mobileDrawerPanelRectRef.current;
    if (mobileDrawerOpen && panelRect) {
      const bottomInset = Math.max(0, window.innerHeight - panelRect.top);
      if (bottomInset > 0) {
        return { visibleViewportArea: { bottom: bottomInset } };
      }
    }

    return mobileBottomSheetHeight > 0
      ? { visibleArea: { bottom: mobileBottomSheetHeight } }
      : undefined;
  }, [isMobile, mobileBottomSheetHeight, mobileDrawerOpen]);

  return {
    mobileDrawerOpen,
    mobileBottomSheetHeight,
    setMobileBottomSheetHeight,
    openMobileDrawer,
    openMobileDrawerForLocationSearch,
    clearPendingLocationSearchFlyTo,
    queueCurrentLocationFlyTo,
    handleMobileDrawerPanelRectChange,
    handleMobileDrawerClose,
    getMobileMapFlyToOptions,
  };
}
