import { useMemo, useCallback } from 'react'

import Box from '../box'
import Button from '../button'
import { useStyleConfig, vui } from '../core'
import type { VuiComponent } from '../core'
import Icon from '../icon'
import { cs, isActivePath, generateMenuItemId } from '../utils'
import { collapsedWidth, expandedWidth } from './consts'
import { SidemenuStateProvider, useSidemenuContext } from './context'
import SidemenuItemV2 from './sidemenuItemV2'
import type { SidemenuItemV2Props, SidemenuV2Props } from './sidemenuV2.types'

// Re-export context provider and hook
export { SidemenuStateProvider, useSidemenuContext }

/** A collapsible side menu for navigation. */
export const SidemenuV2 = vui<'div', SidemenuV2Props>((props, ref) => {
  const {
    isExpandedInitial = true, // Uncontrolled: only affects initial state
    size,
    variant = 'light',
    onExpandedChange,
  } = props

  return (
    <SidemenuStateProvider
      isExpandedInitial={isExpandedInitial}
      onExpandedChange={onExpandedChange}
      variant={variant}
      size={size}
      isDark={variant === 'dark'}
    >
      <SidemenuInner {...props} ref={ref} />
    </SidemenuStateProvider>
  )
}) as VuiComponent<'div', SidemenuV2Props> & {
  Item: typeof SidemenuItemV2
}

