import {
  DndContext,
  closestCenter,
  KeyboardSensor,
  PointerSensor,
  useSensor,
  useSensors,
  type DragEndEvent,
} from '@dnd-kit/core'
import {
  arrayMove,
  SortableContext,
  sortableKeyboardCoordinates,
  verticalListSortingStrategy,
} from '@dnd-kit/sortable'
import { IconButton, Menu, SvgIcon } from '@mui/material'
import {
  useCallback,
  useEffect,
  useMemo,
  useState,
  type MouseEvent,
} from 'react'
import { widgetStoreActions } from '../../stores/widget-store'
import type { ChangeColumnProps } from './types'
import { actionButtonStyles } from '../shared/styles'
import { Tooltip } from '../../../components'
import type { TableColumn, TableWidgetState } from '../../table/types'
import { ChangeColumnIcon } from './change-column-icon'
import { SortableColumnItem } from './sortable-column-item'
import { useWidgetSelector } from '../../stores/use-widget-selector'

export const CHANGE_COLUMN_TOOL_ID = 'change-column'

/**
 * Widget action to reorder columns in a table widget via drag-and-drop.
 *
 * This action reads the columns from the widget store and allows users to
 * drag and drop columns to reorder them. All columns are displayed and
 * can be reordered.
 *
 * Registers as a config pipeline tool so that column order is automatically
 * re-applied when the base config is updated (e.g., by WidgetLoader).
 *
 * Returns null if there are fewer than 2 columns.
 *
 * @example
 * ```tsx
 * <ChangeColumn id="my-table-widget" />
 * ```
 */
export function ChangeColumn({
  id,
  labels,
  Icon,
  IconButtonProps,
  MenuProps,
}: ChangeColumnProps) {
  const [anchorEl, setAnchorEl] = useState<HTMLElement | null>(null)
  const { columns } = useWidgetSelector(id, (w) => ({
    columns: (w as TableWidgetState | undefined)?.columns,
  }))

  /**
   * Config tool function that reorders columns to match the current widget state.
   * Reads desired order from the widget store (set by handleDragEnd via setWidget).
   * Preserves referential identity when the order already matches to prevent
   * re-render loops in the config pipeline.
   */
  const reorderFn = useCallback(
    (currentConfig: unknown): unknown => {
      const widgetState = widgetStoreActions.getWidget<TableWidgetState>(id)
      const currentColumns = widgetState?.columns
      if (!currentColumns || currentColumns.length === 0) return currentConfig

      const config = currentConfig as Record<string, unknown>
      const configColumns = config.columns as TableColumn[] | undefined
      if (!configColumns || configColumns.length === 0) return currentConfig

      // Check if config columns are already in the same order as widget columns
      const alreadyMatches =
        configColumns.length === currentColumns.length &&
        configColumns.every((col, i) => col.id === currentColumns[i]?.id)
      if (alreadyMatches) return currentConfig

      // Reorder config columns to match widget column order
      const columnMap = new Map(configColumns.map((col) => [col.id, col]))
      const reordered: TableColumn[] = []

      for (const widgetCol of currentColumns) {
        const col = columnMap.get(widgetCol.id)
        if (col) {
          reordered.push(col)
          columnMap.delete(widgetCol.id)
        }
      }

      // Append any new columns not in the widget order
      for (const col of columnMap.values()) {
        reordered.push(col)
      }

      // If result matches current widget columns, reuse the same array reference
      // to prevent downstream subscribers from detecting a change
      const matchesWidget =
        reordered.length === currentColumns.length &&
        reordered.every((col, i) => col.id === currentColumns[i]?.id)

      return { ...config, columns: matchesWidget ? currentColumns : reordered }
    },
    [id],
  )

  const sensors = useSensors(
    useSensor(PointerSensor),
    useSensor(KeyboardSensor, {
      coordinateGetter: sortableKeyboardCoordinates,
    }),
  )

  const columnIds = useMemo(
    () => columns?.map((col) => col.id) ?? [],
    [columns],
  )

  // Register config tool on mount
  useEffect(() => {
    widgetStoreActions.registerTool(id, {
      id: CHANGE_COLUMN_TOOL_ID,
      type: 'config',
      order: 100,
      enabled: true,
      fn: reorderFn,
    })
    return () => widgetStoreActions.unregisterTool(id, CHANGE_COLUMN_TOOL_ID)
  }, [id, reorderFn])

  const handleToggle = useCallback((event: MouseEvent<HTMLElement>) => {
    event.stopPropagation()
    setAnchorEl(event.currentTarget)
  }, [])

  const handleClose = useCallback(() => {
    setAnchorEl(null)
  }, [])

  const handleDragEnd = (event: DragEndEvent) => {
    const { active, over } = event
    if (!over || active.id === over.id || !columns) return
    const oldIndex = columns.findIndex((col) => col.id === active.id)
    const newIndex = columns.findIndex((col) => col.id === over.id)
    if (oldIndex !== -1 && newIndex !== -1) {
      const newColumns = arrayMove(columns, oldIndex, newIndex)
      widgetStoreActions.setWidget(id, { columns: newColumns })
    }
  }

  // Return null if there are fewer than 2 columns
  if (!columns || columns.length < 2) {
    return null
  }

  const tooltipLabel = labels?.tooltip ?? 'Change column'
  const isOpen = Boolean(anchorEl)

  return (
    <>
      <Tooltip title={tooltipLabel}>
        <IconButton
          size='small'
          aria-label={labels?.ariaLabel ?? tooltipLabel}
          aria-controls={isOpen ? 'change-column-menu' : undefined}
          aria-haspopup='true'
          aria-expanded={isOpen ? 'true' : undefined}
          onClick={handleToggle}
          data-active={isOpen}
          sx={actionButtonStyles.trigger}
          {...IconButtonProps}
        >
          {Icon ?? (
            <SvgIcon>
              <ChangeColumnIcon />
            </SvgIcon>
          )}
        </IconButton>
      </Tooltip>
      <DndContext
        sensors={sensors}
        collisionDetection={closestCenter}
        onDragEnd={handleDragEnd}
      >
        <Menu
          id='change-column-menu'
          anchorEl={anchorEl}
          open={isOpen}
          onClose={handleClose}
          anchorOrigin={{ vertical: 'bottom', horizontal: 'right' }}
          transformOrigin={{ vertical: 'top', horizontal: 'right' }}
          slotProps={{
            paper: {
              sx: { overflow: 'hidden' },
            },
          }}
          {...MenuProps}
        >
          <SortableContext
            items={columnIds}
            strategy={verticalListSortingStrategy}
          >
            {columns.map((column) => (
              <SortableColumnItem key={column.id} column={column} />
            ))}
          </SortableContext>
        </Menu>
      </DndContext>
    </>
  )
}
