/**
 * useMapLayers — React 리렌더 없이 deck.gl 레이어를 업데이트하는 hook
 *
 * 원리: RAF(requestAnimationFrame) 배치로 overlay.setProps() 직접 호출
 * React의 setState/render cycle을 완전 우회
 */
import { useRef, useEffect } from 'react';
import type { MapboxOverlay } from '@deck.gl/mapbox';
import type { Layer } from 'deck.gl';
import type maplibregl from 'maplibre-gl';

export interface MapHandle {
  overlay: MapboxOverlay | null;
  map: maplibregl.Map | null;
}

/**
 * 레이어를 RAF 기반으로 업데이트
 * @param handleRef — BaseMap의 overlay를 담은 ref
 * @param buildLayers — 레이어 빌드 함수 (호출될 때만 실행)
 * @param deps — 변경 감지 대상 (shallow 비교)
 */
export function useMapLayers(
  handleRef: React.RefObject<MapHandle | null>,
  buildLayers: () => Layer[],
  deps: unknown[],
) {
  const prevRef = useRef<unknown[]>([]);
  const rafRef = useRef(0);

  // deps 변경 시에만 레이어 갱신 (매 렌더 아닌 deps diff 기반)
  useEffect(() => {
    if (shallowEqual(prevRef.current, deps)) return;
    prevRef.current = deps;

    cancelAnimationFrame(rafRef.current);
    rafRef.current = requestAnimationFrame(() => {
      handleRef.current?.overlay?.setProps({ layers: buildLayers() });
    });
  });

  // 언마운트 시에만 레이어 초기화 — stale WebGL 참조 방지
  useEffect(() => {
    return () => {
      cancelAnimationFrame(rafRef.current);
      try { handleRef.current?.overlay?.setProps({ layers: [] }); } catch { /* finalized */ }
    };
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);
}

/**
 * Zustand store.subscribe + RAF 기반 레이어 업데이트
 * store 변경 → RAF 배치 → overlay.setProps (React 리렌더 0회)
 */
export function useStoreLayerSync<T>(
  handleRef: React.RefObject<MapHandle | null>,
  subscribe: (callback: (state: T) => void) => () => void,
  buildLayers: (state: T) => Layer[],
) {
  const rafRef = useRef(0);

  useEffect(() => {
    const unsub = subscribe((state) => {
      cancelAnimationFrame(rafRef.current);
      rafRef.current = requestAnimationFrame(() => {
        handleRef.current?.overlay?.setProps({ layers: buildLayers(state) });
      });
    });
    return () => {
      unsub();
      cancelAnimationFrame(rafRef.current);
      // 언마운트 시 레이어 초기화 — stale WebGL 참조 방지
      try { handleRef.current?.overlay?.setProps({ layers: [] }); } catch { /* finalized */ }
    };
  // buildLayers는 안정적 참조여야 함 (useCallback으로 감싸거나 모듈 스코프)
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [handleRef, subscribe]);
}

function shallowEqual(a: unknown[], b: unknown[]): boolean {
  if (a.length !== b.length) return false;
  for (let i = 0; i < a.length; i++) {
    if (!Object.is(a[i], b[i])) return false;
  }
  return true;
}