// Inner component that uses context
const SidemenuInner = vui<'div', SidemenuV2Props>((props, ref) => {
  const {
    children,
    className,
    items,
    shouldExpand, // Callback: fine-grained control over expansion
    isSticky = false,
    size,
    variant = 'light',
    width = expandedWidth,
    top,
    onNavigate,
    ...rest
  } = props

  // Get state from context
  const context = useSidemenuContext()
  const { isMenuExpanded = true, setIsExpanded } = context || {}

  // Get styles configuration early
  const styles = useStyleConfig('SidemenuV2', props)

  // Memoized boolean conditions
  const conditions = useMemo(
    () => ({
      isDark: variant === 'dark',
      hasItems: !!items,
      shouldShowItems: !!items && items.length > 0,
      isMenuSticky: isSticky,
      isMenuExpanded: isMenuExpanded,
    }),
    [variant, items, isSticky, isMenuExpanded],
  )

  // Memoized computed values for styling and behavior
  const computedValues = useMemo(() => {
    // Get toggle button config from theme
    const toggleButtonTheme = styles.toggleButton || {}
    const currentSize = size || 'md' // Ensure we have a valid size
    const sizeStyles = (styles as any).sizes?.[currentSize] || {}
    const toggleButtonSizeTheme = sizeStyles.toggleButton || {}

    // Merge base theme with size-specific overrides
    const toggleConfig = {
      baseHeight: toggleButtonSizeTheme.baseHeight || toggleButtonTheme.baseHeight || 52,
      basePadding: toggleButtonSizeTheme.basePadding || toggleButtonTheme.basePadding || 2,
      expandedPaddingMultiplier: toggleButtonTheme.expandedPaddingMultiplier || 5,
      expandedWidth: toggleButtonTheme.expandedWidth || '52px',
      collapsedWidth: toggleButtonTheme.collapsedWidth || '100%',
    }

    // Helper function for consistent top position formatting
    const getMenuTopPosition = (isSticky: boolean, topValue?: any): string => {
      if (!isSticky) return 'auto'

      if (typeof topValue === 'string') return topValue
      if (typeof topValue === 'number') return `${topValue}px`

      return '0px' // fallback for undefined/null/complex types
    }

    return {
      menuWidth: conditions.isMenuExpanded ? width : collapsedWidth,
      toggleArrowDirection: conditions.isMenuExpanded ? 'Left' : 'Right',
      toggleButtonJustification: conditions.isMenuExpanded ? 'flex-end' : 'center',
      toggleButtonVariant: styles.toggleButton?.variant || (conditions.isDark ? 'tertiaryLight' : 'tertiaryDark'),
      toggleAriaLabel: conditions.isMenuExpanded ? 'Collapse menu' : 'Expand menu',
      toggleButtonPadding: toggleConfig.basePadding,
      toggleButtonVerticalPadding: conditions.isMenuExpanded
        ? toggleConfig.basePadding * toggleConfig.expandedPaddingMultiplier
        : toggleConfig.basePadding,
      toggleButtonHeight: toggleConfig.baseHeight,
      toggleButtonWidth: conditions.isMenuExpanded ? toggleConfig.expandedWidth : toggleConfig.collapsedWidth,
      menuPosition: conditions.isMenuSticky ? 'sticky' : 'static',
      menuTopPosition: getMenuTopPosition(conditions.isMenuSticky, top),
    }
  }, [conditions, width, top, styles, size])

  // Memoized icon name
  const toggleIcon = useMemo(
    () => `falArrow${computedValues.toggleArrowDirection}` as const,
    [computedValues.toggleArrowDirection],
  )

  // Stable callback for toggle (simplified)
  const handleToggle = useCallback(() => {
    const newExpandedState = !conditions.isMenuExpanded

    // Check if expansion should be allowed via callback
    if (shouldExpand && !shouldExpand('toggle', conditions.isMenuExpanded)) {
      return // Block the toggle
    }

    // Update state via context
    setIsExpanded?.(newExpandedState)
  }, [conditions.isMenuExpanded, shouldExpand, setIsExpanded])

  // Stable callback for item click - with expansion control
  const handleItemClick = useCallback(
    (item: SidemenuItemV2Props) => {
      // Early return for clarity
      if (shouldExpand && item?.path) {
        const shouldExpandOnNav = shouldExpand('navigation', conditions.isMenuExpanded)
        if (typeof shouldExpandOnNav === 'boolean' && shouldExpandOnNav !== conditions.isMenuExpanded) {
          setIsExpanded?.(shouldExpandOnNav)
        }
      }
      // Call item click handler
      item?.onClick?.()
      // Navigate if path exists
      if (item?.path) onNavigate?.(item.path)
    },
    [onNavigate, shouldExpand, conditions.isMenuExpanded, setIsExpanded],
  )

  // Create stable item click handlers
  const itemClickHandlers = useMemo(() => {
    return (items ?? []).reduce<Record<string, () => void>>((handlers, item, index) => {
      const itemId = item.itemId || item.path || generateMenuItemId(item.title, item.level || 0) || `item-${index}`
      handlers[itemId] = () => handleItemClick(item)
      return handlers
    }, {})
  }, [items, handleItemClick])

  // Memoized items rendering
  const renderedItems = useMemo(() => {
    if (!conditions.shouldShowItems) return children

    return items?.map?.((item: SidemenuItemV2Props, index: number) => {
      // Use consistent itemId generation that matches the component's logic
      const itemId = item.itemId || item.path || generateMenuItemId(item.title, item.level || 0) || `item-${index}`

      // Use explicit isActive prop if provided, otherwise calculate from path
      const calculatedIsActive = item.isActive !== undefined ? item.isActive : isActivePath(item?.path || '')

      return (
        <SidemenuItemV2
          isActive={calculatedIsActive}
          onBottom={item.onBottom}
          key={itemId}
          itemId={itemId}
          level={item.level || 0}
          {...item}
          onClick={itemClickHandlers[itemId]}
        />
      )
    })
  }, [conditions.shouldShowItems, conditions.isMenuExpanded, items, children, itemClickHandlers])

  // Memoized CSS class names
  const cssClasses = useMemo(
    () =>
      cs(
        'vui-sidemenu',
        `vui-sidemenu--${variant}`,
        conditions.isMenuExpanded && 'vui-sidemenu--expanded',
        !conditions.isMenuExpanded && 'vui-sidemenu--collapsed',
        conditions.isDark && 'vui-sidemenu--dark',
        conditions.isMenuSticky && 'vui-sidemenu--sticky',
        className,
      ),
    [variant, conditions, className],
  )

  // Memoized container props
  const containerProps = useMemo(
    () => ({
      className: cssClasses,
      flexDirection: 'column' as const,
      overflowX: 'hidden' as const,
      position: computedValues.menuPosition,
      ref,
      top: computedValues.menuTopPosition,
      transitionDuration: 'normal' as const,
      transitionProperty: 'width' as const,
      w: computedValues.menuWidth,

      ...styles.container,
      ...rest,
    }),
    [
      cssClasses,
      computedValues.menuPosition,
      computedValues.menuTopPosition,
      computedValues.menuWidth,
      ref,
      styles.container,
      rest,
    ],
  )

  // Memoized toggle button props with complete dependencies
  const toggleButtonProps = useMemo(
    () => ({
      'aria-expanded': conditions.isMenuExpanded,
      'aria-label': computedValues.toggleAriaLabel,
      border: 0,
      borderRadius: 0,
      justifyContent: computedValues.toggleButtonJustification,
      minHeight: `${computedValues.toggleButtonHeight}px`,
      minWidth: 0,
      onClick: handleToggle,
      px: computedValues.toggleButtonPadding,
      py: `${computedValues.toggleButtonVerticalPadding}px`,
      size: 'lg' as const,
      variant: computedValues.toggleButtonVariant,
      w: computedValues.toggleButtonWidth,
      flexShrink: 0,
      transition: styles.toggleButton?.transition || 'padding 0.2s ease-in-out, width 0.2s ease-in-out',
    }),
    [
      computedValues.toggleAriaLabel,
      computedValues.toggleButtonJustification,
      computedValues.toggleButtonVariant,
      computedValues.toggleButtonPadding,
      computedValues.toggleButtonVerticalPadding,
      computedValues.toggleButtonHeight,
      computedValues.toggleButtonWidth,
      conditions.isMenuExpanded,
      handleToggle,
      styles.toggleButton,
    ],
  )

  return (
    <Box elevation="3" {...containerProps}>
      <Box
        className="vui-sidemenu-content"
        flexDirection="column"
        flexGrow={1}
        justifyContent="flex-start"
        minW={expandedWidth}
        overflowX="hidden"
        overflowY="auto"
        w="100%"
        role="navigation"
        aria-label="Main navigation"
      >
        {renderedItems}
      </Box>

      <Box
        className="vui-sidemenu-bottom"
        justifyContent={conditions.isMenuExpanded ? 'flex-end' : 'flex-start'}
        borderTop="1px solid sandstone.main"
      >
        <Button {...toggleButtonProps} key={'sidemenu-toggle-button'}>
          <Icon name={toggleIcon} size="sm" aria-hidden="true" />
        </Button>
      </Box>
    </Box>
  )
}) as VuiComponent<'div', SidemenuV2Props> & {
  // Top: typeof SidemenuTop
  Item: typeof SidemenuItemV2
}

SidemenuV2.Item = SidemenuItemV2
SidemenuV2.displayName = 'SidemenuV2'

export default SidemenuV2
