import {
  ArrowDropDown,
  LayersOutlined,
  MoreVertOutlined,
} from '@mui/icons-material'
import {
  Box,
  Divider,
  IconButton,
  ListItemIcon,
  ListItemText,
  Menu,
  MenuItem,
  Paper,
  SvgIcon,
  ToggleButton,
  Typography,
  type SxProps,
  type Theme,
} from '@mui/material'
import deepmerge from 'deepmerge'
import {
  useMemo,
  useState,
  type ComponentProps,
  type JSX,
  type MouseEvent,
  type PropsWithChildren,
  type ReactNode,
} from 'react'
import type { PickDeep } from 'type-fest'
import { Tooltip } from '../tooltip/tooltip'
import type { LassoToolsModes } from '../types'
import { LassoToolsUIChip } from './chip'
import { LASSO_TOOLS_LABELS } from './const'
import { styles } from './styles'
import type { LassoToolsComponentProps, OptionsChildrenProps } from './types'

const EMPTY_VALUES: LassoToolsComponentProps['values'] = []
const EMPTY_PAPER_PROPS: NonNullable<LassoToolsComponentProps['PaperProps']> =
  {}

/**
 * Provides a comprehensive floating interface for spatial selection and drawing operations on maps, supporting polygon, rectangle, circle, freehand lasso, and edit modes.
 *
 * @remarks
 * Supports customizable slots for action, options, chips, and secondary actions via compound component pattern (e.g., `LassoToolsUI.Action`, `LassoToolsUI.Chips`).
 *
 * @example
 * ```tsx
 * <LassoToolsUI
 *   enabled={enabled}
 *   values={spatialFilters}
 *   modes={modes}
 *   modesMapping={modesMapping}
 *   modeSelected={modeSelected}
 *   onActionToggle={setEnabled}
 *   onChangeMode={setModeSelected}
 *   onChipToggle={handleChipToggle}
 *   onDelete={handleDelete}
 * />
 * ```
 */
export function LassoToolsUI({
  enabled,
  values = EMPTY_VALUES,
  actionProps,
  chipProps,
  labels,
  modes,
  modesMapping,
  modeSelected,
  PaperProps: { sx, ...PaperProps } = EMPTY_PAPER_PROPS,
  onActionToggle,
  onChipToggle,
  onDelete,
  onChangeMode,
  onAllChipToggle,
  onAllDelete,
  onLayerFilters,
  ChipsSlot = LassoToolsUI.Chips,
  ActionSlot = LassoToolsUI.Action,
  SecondaryActionsSlot = LassoToolsUI.SecondaryActions,
  OptionsSlot = LassoToolsUI.Options,
}: LassoToolsComponentProps): JSX.Element {
  const slotArgs = {
    enabled,
    values,
    actionProps,
    chipProps,
    labels,
    modes,
    modesMapping,
    modeSelected,
    PaperProps,
    onActionToggle,
    onChipToggle,
    onDelete,
    onChangeMode,
    onAllChipToggle,
    onAllDelete,
    onLayerFilters,
  }
  return (
    <Paper
      sx={{
        ...styles.container,
        ...sx,
      }}
      {...PaperProps}
    >
      {!!ActionSlot && <ActionSlot {...slotArgs} />}
      {!!OptionsSlot && (
        <Options>
          {(props) => {
            return <OptionsSlot {...slotArgs} {...props} />
          }}
        </Options>
      )}
      {!!ChipsSlot && <ChipsSlot {...slotArgs} />}
      {!!SecondaryActionsSlot && <SecondaryActionsSlot {...slotArgs} />}
    </Paper>
  )
}

function LassoToolsUIAction({
  actionProps,
  labels,
  enabled,
  children,
  onActionToggle,
}: PropsWithChildren<
  Pick<
    LassoToolsComponentProps,
    'actionProps' | 'enabled' | 'onActionToggle'
  > & {
    labels?: PickDeep<LassoToolsComponentProps['labels'], 'action'>['action']
  }
>) {
  const actionState = enabled ? 'active' : 'inactive'
  const actionLabel =
    labels?.tooltip?.[actionState] ??
    LASSO_TOOLS_LABELS.action.tooltip[actionState]

  return (
    <Tooltip
      title={actionLabel}
      placement='right'
      {...actionProps?.TooltipProps}
    >
      <ToggleButton
        value='toggle'
        sx={styles.actions.icon}
        onClick={() => onActionToggle(!enabled)}
        aria-label={actionLabel}
        selected={enabled}
      >
        {children}
      </ToggleButton>
    </Tooltip>
  )
}

const EMPTY_TRIGGER_PROPS: { Icon?: ReactNode; sx?: SxProps<Theme> } = {}

