import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import type { Layer, PickingInfo } from '@deck.gl/core';
import { ScatterplotLayer, TextLayer } from '@deck.gl/layers';

import type { Development } from '../types';
import { trackEvent } from '../lib/analytics';

const COUNT_LABEL_MIN_ZOOM = 14;

export interface DevelopmentPopupInfo {
  x: number;
  y: number;
  development: Development;
}

interface UseDevelopmentLayersProps {
  developments: Development[];
  zoom: number;
  isDark: boolean;
}

function dwellingCount(d: Development): number | null {
  return d.max_dwellings ?? d.min_dwellings ?? null;
}

// Pixel radius scales with the number of homes so larger schemes read as larger
// dots. sqrt compresses the range (a 5,000-home site shouldn't dwarf a 20-home
// one) and the result is clamped to a sane band; sites with no count get the floor.
function radiusForDwellings(d: Development): number {
  const count = dwellingCount(d) ?? 0;
  return Math.min(22, 4 + Math.sqrt(Math.max(count, 0)) * 0.8);
}

/**
 * Renders development sites as blue point markers (a soft shadow, a pin, and a
 * dwelling-count label at higher zoom). Sparse data — the endpoint caps the
 * viewport at a few thousand points — so no clustering is needed, unlike the
 * listings layer. Hover shows a popup; click opens the planning record.
 */
export function useDevelopmentLayers({ developments, zoom, isDark }: UseDevelopmentLayersProps) {
  const [popupInfo, setPopupInfo] = useState<DevelopmentPopupInfo | null>(null);

  // A fresh fetch returns a new array; the previously hovered object is gone.
  useEffect(() => {
    setPopupInfo(null);
  }, [developments]);

  const points = useMemo(
    () => developments.filter((d) => Number.isFinite(d.lat) && Number.isFinite(d.lon)),
    [developments]
  );

  const handleHover = useCallback((info: PickingInfo<Development>) => {
    if (info.object && info.x !== undefined && info.y !== undefined) {
      setPopupInfo({ x: info.x, y: info.y, development: info.object });
    } else {
      setPopupInfo(null);
    }
  }, []);

  const handleClick = useCallback((info: PickingInfo<Development>) => {
    const url = info.object?.url;
    if (!url) return;
    trackEvent('Development Site Click', { url });
    window.open(url, '_blank', 'noopener,noreferrer');
  }, []);

  const handleHoverRef = useRef(handleHover);
  handleHoverRef.current = handleHover;
  const stableHover = useCallback(
    (info: PickingInfo<Development>) => handleHoverRef.current(info),
    []
  );

  const handleClickRef = useRef(handleClick);
  handleClickRef.current = handleClick;
  const stableClick = useCallback(
    (info: PickingInfo<Development>) => handleClickRef.current(info),
    []
  );

  const shadowLayer = useMemo(
    () =>
      new ScatterplotLayer<Development>({
        id: 'development-shadow',
        data: points,
        getPosition: (d) => [d.lon, d.lat],
        getRadius: (d) => radiusForDwellings(d) + 1.5,
        radiusUnits: 'pixels',
        getFillColor: isDark ? [0, 0, 0, 80] : [0, 0, 0, 40],
        pickable: false,
      }),
    [points, isDark]
  );

  const pinLayer = useMemo(
    () =>
      new ScatterplotLayer<Development>({
        id: 'development-pin',
        data: points,
        getPosition: (d) => [d.lon, d.lat],
        getRadius: radiusForDwellings,
        radiusUnits: 'pixels',
        getFillColor: [37, 99, 235, 235],
        getLineColor: [255, 255, 255, 255],
        getLineWidth: 1.5,
        lineWidthUnits: 'pixels',
        stroked: true,
        pickable: true,
        autoHighlight: true,
        highlightColor: [29, 228, 195, 220],
        onHover: stableHover,
        onClick: stableClick,
      }),
    [points, stableHover, stableClick]
  );

  const countLabelLayer = useMemo(() => {
    if (zoom < COUNT_LABEL_MIN_ZOOM) return null;
    const labeled = points.filter((d) => (dwellingCount(d) ?? 0) > 0);
    return new TextLayer<Development>({
      id: 'development-count',
      data: labeled,
      getPosition: (d) => [d.lon, d.lat],
      getText: (d) => String(dwellingCount(d) ?? ''),
      getSize: 11,
      getPixelOffset: (d) => [0, -(radiusForDwellings(d) + 6)],
      getColor: isDark ? [191, 219, 254, 240] : [30, 58, 138, 240],
      fontFamily: 'Inter, system-ui, sans-serif',
      fontWeight: 700,
      getTextAnchor: 'middle',
      getAlignmentBaseline: 'bottom',
      outlineWidth: 3,
      outlineColor: isDark ? [10, 10, 10, 220] : [255, 255, 255, 230],
      fontSettings: { sdf: true },
      sizeUnits: 'pixels',
      sizeMinPixels: 9,
      sizeMaxPixels: 13,
      pickable: false,
    });
  }, [points, zoom, isDark]);

  const developmentLayers = useMemo(() => {
    const layers: Layer[] = [shadowLayer, pinLayer];
    if (countLabelLayer) layers.push(countLabelLayer);
    return layers;
  }, [shadowLayer, pinLayer, countLabelLayer]);

  const clearDevelopmentPopup = useCallback(() => setPopupInfo(null), []);

  return { developmentLayers, developmentPopup: popupInfo, clearDevelopmentPopup };
}
