import { bbox } from '@turf/turf'
import type { ReactNode } from 'react'
import { createPortal } from 'react-dom'
import { useMapStore } from '../../providers/map/provider'
import { DEFAULT_LASSO_TOOLS_MODES } from './const'
import { useLassoToolsState } from './provider/provider'
import type { DataType } from './types'

export interface LassoGeometryToolbarChildrenProps {
  /** Geometry type — `mask` clips layers, `draw` is annotation only. */
  type: DataType
  label: string
  locked: boolean
  onToggleType: (type: DataType) => void
  onRename: (label: string) => void
  onToggleLock: (locked: boolean) => void
  onDelete: () => void
}

export interface LassoGeometryToolbarProps {
  mapId: string
  /** Pixels above the geometry's top edge to float the toolbar. @default 12 */
  offsetY?: number
  children: (props: LassoGeometryToolbarChildrenProps) => ReactNode
}

/**
 * Renders a contextual toolbar over the geometry currently selected in **edit**
 * mode, anchored to the top-center of its bounding box and kept in sync with the
 * map via screen projection (works for both MapLibre and Google Maps basemaps).
 *
 * Render-props, like {@link LassoToolsControls}: drop a presentational toolbar
 * (e.g. `LassoGeometryToolbarUI` from `@carto/ps-react-ui`) inside and wire the
 * supplied handlers. Renders nothing unless edit mode is active, a geometry is
 * selected, and no edit gesture is in progress.
 *
 * @example
 * ```tsx
 * <LassoTools.GeometryToolbar mapId="map1">
 *   {(props) => <LassoGeometryToolbarUI {...props} />}
 * </LassoTools.GeometryToolbar>
 * ```
 */
export function LassoGeometryToolbar({
  mapId,
  offsetY = 12,
  children,
}: LassoGeometryToolbarProps) {
  const modeSelected = useLassoToolsState((s) => s.data[mapId]?.modeSelected)
  const selectedId = useLassoToolsState((s) => s.data[mapId]?.selectedId)
  const editing = useLassoToolsState((s) => s.data[mapId]?.editing)
  const value = useLassoToolsState((s) =>
    selectedId ? s.data[mapId]?.values[selectedId] : undefined,
  )
  const setValue = useLassoToolsState((s) => s.setValue)
  const setSelectedId = useLassoToolsState((s) => s.setSelectedId)

  // Subscribe to view state so the toolbar re-projects on every pan/zoom.
  const viewState = useMapStore((s) => s.viewStates[mapId])
  const getViewport = useMapStore((s) => s.getViewport)
  const getDeckInstance = useMapStore((s) => s.getDeckInstance)

  const isEdit = modeSelected === DEFAULT_LASSO_TOOLS_MODES.edit.value
  if (!isEdit || !selectedId || editing || !value) return null

  const viewport = viewState && getViewport(mapId)
  if (!viewport) return null

  const [minX, , maxX, maxY] = bbox(value.geometry)
  const [x, y] = viewport.project([(minX + maxX) / 2, maxY])
  if (x == null || y == null || isNaN(x) || isNaN(y)) return null

  const container = getDeckInstance(mapId)
    ?.getCanvas?.()
    ?.closest('.deckgl-map')
  if (!container) return null

  const id = value.id
  const properties = value.properties

  return createPortal(
    <div
      style={{
        position: 'absolute',
        left: x,
        top: y - offsetY,
        transform: 'translate(-50%, -100%)',
        zIndex: 2,
      }}
    >
      {children({
        type: value.type,
        label: properties.label,
        locked: !!properties.locked,
        onToggleType: (type) => setValue(mapId, id, { type }),
        onRename: (label) =>
          setValue(mapId, id, { properties: { ...properties, label } }),
        onToggleLock: (locked) =>
          setValue(mapId, id, { properties: { ...properties, locked } }),
        onDelete: () => {
          setValue(mapId, id)
          setSelectedId(mapId, null)
        },
      })}
    </div>,
    container,
  )
}