function Options({
  TriggerProps = EMPTY_TRIGGER_PROPS,
  MenuProps,
  children,
}: {
  TriggerProps?: {
    Icon?: ReactNode
    sx?: SxProps<Theme>
  }
  MenuProps?: Partial<ComponentProps<typeof Menu>>
  children: (props: OptionsChildrenProps) => JSX.Element
}) {
  const { Icon, sx } = TriggerProps
  const IconNode = Icon ?? <ArrowDropDown />

  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)

  const open = Boolean(anchorEl)

  const handleToggle = (event: MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(event.currentTarget)
  }

  const handleClose = () => {
    setAnchorEl(null)
  }

  return (
    <>
      <IconButton
        sx={{
          ...styles.options.icon,
          ...sx,
        }}
        onClick={handleToggle}
      >
        {IconNode}
      </IconButton>
      <Menu
        id='lasso-menu'
        anchorEl={anchorEl}
        open={open}
        onClose={handleClose}
        MenuListProps={{
          'aria-labelledby': 'lasso-button',
          sx: styles.options.menu,
        }}
        {...MenuProps}
      >
        <div>
          {children({
            onClose: handleClose,
          })}
        </div>
      </Menu>
    </>
  )
}

function ModeList({
  data,
  labels,
  children,
}: {
  labels?: PickDeep<LassoToolsComponentProps['labels'], 'options'>['options']
  data: LassoToolsComponentProps['modes'][keyof LassoToolsModes][]
  children: ReactNode
}) {
  if (data.length <= 1) {
    return null
  }

  const modeTitle = labels?.mode?.title ?? LASSO_TOOLS_LABELS.options.mode.title

  return (
    <>
      <Typography
        variant='subtitle2'
        color='text.secondary'
        sx={styles.options.title}
      >
        {modeTitle}
      </Typography>
      {children}
    </>
  )
}

function OptionsList({
  data,
  modeSelected,
  labels,
  onChangeMode,
  onClose,
}: Required<Pick<LassoToolsComponentProps, 'modeSelected' | 'onChangeMode'>> & {
  labels?: PickDeep<LassoToolsComponentProps['labels'], 'options'>['options']
  data: (LassoToolsComponentProps['modes'] &
    LassoToolsComponentProps['modesMapping'])[keyof LassoToolsModes][]
  onClose: OptionsChildrenProps['onClose']
}) {
  const handleClick = (
    e: MouseEvent<HTMLLIElement>,
    value: NonNullable<(typeof data)[number]>['value'],
  ) => {
    e.preventDefault()
    onChangeMode?.(value)
    onClose()
  }

  return data
    .filter((mode): mode is NonNullable<typeof mode> => Boolean(mode))
    .map((mode) => {
      const options =
        labels?.mode?.options ?? LASSO_TOOLS_LABELS.options.mode.options
      const label = options[mode.value]

      return (
        <MenuItem
          key={mode.value}
          disabled={!!mode.disabled}
          onClick={(e) => handleClick(e, mode.value)}
          selected={mode.value === modeSelected}
        >
          <ListItemIcon sx={styles.options.icons}>
            <SvgIcon>{mode.icon}</SvgIcon>
          </ListItemIcon>
          <ListItemText>{label}</ListItemText>
        </MenuItem>
      )
    })
}

function SecondaryActionsWrapper({
  values,
  labels,
  onAllChipToggle,
  onAllDelete,
}: Partial<
  Omit<
    LassoToolsComponentProps,
    'ChipsSlot' | 'ActionsSlot' | 'OptionsSlot' | 'SecondaryActionsSlot'
  >
>) {
  if (!values?.length || values.length <= 1) {
    return null
  }

  const hasVisible = values?.some((value) => value.visible)

  const toggleAllLabel = hasVisible
    ? (labels?.actions?.toggleAll?.active ??
      LASSO_TOOLS_LABELS.actions.toggleAll.active)
    : (labels?.actions?.toggleAll?.inactive ??
      LASSO_TOOLS_LABELS.actions.toggleAll.inactive)

  const deleteAllLabel =
    labels?.actions?.deleteAll ?? LASSO_TOOLS_LABELS.actions.deleteAll

  return (
    <>
      <Divider orientation='vertical' flexItem />
      <Options
        TriggerProps={{ Icon: <MoreVertOutlined />, sx: styles.options.more }}
        MenuProps={{
          anchorOrigin: {
            vertical: 'bottom',
            horizontal: 'right',
          },
          transformOrigin: {
            vertical: 'top',
            horizontal: 'right',
          },
        }}
      >
        {(props) => {
          return (
            <>
              <MenuItem
                color='inherit'
                onClick={() => {
                  onAllChipToggle?.()
                  props.onClose()
                }}
              >
                {toggleAllLabel}
              </MenuItem>
              <Divider />
              <MenuItem
                color='error'
                onClick={() => {
                  onAllDelete?.()
                  props.onClose()
                }}
              >
                <Typography variant='body2' color='error'>
                  {deleteAllLabel}
                </Typography>
              </MenuItem>
            </>
          )
        }}
      </Options>
    </>
  )
}

