import { useEffect } from 'react'
import { IconButton } from '@mui/material'
import {
  VisibilityOutlined as VisibilityIcon,
  VisibilityOffOutlined as VisibilityOffIcon,
} from '@mui/icons-material'
import { Tooltip } from '../../../components/tooltip/tooltip'
import {
  selectGroupVisibility,
  useLegendId,
  useLegendShallow,
  useLegendStore,
  type LegendVisibility,
} from '../../stores'
import { useLegendConfig } from '../../provider'
import type { LegendLabels } from '../../provider/labels'
import { VisibilityIndeterminateIcon } from '../icons'
import { useLegendGroupId, useLegendRow } from '../contexts'

const ICONS = {
  visible: VisibilityIcon,
  hidden: VisibilityOffIcon,
  mixed: VisibilityIndeterminateIcon,
} as const

export type LegendVisibilityToggleProps = {
  /**
   * Override the aria labels (defaults: the layer pair in rows and controlled
   * mode, the group pair in group headers) — e.g.
   * `labels={{ hideLayer: 'Hide section', showLayer: 'Show section' }}`.
   */
  labels?: Pick<LegendLabels, 'hideLayer' | 'showLayer'>
} & (
  | {
      /**
       * Controlled mode: current state — renders the matching eye (`mixed` →
       * the indeterminate icon). Use it for element kinds beyond layer/group.
       */
      visibility: LegendVisibility
      /**
       * Controlled mode: called with the **next** visibility (`hidden` →
       * `true`; `visible`/`mixed` → `false`) — mirrors the store setters.
       */
      onToggle: (visible: boolean) => void
    }
  | { visibility?: never; onToggle?: never }
)

/**
 * Agnostic visibility eye. Pass `visibility` + `onToggle` to control it for
 * any element kind (no context needed); without them it wires itself to the
 * nearest context — the layer eye inside a `Legend.Row`, the tri-state group
 * eye (cascading to every member) inside a `Legend.Group`. Pinned `.active`
 * in the hover-fade group while not fully visible. Renders nothing without
 * props or context (dev-only warning).
 *
 * @experimental This API is new and may change in a future release.
 */
export function LegendVisibilityToggle({
  visibility,
  onToggle,
  labels,
}: LegendVisibilityToggleProps) {
  const id = useLegendId()
  const row = useLegendRow()
  const groupId = useLegendGroupId()
  const { labels: configLabels } = useLegendConfig()

  const controlled = visibility !== undefined && onToggle !== undefined

  const layerVisible = useLegendStore(id, (s) =>
    !controlled && row ? (s.layers[row.layerId]?.visible ?? true) : true,
  )
  const groupVisibility = useLegendShallow(id, (s) =>
    !controlled && !row && groupId
      ? selectGroupVisibility(s, groupId)
      : 'visible',
  )
  const setVisibility = useLegendStore(id, (s) => s.setVisibility)
  const setGroupVisibility = useLegendStore(id, (s) => s.setGroupVisibility)

  useEffect(() => {
    if (!controlled && !row && !groupId) {
      // eslint-disable-next-line no-console
      console.warn(
        'Legend.VisibilityToggle needs `visibility`/`onToggle` props or a Legend.Row / Legend.Group context.',
      )
    }
  }, [controlled, row, groupId])

  // Resolve the tri-state + handler + label pair for the three modes.
  let state: LegendVisibility
  let handleToggle: () => void
  let labelPair: Pick<LegendLabels, 'hideLayer' | 'showLayer'> | undefined =
    labels

  if (controlled) {
    state = visibility
    handleToggle = () => onToggle(visibility === 'hidden')
  } else if (row) {
    state = layerVisible ? 'visible' : 'hidden'
    handleToggle = () => setVisibility(row.layerId, !layerVisible)
  } else if (groupId) {
    state = groupVisibility
    handleToggle = () =>
      setGroupVisibility(groupId, groupVisibility === 'hidden')
    labelPair = labels ?? {
      hideLayer: configLabels.hideGroup,
      showLayer: configLabels.showGroup,
    }
  } else {
    return null
  }

  const { hideLayer, showLayer } = labelPair ?? configLabels
  const EyeIcon = ICONS[state]
  const label = state === 'hidden' ? showLayer : hideLayer

  return (
    // Tooltip clones the button (no wrapper element), so the hover-fade
    // group's direct-child rules and the `.active` pin keep working.
    <Tooltip title={label}>
      <IconButton
        size='small'
        className={state === 'visible' ? undefined : 'active'}
        onClick={handleToggle}
        aria-label={label}
      >
        <EyeIcon fontSize='small' />
      </IconButton>
    </Tooltip>
  )
}
