import { useCallback, useMemo, type ReactNode } from 'react'
import { useShallow } from 'zustand/shallow'
import { useLayerStore } from '../../providers/layers/store'
import { useMapStore } from '../../providers/map/provider'
import { MAP_MEASURE_LAYER_ID } from '../measurement-tools/const'
import { MAP_LASSO_TOOLS_LAYER_ID, MAP_MASK_LAYER_ID } from './const'
import { useLassoToolsState } from './provider/provider'

const EXCEPTED_LAYER_IDS = new Set([
  MAP_LASSO_TOOLS_LAYER_ID,
  MAP_MASK_LAYER_ID,
  MAP_MEASURE_LAYER_ID,
])

export interface LassoLayerFilterEntry {
  id: string
  label: string
  /** Whether the mask currently clips this layer. */
  checked: boolean
}

export interface LassoLayerFiltersChildrenProps {
  layers: LassoLayerFilterEntry[]
  /** Commit a new selection (e.g. the dialog's Apply): writes `maskLayerIds`. */
  onApply: (selectedIds: string[]) => void
  /** True when at least one mask geometry exists (the dialog is only relevant then). */
  maskExists: boolean
}

export interface LassoLayerFiltersProps {
  mapId: string
  /** Extra layer ids to omit from the list (e.g. base/reference layers). */
  excludeLayerIds?: string[]
  children: (props: LassoLayerFiltersChildrenProps) => ReactNode
}

/**
 * Render-props member (composed like {@link LassoToolsControls}) exposing the
 * map's data layers and the global per-map mask/layer selection. Drop a
 * presentational panel (e.g. `LassoLayerFiltersUI` from `@carto/ps-react-ui`)
 * inside and wire the supplied props.
 *
 * The selection is stored as `maskLayerIds` (`null` = all layers). Applying a
 * selection that covers every layer normalizes to `null`, so layers added later
 * stay masked by default.
 *
 * @example
 * ```tsx
 * <LassoTools.LayerFilters mapId="map1">
 *   {({ layers, onApply, maskExists }) => (
 *     <LassoLayerFiltersUI
 *       open={open}
 *       onClose={close}
 *       layers={layers}
 *       onApply={onApply}
 *     />
 *   )}
 * </LassoTools.LayerFilters>
 * ```
 */
export function LassoLayerFilters({
  mapId,
  excludeLayerIds,
  children,
}: LassoLayerFiltersProps) {
  const layerIds = useMapStore(
    useShallow((state) =>
      (state.maps[mapId]?.layers ?? []).filter(
        (id) => !EXCEPTED_LAYER_IDS.has(id) && !excludeLayerIds?.includes(id),
      ),
    ),
  )
  const maskLayerIds = useLassoToolsState(
    (state) => state.data[mapId]?.maskLayerIds,
  )
  const setMaskLayerIds = useLassoToolsState((state) => state.setMaskLayerIds)
  const maskExists = useLassoToolsState((state) =>
    Object.values(state.data[mapId]?.values ?? {}).some(
      (value) => value.type === 'mask',
    ),
  )

  const layers = useMemo(() => {
    const effective = maskLayerIds ?? layerIds
    const getLayer = useLayerStore.getState().getLayer
    return layerIds.map((id) => ({
      id,
      label: getLayer(id)?.name ?? id,
      checked: effective.includes(id),
    }))
  }, [layerIds, maskLayerIds])

  const onApply = useCallback(
    (selectedIds: string[]) => {
      // Keep only known layers; all selected → `null` so future layers stay masked.
      const next = layerIds.filter((id) => selectedIds.includes(id))
      setMaskLayerIds(mapId, next.length === layerIds.length ? null : next)
    },
    [layerIds, mapId, setMaskLayerIds],
  )

  return children({ layers, onApply, maskExists })
}
