import { useEffect } from 'react';
import type { MutableRefObject } from 'react';

import type { PostcodeGeometry, ViewState } from '../../../types';
import type { useMapData } from '../../../hooks/useMapData';
import { authHeaders } from '../../../lib/api';
import { POSTCODE_SEARCH_ZOOM } from '../../../lib/consts';
import { canWheelScrollInsideTarget } from '../../../lib/dom-scroll';
import type { MapFlyTo } from './types';

type MapData = ReturnType<typeof useMapData>;
type RightPaneTab = 'properties' | 'area';
const SCREENSHOT_MAP_IDLE_FALLBACK_MS = 1000;

export function useInitialMapPageView(
  mapData: MapData,
  initialViewState: ViewState,
  initialTab: RightPaneTab,
  setRightPaneTab: (tab: RightPaneTab) => void
) {
  useEffect(() => {
    mapData.setInitialView(initialViewState);
    setRightPaneTab(initialTab);
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
}

interface UseInitialPostcodeSelectionOptions {
  initialPostcode?: string;
  isMobile: boolean;
  flyTo: MutableRefObject<MapFlyTo | null>;
  onLocationSearch: (
    postcode: string,
    geometry: PostcodeGeometry,
    lat?: number,
    lng?: number
  ) => void;
  onOpenMobileDrawer: (target: { lat: number; lng: number; zoom: number }) => void;
  onSettled?: () => void;
}

export function useInitialPostcodeSelection({
  initialPostcode,
  isMobile,
  flyTo,
  onLocationSearch,
  onOpenMobileDrawer,
  onSettled,
}: UseInitialPostcodeSelectionOptions) {
  useEffect(() => {
    if (!initialPostcode) return;

    fetch(`/api/postcode/${encodeURIComponent(initialPostcode)}`, authHeaders())
      .then((res) => {
        if (!res.ok) throw new Error('Postcode not found');
        return res.json();
      })
      .then(
        (data: {
          postcode: string;
          latitude: number;
          longitude: number;
          geometry: PostcodeGeometry;
        }) => {
          flyTo.current?.(data.latitude, data.longitude, POSTCODE_SEARCH_ZOOM);
          onLocationSearch(data.postcode, data.geometry, data.latitude, data.longitude);
          if (isMobile) {
            onOpenMobileDrawer({
              lat: data.latitude,
              lng: data.longitude,
              zoom: POSTCODE_SEARCH_ZOOM,
            });
          }
        }
      )
      .catch(() => {
        // Silently fail because the postcode might no longer exist.
      })
      .finally(() => {
        onSettled?.();
      });
  }, []); // eslint-disable-line react-hooks/exhaustive-deps
}

export function useHorizontalSwipeNavigationGuard() {
  useEffect(() => {
    const handleWheel = (e: WheelEvent) => {
      if (
        Math.abs(e.deltaX) > Math.abs(e.deltaY) &&
        !canWheelScrollInsideTarget(e.target, e.deltaX, e.deltaY)
      ) {
        e.preventDefault();
      }
    };
    document.addEventListener('wheel', handleWheel, { passive: false });
    return () => document.removeEventListener('wheel', handleWheel);
  }, []);
}

export function useMobileBackNavigationGuard(isMobile: boolean) {
  useEffect(() => {
    if (!isMobile) return;
    window.history.pushState({ dashboardGuard: true }, '');
    const handlePopState = () => {
      window.history.pushState({ dashboardGuard: true }, '');
    };
    window.addEventListener('popstate', handlePopState);
    return () => window.removeEventListener('popstate', handlePopState);
  }, [isMobile]);
}

interface UseScreenshotReadySignalOptions {
  screenshotMode?: boolean;
  loading: boolean;
  boundsReady: boolean;
  dataLength: number;
  postcodeDataLength: number;
  usePostcodeView: boolean;
  licenseRequired: boolean;
}

export function useScreenshotReadySignal({
  screenshotMode,
  loading,
  boundsReady,
  dataLength,
  postcodeDataLength,
  usePostcodeView,
  licenseRequired,
}: UseScreenshotReadySignalOptions) {
  useEffect(() => {
    if (!screenshotMode || loading || !boundsReady) return;

    const hasData = usePostcodeView ? postcodeDataLength > 0 : dataLength > 0;
    if (!hasData && !licenseRequired) return;

    let cancelled = false;
    let signalled = false;
    let frameId: number | null = null;
    let timeoutId: number | null = null;

    const signalReady = () => {
      if (cancelled || signalled) return;
      signalled = true;
      if (timeoutId != null) window.clearTimeout(timeoutId);
      if (frameId != null) window.cancelAnimationFrame(frameId);
      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          if (!cancelled) window.__screenshot_ready = true;
        });
      });
    };

    const waitAndSignal = () => {
      if (window.__map_idle) {
        signalReady();
      } else {
        frameId = requestAnimationFrame(waitAndSignal);
      }
    };

    // In webpack dev mode MapLibre's idle event can be delayed by the dev
    // client/HMR churn even after data has rendered. Keep production-quality
    // waiting when idle fires, but avoid forcing the screenshot service to hit
    // its much longer timeout in local development.
    timeoutId = window.setTimeout(signalReady, SCREENSHOT_MAP_IDLE_FALLBACK_MS);
    waitAndSignal();

    return () => {
      cancelled = true;
      if (timeoutId != null) window.clearTimeout(timeoutId);
      if (frameId != null) window.cancelAnimationFrame(frameId);
    };
  }, [
    screenshotMode,
    loading,
    boundsReady,
    dataLength,
    postcodeDataLength,
    usePostcodeView,
    licenseRequired,
  ]);
}
