import { CloseOutlined } from '@mui/icons-material'
import { Box, Divider, Grow, IconButton, Paper } from '@mui/material'
import { isValidElement, useState, type JSX, type MouseEvent } from 'react'
import { Tooltip } from '../../components/tooltip/tooltip'
import { styles } from './styles'
import type { ToolbarActionsProps } from './types'
import { WidgetOptions } from '@carto/meridian-ds/custom-icons'

const DEFAULT_LABELS = {
  trigger: 'More actions',
  close: 'Close',
}

/**
 * A floating toolbar component that displays action buttons.
 * Shows a trigger button when there are more visible actions than the visibleCount,
 * which expands to reveal all actions with a close button.
 * When visibleCount is undefined, all actions are shown without overflow.
 *
 * Children with the `data-toolbar-hidden` attribute are ignored when counting
 * visible actions but are included in the preview. This is useful for elements
 * like Dividers that should appear between actions but not count toward the limit.
 *
 * Example: With visibleCount=2 and children [Action1, Divider, Action2, Divider, Action3]:
 * - Preview shows: Action1, Divider, Action2 (2 visible actions + divider)
 * - Hidden: Divider, Action3
 */
export function ToolbarActions({
  children: _children,
  visibleCount,
  direction,
  labels,
  sx,
  IconButtonProps,
  TooltipProps,
}: ToolbarActionsProps): JSX.Element | null {
  const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)

  const triggerLabel = labels?.trigger ?? DEFAULT_LABELS.trigger
  const closeLabel = labels?.close ?? DEFAULT_LABELS.close

  const children = (Array.isArray(_children) ? _children : [_children]).filter(
    Boolean,
  )

  // Don't render if no actions
  if (children.length === 0) {
    return null
  }

  // Filter out children with data-toolbar-hidden attribute for counting
  const visibleChildren = children.filter(
    (child) =>
      !(
        isValidElement<{ 'data-toolbar-hidden'?: boolean }>(child) &&
        child.props['data-toolbar-hidden']
      ),
  )

  // Show trigger only when visibleCount is defined and there are more visible actions
  const showTrigger =
    visibleCount !== undefined && visibleChildren.length > visibleCount

  const handleToggle = (e: MouseEvent<HTMLElement>) => {
    setAnchorEl((prev) => (prev ? null : e.currentTarget))
  }

  // Build preview by including hidden elements but only counting visible ones
  const childrenPreview =
    visibleCount === undefined
      ? children
      : children.reduce<{ result: React.ReactNode[]; count: number }>(
          (acc, child) => {
            // Stop if we've already collected enough visible actions
            if (acc.count >= visibleCount) {
              return acc
            }

            const isHidden =
              isValidElement<{ 'data-toolbar-hidden'?: boolean }>(child) &&
              child.props['data-toolbar-hidden']

            return {
              result: [...acc.result, child],
              count: acc.count + (isHidden ? 0 : 1),
            }
          },
          { result: [], count: 0 },
        ).result

  const width = anchorEl?.getBoundingClientRect().width

  return (
    <Box
      sx={{
        ...styles.root,
        ...sx,
        flexDirection: direction === 'left' ? 'row-reverse' : 'row',
      }}
    >
      {/* Trigger button - only shown when 3+ actions */}
      {showTrigger && (
        <>
          <Tooltip
            title={anchorEl ? closeLabel : triggerLabel}
            placement='top'
            {...TooltipProps}
          >
            <IconButton
              size='small'
              aria-label={anchorEl ? closeLabel : triggerLabel}
              onClick={handleToggle}
              sx={styles.triggerButton}
              data-active={!!anchorEl}
              {...IconButtonProps}
            >
              {anchorEl ? <CloseOutlined /> : <WidgetOptions />}
            </IconButton>
          </Tooltip>
          <Divider orientation='vertical' flexItem />
        </>
      )}

      <Box sx={styles.preview}>{childrenPreview}</Box>

      {/* Actions container - shown only when expanded (trigger is active) */}
      <Grow in={!!anchorEl}>
        <Paper
          sx={{
            ...styles.actionsContainer,
            transform: `translate3d(${
              direction === 'left' ? `-${width ?? 0}px` : `${width ?? 0}px`
            }, 0, 0) !important`,
            marginLeft: ({ spacing }) =>
              direction === 'left' ? 0 : spacing(0.25),
            marginRight: ({ spacing }) =>
              direction === 'left' ? spacing(0.25) : 0,
          }}
        >
          {children}
        </Paper>
      </Grow>
    </Box>
  )
}
