import { useCallback, useEffect, useMemo, type ComponentType } from 'react'
import { IconButton, type SvgIconProps } from '@mui/material'
import { Lock as LockIcon } from '@mui/icons-material'
import { LockOpen as LockOpenIcon } from '@mui/icons-material'
import { Tooltip } from '../../../components'
import { useTransform, useWidgetId } from '../../stores'
import type { TransformPair } from '../../stores'
import { filterByLockedItems } from './transforms'
import {
  DEFAULT_LOCK_SELECTION_LABELS,
  type LockSelectionLabels,
} from './labels'
import { styles } from './style'

const LOCK_SELECTION_DESCRIPTOR = {
  id: 'lock-selection',
  type: 'data' as const,
  order: 30,
}

export type LockSelectionKey = string | number

export interface LockSelectionProps {
  /**
   * Currently-selected items (destination-owned). On lock, this set is
   * snapshotted into `lockedItems` via `onLockChange`.
   */
  selection: readonly LockSelectionKey[]
  /**
   * Currently-locked items (destination-owned). When non-empty, the widget's
   * data is filtered to these items via the registered transform.
   */
  lockedItems: readonly LockSelectionKey[]
  /**
   * Fires when the user toggles the lock. The destination is responsible for
   * persisting the new locked set (and re-feeding it via `lockedItems`).
   */
  onLockChange: (next: readonly LockSelectionKey[]) => void
  labels?: Partial<LockSelectionLabels>
  /** Lock icon (default: `LockIcon`). */
  lockIcon?: ComponentType<SvgIconProps>
  /** Unlock icon (default: `LockOpenIcon`). */
  unlockIcon?: ComponentType<SvgIconProps>
  iconProps?: SvgIconProps
}

/**
 * Action that pins the current selection so the widget's data filters down
 * to those items even as the user changes selections. Selection and lock
 * state are destination-owned; this component is a controlled UI + a data
 * transform registered against the widget's pipeline.
 *
 * The trigger is disabled when there is nothing to lock and nothing locked.
 */
export function LockSelection({
  selection,
  lockedItems,
  onLockChange,
  labels,
  lockIcon: LockSvg = LockIcon,
  unlockIcon: UnlockSvg = LockOpenIcon,
  iconProps,
}: LockSelectionProps) {
  const id = useWidgetId()
  const _labels = { ...DEFAULT_LOCK_SELECTION_LABELS, ...labels }
  const isLocked = lockedItems.length > 0
  const canToggle = isLocked || selection.length > 0

  // Pair memoized on lockedItems identity — useTransform re-registers the fn
  // (which closes over the current locked set) whenever the destination feeds
  // a new array. setEnabled drives the middleware-level `enabled` flag.
  const pairs = useMemoPair(lockedItems)
  const { enabled, setEnabled } = useTransform(id, pairs, {
    initialEnabled: isLocked,
  })

  // Keep middleware `enabled` in sync with destination-owned `isLocked`. The
  // pipeline only short-circuits the transform when enabled === false, so
  // toggling here is what makes lock/unlock actually take effect.
  useEffect(() => {
    if (enabled !== isLocked) setEnabled(isLocked)
  }, [enabled, isLocked, setEnabled])

  const handleToggle = useCallback(() => {
    if (isLocked) {
      onLockChange([])
    } else {
      onLockChange([...selection])
    }
  }, [isLocked, onLockChange, selection])

  const tooltip = !canToggle
    ? _labels.disabled
    : isLocked
      ? _labels.unlock
      : _labels.lock
  const Icon = isLocked ? UnlockSvg : LockSvg

  return (
    <Tooltip title={tooltip}>
      <span>
        <IconButton
          size='small'
          aria-label={tooltip}
          aria-pressed={isLocked}
          disabled={!canToggle}
          onClick={handleToggle}
          sx={{ ...styles.toggle, ...(isLocked && styles.toggleActive) }}
        >
          <Icon fontSize='small' {...iconProps} />
        </IconButton>
      </span>
    </Tooltip>
  )
}

/**
 * Builds a stable `[{ descriptor, fn }]` pair where `fn` closes over the
 * latest `lockedItems`. Re-memoized whenever the array identity changes so
 * `useTransform` re-registers the closure with the freshest data.
 */
function useMemoPair(
  lockedItems: readonly LockSelectionKey[],
): readonly TransformPair[] {
  const fn = useCallback(
    (data: unknown) => filterByLockedItems(data, lockedItems),
    [lockedItems],
  )
  return useMemo(() => [{ descriptor: LOCK_SELECTION_DESCRIPTOR, fn }], [fn])
}