LassoToolsUI.Action = function ActionsWrapper({
  modes,
  modesMapping,
  modeSelected,
  actionProps,
  enabled,
  labels,
  onActionToggle,
}: Partial<
  Omit<
    LassoToolsComponentProps,
    'ChipsSlot' | 'ActionsSlot' | 'OptionsSlot' | 'SecondaryActionsSlot'
  >
> & {
  labels?: PickDeep<LassoToolsComponentProps['labels'], 'actions'>['actions']
}) {
  const data = useMemo(() => {
    return deepmerge(modes ?? {}, modesMapping ?? {})
  }, [modes, modesMapping])

  const handleToggle: LassoToolsComponentProps['onActionToggle'] = (data) => {
    return onActionToggle?.(data)
  }

  const modeExists = modeSelected ? modeSelected in (modes ?? {}) : false

  const mode = (
    modeExists ? modeSelected : Object.keys(data)[0]
  ) as keyof typeof data

  const modeSelectedValue = data[mode]

  return (
    <LassoToolsUIAction
      actionProps={actionProps}
      labels={labels?.action}
      enabled={enabled ?? false}
      onActionToggle={handleToggle}
    >
      {modeSelectedValue?.icon}
    </LassoToolsUIAction>
  )
}

LassoToolsUI.Chips = function ChipsWrapper({
  values,
  labels,
  chipProps,
  onDelete,
  onChipToggle,
  onActionToggle,
}: Partial<
  Omit<
    LassoToolsComponentProps,
    'ChipsSlot' | 'ActionsSlot' | 'OptionsSlot' | 'SecondaryActionsSlot'
  >
>) {
  const handleDelete: LassoToolsComponentProps['onDelete'] = (valueId) => {
    onActionToggle?.(false)
    return onDelete?.(valueId)
  }

  return (
    !!values?.length && (
      <Box sx={styles.chip.container}>
        {values?.map((value) => (
          <LassoToolsUIChip
            key={value.id}
            value={value}
            labels={labels?.chip}
            chipProps={chipProps}
            onDelete={handleDelete}
            onChipToggle={(id, data) => onChipToggle?.(id, data)}
          />
        ))}
      </Box>
    )
  )
}

LassoToolsUI.SecondaryActions = SecondaryActionsWrapper

LassoToolsUI.Options = function OptionsWrapper({
  values,
  modes,
  modesMapping,
  modeSelected,
  labels,
  onChangeMode,
  ...props
}: Partial<
  Omit<
    LassoToolsComponentProps,
    'ChipsSlot' | 'ActionsSlot' | 'OptionsSlot' | 'SecondaryActionsSlot'
  >
> &
  OptionsChildrenProps) {
  const data = useMemo(() => {
    return deepmerge(modes ?? {}, modesMapping ?? {})
  }, [modes, modesMapping])

  const { edit, ..._modes } = data

  const modeExists = modeSelected ? modeSelected in (modes ?? {}) : false

  const mode = (
    modeExists ? modeSelected : Object.keys(data)[0]
  ) as keyof typeof data

  const modesValues = Object.values(_modes)

  const handleChangeMode: LassoToolsComponentProps['onChangeMode'] = (data) => {
    return onChangeMode?.(data)
  }

  return (
    <>
      <ModeList data={modesValues} labels={labels?.options}>
        <OptionsList
          {...props}
          labels={labels?.options}
          data={modesValues}
          modeSelected={mode}
          onChangeMode={handleChangeMode}
        />
      </ModeList>
      {!!edit && (
        <>
          <Divider />
          <OptionsList
            {...props}
            labels={labels?.options}
            data={[
              {
                ...edit,
                disabled: !values?.length,
              },
            ]}
            modeSelected={mode}
            onChangeMode={handleChangeMode}
          />
        </>
      )}
      {!!props.onLayerFilters && (
        <>
          <Divider />
          <MenuItem
            onClick={() => {
              props.onLayerFilters?.()
              props.onClose()
            }}
          >
            <ListItemIcon sx={styles.options.icons}>
              <LayersOutlined />
            </ListItemIcon>
            <ListItemText>
              {labels?.options?.layerFilters ?? 'Layer filters'}
            </ListItemText>
          </MenuItem>
        </>
      )}
    </>
  )
}
