import { useCallback, useState, type ComponentType } from 'react'
import {
  CircularProgress,
  IconButton,
  ListItemIcon,
  ListItemText,
  Menu,
  MenuItem,
  type SvgIconProps,
} from '@mui/material'
import { FileDownload as DownloadIcon } from '@mui/icons-material'
import { Tooltip } from '../../../components'
import { triggerLinkDownload } from './exports'
import type { DownloadItem } from './types'
import { DEFAULT_DOWNLOAD_LABELS, type DownloadLabels } from './labels'

export interface DownloadProps {
  items: readonly DownloadItem[]
  labels?: Partial<DownloadLabels>
  icon?: ComponentType<SvgIconProps>
  iconProps?: SvgIconProps
  /**
   * Fires when an item's `resolve()` rejects (or any step in the click-through
   * path throws). The error is normalised to a real `Error` first. Surface
   * to your telemetry / toast layer here; the trigger already shows a visual
   * error indicator independently.
   */
  onError?: (err: Error) => void
}

export function Download({
  items,
  labels,
  icon: Icon = DownloadIcon,
  iconProps,
  onError,
}: DownloadProps) {
  const _labels = { ...DEFAULT_DOWNLOAD_LABELS, ...labels }
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
  const [isDownloading, setIsDownloading] = useState(false)
  const [error, setError] = useState<Error | null>(null)

  const open = useCallback((e: React.MouseEvent<HTMLButtonElement>) => {
    // Clear any prior error when the user re-engages the menu — the next
    // attempt is a fresh interaction, not a continuation of the failed one.
    setError(null)
    setAnchorEl(e.currentTarget)
  }, [])
  const close = useCallback(() => setAnchorEl(null), [])

  const onSelect = useCallback(
    (item: DownloadItem) => {
      // Single-flight: ignore re-entry while a download is already in flight.
      // The trigger is `disabled` and the menu is closed during that window,
      // so this guard mostly defends against future call sites that bypass
      // the menu (e.g. keyboard shortcuts).
      if (item.disabled || isDownloading) return
      close()
      setIsDownloading(true)
      setError(null)
      item
        .resolve()
        .then(({ url, filename, revoke }) => {
          // `item.filename` overrides what `resolve()` returns — lets consumers
          // template the filename with widget metadata declaratively.
          triggerLinkDownload({ url, filename: item.filename ?? filename })
          // Defer revoke past the current task so Safari / Firefox-on-slow-disk
          // can dispatch the download before the blob URL is invalidated.
          // Synchronous revoke here can silently cancel the download.
          if (revoke) setTimeout(revoke, 0)
        })
        .catch((err: unknown) => {
          const e = err instanceof Error ? err : new Error(String(err))
          setError(e)
          onError?.(e)
        })
        .finally(() => {
          setIsDownloading(false)
        })
    },
    [close, isDownloading, onError],
  )

  const triggerLabel = isDownloading
    ? _labels.loading
    : error
      ? _labels.error
      : _labels.trigger
  const triggerActive = Boolean(anchorEl) || isDownloading

  return (
    <>
      <Tooltip title={triggerLabel}>
        <IconButton
          size='small'
          aria-label={triggerLabel}
          aria-haspopup='true'
          aria-expanded={anchorEl ? 'true' : undefined}
          aria-busy={isDownloading || undefined}
          data-error={error ? 'true' : undefined}
          disabled={isDownloading || items.length === 0}
          onClick={open}
          className={triggerActive ? 'active' : undefined}
        >
          {isDownloading ? (
            <CircularProgress size={18} color='inherit' />
          ) : (
            <Icon fontSize='small' {...iconProps} />
          )}
        </IconButton>
      </Tooltip>
      <Menu
        anchorEl={anchorEl}
        open={Boolean(anchorEl)}
        onClose={close}
        variant='menu'
        elevation={8}
        anchorOrigin={{
          vertical: 'bottom',
          horizontal: 'right',
        }}
        transformOrigin={{
          vertical: 'top',
          horizontal: 'right',
        }}
      >
        {items.map((item) => (
          <MenuItem
            key={item.id}
            disabled={item.disabled}
            onClick={() => void onSelect(item)}
          >
            {item.icon && (
              <ListItemIcon sx={{ color: 'text.primary' }}>
                {item.icon}
              </ListItemIcon>
            )}
            <ListItemText>{item.label}</ListItemText>
          </MenuItem>
        ))}
      </Menu>
    </>
  )
}
