import { useState } from 'react'
import {
  Box,
  IconButton,
  InputAdornment,
  MenuItem,
  OutlinedInput,
  Slider,
  Typography,
} from '@mui/material'
import { Close as CloseIcon } from '@mui/icons-material'
import { Tooltip } from '../../../components/tooltip/tooltip'
import { useLegendId, useLegendShallow, useLegendStore } from '../../stores'
import { useLegendConfig } from '../../provider'
import { useLegendRow } from '../contexts'
import { styles } from './styles'

const clampPercent = (value: number): number =>
  Math.min(100, Math.max(0, Math.round(value)))

/**
 * Opacity menu item — toggles the row's inline bar; self-hides when hidden or no `opacityControl`.
 *
 * @experimental This API is new and may change in a future release.
 */
function OpacityItem() {
  const id = useLegendId()
  const row = useLegendRow()
  const layer = useLegendShallow(id, (s) =>
    row ? s.layers[row.layerId] : undefined,
  )
  const setCollapsed = useLegendShallow(id, (s) => s.setCollapsed)
  const { labels } = useLegendConfig()

  if (!row || !layer?.visible || !layer.opacityControl) return null

  const toggle = () => {
    const next = !row.opacityOpen
    row.setOpacityOpen(next)
    if (next && layer.collapsed) setCollapsed(row.layerId, false)
  }

  return (
    <MenuItem selected={row.opacityOpen} onClick={toggle}>
      {labels.opacity}
    </MenuItem>
  )
}

/**
 * Inline opacity bar (first body child of `Legend.Row`). Slider commits on release; input commits live.
 *
 * @experimental This API is new and may change in a future release.
 */
function OpacityInline() {
  const id = useLegendId()
  const row = useLegendRow()
  const layerId = row?.layerId
  const opacity = useLegendStore(id, (s) =>
    layerId ? (s.layers[layerId]?.opacity ?? 1) : 1,
  )
  const setOpacity = useLegendStore(id, (s) => s.setOpacity)
  const { labels } = useLegendConfig()

  const [dragPercent, setDragPercent] = useState<number | null>(null)
  const [draft, setDraft] = useState<string | null>(null)

  if (!row || !layerId || !row.opacityOpen) return null

  const percent = dragPercent ?? clampPercent(opacity * 100)

  const commitPercent = (value: number): number => {
    const clamped = clampPercent(value)
    setOpacity(layerId, clamped / 100)
    return clamped
  }

  const handleInputChange = (raw: string) => {
    setDraft(raw)
    if (raw.trim() === '') return
    const parsed = Number(raw)
    if (!Number.isFinite(parsed)) return
    const clamped = commitPercent(parsed)
    if (clamped !== parsed) setDraft(String(clamped))
  }

  const handleClose = () => {
    row.setOpacityOpen(false)
    setDraft(null)
    setDragPercent(null)
  }

  return (
    <Box sx={styles.inline}>
      <Box sx={styles.inlineHeader}>
        <Typography variant='caption' fontWeight='medium' color='text.primary'>
          {labels.opacity}
        </Typography>
        <Tooltip title={labels.close}>
          <IconButton
            size='small'
            onClick={handleClose}
            aria-label={labels.close}
          >
            <CloseIcon fontSize='small' />
          </IconButton>
        </Tooltip>
      </Box>
      <Box sx={styles.inlineControls}>
        <Slider
          size='small'
          min={0}
          max={100}
          step={1}
          value={percent}
          onChange={(_, value) => {
            setDragPercent(value as number)
            setDraft(null)
          }}
          onChangeCommitted={(_, value) => {
            commitPercent(value as number)
            setDragPercent(null)
          }}
          aria-label={labels.opacity}
          sx={styles.slider}
        />
        <OutlinedInput
          size='small'
          value={draft ?? String(percent)}
          onChange={(event) => handleInputChange(event.target.value)}
          onBlur={() => setDraft(null)}
          onKeyDown={(event) => {
            if (event.key === 'Enter') (event.target as HTMLElement).blur()
          }}
          endAdornment={<InputAdornment position='end'>%</InputAdornment>}
          inputProps={{ inputMode: 'numeric', 'aria-label': labels.opacity }}
          sx={styles.input}
        />
      </Box>
    </Box>
  )
}

/**
 * Compound opacity control: `Item` in the row menu, `Inline` in the body.
 *
 * @experimental This API is new and may change in a future release.
 */
export const LegendOpacity = {
  Item: OpacityItem,
  Inline: OpacityInline,
}
