import { Close, Search } from '@mui/icons-material'
import {
  Box,
  Button,
  Checkbox,
  Dialog,
  DialogActions,
  DialogContent,
  DialogTitle,
  Divider,
  IconButton,
  InputBase,
  List,
  ListItemButton,
  ListItemIcon,
  ListItemText,
  Typography,
  type SxProps,
  type Theme,
} from '@mui/material'
import { useId, useState, type JSX } from 'react'
import type { LassoLayerFiltersUIProps } from './types'

const titleSx = {
  display: 'flex',
  alignItems: 'center',
  justifyContent: 'space-between',
  gap: 1,
  p: 2,
} satisfies SxProps<Theme>

// One rounded group on the grey "default" background, holding the search row,
// a divider, and the scrollable list (Figma node 5960-14780).
const groupSx = {
  borderRadius: ({ spacing }) => spacing(0.5),
  overflow: 'hidden',
  backgroundColor: ({ palette }) => palette.background.default,
} satisfies SxProps<Theme>

const searchSx = {
  display: 'flex',
  alignItems: 'center',
  gap: 1,
  height: 32,
  px: 1.5,
  color: ({ palette }) => palette.text.secondary,
} satisfies SxProps<Theme>

const rowSx = {
  px: 2,
  py: 0.5,
} satisfies SxProps<Theme>

const checkboxSlotSx = {
  minWidth: 0,
  mr: 1,
} satisfies SxProps<Theme>

/**
 * "Layer filters" dialog: choose which map layers the lasso mask clips. The
 * selection is a draft committed on Apply (Cancel/close discards it). Wire it
 * to the lasso store via `LassoTools.LayerFilters` in `@carto/ps-react-maps`,
 * and open it from the drawing-tool menu's "Layer filters" item
 * (`LassoToolsUI` `onLayerFilters`).
 */
export function LassoLayerFiltersUI({
  open,
  onClose,
  layers,
  onApply,
  labels,
  DialogProps,
}: LassoLayerFiltersUIProps): JSX.Element {
  const labelId = useId()
  const [draft, setDraft] = useState<Set<string>>(() => new Set())
  const [search, setSearch] = useState('')

  // Reset the draft from the committed selection when the dialog opens, by
  // adjusting state during render (the React-recommended pattern — no effect,
  // and later `layers` changes don't discard in-progress edits).
  const [wasOpen, setWasOpen] = useState(false)
  if (open !== wasOpen) {
    setWasOpen(open)
    if (open) {
      setDraft(new Set(layers.filter((l) => l.checked).map((l) => l.id)))
      setSearch('')
    }
  }

  const title = labels?.title ?? 'Layer Filters'
  const description =
    labels?.description ??
    'Select the layers you want to filter with this polygon'
  const allSelectedLabel = labels?.allSelected ?? 'All selected'
  const empty = labels?.empty ?? 'No layers to filter'

  const allChecked = layers.length > 0 && layers.every((l) => draft.has(l.id))
  const someChecked = layers.some((l) => draft.has(l.id))

  const toggle = (id: string) =>
    setDraft((prev) => {
      const next = new Set(prev)
      if (next.has(id)) next.delete(id)
      else next.add(id)
      return next
    })

  const toggleAll = () =>
    setDraft(allChecked ? new Set() : new Set(layers.map((l) => l.id)))

  const handleApply = () => {
    // Emit ids in `layers` order (stable, diff-friendly), not Set-insertion order.
    onApply(layers.filter((l) => draft.has(l.id)).map((l) => l.id))
    onClose()
  }

  const normalized = search.trim().toLowerCase()
  const filtered = normalized
    ? layers.filter((l) => l.label.toLowerCase().includes(normalized))
    : layers

  return (
    <Dialog
      open={open}
      onClose={onClose}
      maxWidth='xs'
      fullWidth
      {...DialogProps}
    >
      <DialogTitle component='div' sx={titleSx}>
        <Typography variant='subtitle1' fontWeight={500}>
          {title}
        </Typography>
        <IconButton aria-label='close' size='small' onClick={onClose}>
          <Close fontSize='small' />
        </IconButton>
      </DialogTitle>

      <DialogContent sx={{ pt: 1, px: 2, pb: 3 }}>
        <Typography variant='body2' color='text.secondary' sx={{ mb: 2 }}>
          {description}
        </Typography>

        <Box sx={groupSx}>
          <Box sx={searchSx}>
            <Search sx={{ fontSize: 18 }} />
            <InputBase
              fullWidth
              placeholder={labels?.search ?? 'Search layers'}
              inputProps={{ 'aria-label': labels?.search ?? 'Search layers' }}
              value={search}
              onChange={(e) => setSearch(e.target.value)}
              sx={{ fontSize: 13, color: 'text.primary' }}
            />
          </Box>
          <Divider />
          <List
            component='div'
            role='list'
            disablePadding
            sx={{
              maxHeight: 240,
              overflowY: 'auto',
              // MUI gives dividers inside a List an 8px vertical margin; the
              // "All selected" separator should sit flush like the rows.
              '& .MuiDivider-root': { my: 0 },
            }}
          >
            {/* "All selected" master row only makes sense over the full
                list — hide it while a search is narrowing the results. */}
            {!normalized && (
              <>
                <ListItemButton dense sx={rowSx} onClick={toggleAll}>
                  <ListItemIcon sx={checkboxSlotSx}>
                    <Checkbox
                      edge='start'
                      tabIndex={-1}
                      disableRipple
                      sx={{ p: 0.5 }}
                      checked={allChecked}
                      indeterminate={!allChecked && someChecked}
                      inputProps={{ 'aria-labelledby': `${labelId}-all` }}
                    />
                  </ListItemIcon>
                  <ListItemText
                    id={`${labelId}-all`}
                    primary={allSelectedLabel}
                    primaryTypographyProps={{ variant: 'body2' }}
                  />
                </ListItemButton>
                <Divider />
              </>
            )}
            {filtered.length === 0 ? (
              <Typography variant='body2' color='text.secondary' sx={{ p: 2 }}>
                {empty}
              </Typography>
            ) : (
              filtered.map((layer) => (
                <ListItemButton
                  key={layer.id}
                  dense
                  sx={rowSx}
                  onClick={() => toggle(layer.id)}
                >
                  <ListItemIcon sx={checkboxSlotSx}>
                    <Checkbox
                      edge='start'
                      tabIndex={-1}
                      disableRipple
                      sx={{ p: 0.5 }}
                      checked={draft.has(layer.id)}
                      inputProps={{
                        'aria-labelledby': `${labelId}-${layer.id}`,
                      }}
                    />
                  </ListItemIcon>
                  <ListItemText
                    id={`${labelId}-${layer.id}`}
                    primary={layer.label}
                    primaryTypographyProps={{
                      variant: 'body2',
                      noWrap: true,
                      title: layer.label,
                    }}
                  />
                </ListItemButton>
              ))
            )}
          </List>
        </Box>
      </DialogContent>

      <DialogActions sx={{ p: 2 }}>
        <Button onClick={onClose}>{labels?.cancel ?? 'Cancel'}</Button>
        <Button variant='contained' onClick={handleApply}>
          {labels?.apply ?? 'Apply'}
        </Button>
      </DialogActions>
    </Dialog>
  )
}
