import type { ChildrenList, PartialElement } from '@furystack/shades'
import { Shade, createComponent } from '@furystack/shades'
import { buildTransition, cssVariableTheme } from '../services/css-variable-theme.js'
import { paletteFullColors } from '../services/palette-css-vars.js'
import type { Palette } from '../services/theme-provider-service.js'
import type { ComponentSize } from './component-size.js'

// ==========================================
// ButtonGroup
// ==========================================

export type ButtonGroupProps = PartialElement<HTMLElement> & {
  /** Visual variant applied to all buttons in the group */
  variant?: 'contained' | 'outlined'
  /** Theme color applied to all buttons */
  color?: keyof Palette
  /** Layout direction */
  orientation?: 'horizontal' | 'vertical'
  /** Whether all buttons in the group are disabled */
  disabled?: boolean
}

const groupChildRadius = cssVariableTheme.shape.borderRadius.md

export const ButtonGroup: (props: ButtonGroupProps, children: ChildrenList) => JSX.Element = Shade<ButtonGroupProps>({
  customElementName: 'shade-button-group',
  css: {
    display: 'inline-flex',
    fontFamily: cssVariableTheme.typography.fontFamily,
    borderRadius: cssVariableTheme.shape.borderRadius.md,

    '&[data-orientation="vertical"]': {
      flexDirection: 'column',
    },

    // Uses [role][data-orientation] for specificity (0,2,1) to override
    // child Button CSS at (0,1,1)
    '&[role][data-orientation] > *': {
      margin: '0',
      borderRadius: '0',
    },

    '&[role]:not([data-orientation="vertical"]) > :first-child': {
      borderRadius: `${groupChildRadius} 0 0 ${groupChildRadius}`,
    },
    '&[role]:not([data-orientation="vertical"]) > :last-child': {
      borderRadius: `0 ${groupChildRadius} ${groupChildRadius} 0`,
    },

    '&[role][data-orientation="vertical"] > :first-child': {
      borderRadius: `${groupChildRadius} ${groupChildRadius} 0 0`,
    },
    '&[role][data-orientation="vertical"] > :last-child': {
      borderRadius: `0 0 ${groupChildRadius} ${groupChildRadius}`,
    },

    '&[role][data-orientation] > :only-child': {
      borderRadius: groupChildRadius,
    },
  },
  render: ({ props, children, useHostProps }) => {
    const { orientation = 'horizontal', disabled, variant, color, style } = props

    useHostProps({
      role: 'group',
      'data-orientation': orientation,
      'data-variant': variant || undefined,
      'data-disabled': disabled ? '' : undefined,
      'data-color': color || undefined,
      ...(style ? { style: style as Record<string, string> } : {}),
    })

    return <>{children}</>
  },
})

// ==========================================
// ToggleButtonGroup
// ==========================================

export type ToggleButtonProps = PartialElement<HTMLButtonElement> & {
  /** The value this button represents */
  value: string
  /** Whether the button is disabled */
  disabled?: boolean
  /**
   * The size of the toggle button.
   * @default 'medium'
   */
  size?: ComponentSize
  /**
   * Whether the button is in a pressed (selected) state.
   * Use this for standalone toggle buttons or controlled state.
   * When used inside a `ToggleButtonGroup`, the group manages the
   * pressed state automatically via its `value` prop and will
   * override this attribute.
   */
  pressed?: boolean
}

export const ToggleButton = Shade<ToggleButtonProps>({
  customElementName: 'shade-toggle-button',
  elementBase: HTMLButtonElement,
  elementBaseName: 'button',
  css: {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    padding: `${cssVariableTheme.spacing.sm} ${cssVariableTheme.spacing.lg}`,
    border: 'none',
    borderRadius: '0',
    margin: '0',
    fontSize: cssVariableTheme.typography.fontSize.md,
    fontWeight: cssVariableTheme.typography.fontWeight.medium,
    letterSpacing: cssVariableTheme.typography.letterSpacing.wider,
    lineHeight: '1.75',
    cursor: 'pointer',
    userSelect: 'none',
    background: 'transparent',
    color: 'var(--toggle-color-main)',
    boxShadow: 'none',
    transition: buildTransition(
      ['background', cssVariableTheme.transitions.duration.normal, cssVariableTheme.transitions.easing.default],
      ['color', cssVariableTheme.transitions.duration.normal, cssVariableTheme.transitions.easing.default],
      ['box-shadow', cssVariableTheme.transitions.duration.normal, cssVariableTheme.transitions.easing.default],
    ),

    '&[data-grouped]': {
      boxShadow: '0px 0px 0px 1px var(--toggle-color-main)',
    },

    '&:hover:not(:disabled):not([data-selected])': {
      background: 'color-mix(in srgb, var(--toggle-color-main) 10%, transparent)',
    },

    '&[data-selected]': {
      background: 'color-mix(in srgb, var(--toggle-color-main) 20%, transparent)',
      color: 'var(--toggle-color-main)',
      fontWeight: cssVariableTheme.typography.fontWeight.semibold,
    },

    '&[data-selected]:hover:not(:disabled)': {
      background: 'color-mix(in srgb, var(--toggle-color-main) 30%, transparent)',
    },

    '&:disabled': {
      cursor: 'not-allowed',
      opacity: cssVariableTheme.action.disabledOpacity,
    },

    '&:active:not(:disabled)': {
      transform: 'scale(0.96)',
    },

    '&[data-size="small"]': {
      padding: `${cssVariableTheme.spacing.xs} ${cssVariableTheme.spacing.sm}`,
      fontSize: cssVariableTheme.typography.fontSize.sm,
    },

    '&[data-size="large"]': {
      padding: `${cssVariableTheme.spacing.md} ${cssVariableTheme.spacing.xl}`,
      fontSize: cssVariableTheme.typography.fontSize.lg,
    },
  },
  render: ({ props, children, useHostProps }) => {
    useHostProps({
      'data-value': props.value || undefined,
      'data-size': props.size && props.size !== 'medium' ? props.size : undefined,
      'data-selected': props.pressed ? '' : undefined,
      type: 'button',
      style: {
        '--toggle-color-main': cssVariableTheme.text.secondary,
        ...(props.style as Record<string, string>),
      },
    })

    return <>{children}</>
  },
})

const defaultToggleColors = {
  main: cssVariableTheme.text.secondary,
  mainContrast: cssVariableTheme.background.default,
  light: cssVariableTheme.text.primary,
  dark: cssVariableTheme.text.disabled,
}

export type ToggleButtonGroupProps = PartialElement<HTMLElement> & {
  /** Currently selected value(s). Use a string for exclusive mode, or string[] for multi-select. */
  value?: string | string[]
  /** When true, only one button can be selected at a time */
  exclusive?: boolean
  /** Callback when the selected value(s) change */
  onValueChange?: (value: string | string[]) => void
  /** Theme color */
  color?: keyof Palette
  /** Layout direction */
  orientation?: 'horizontal' | 'vertical'
  /** Whether all toggle buttons are disabled */
  disabled?: boolean
  /**
   * Size applied to all toggle buttons in the group.
   * Individual ToggleButton `size` props are overridden by this value.
   * @default 'medium'
   */
  size?: ComponentSize
}

export const ToggleButtonGroup: (props: ToggleButtonGroupProps, children: ChildrenList) => JSX.Element =
  Shade<ToggleButtonGroupProps>({
    customElementName: 'shade-toggle-button-group',
    css: {
      display: 'inline-flex',
      borderRadius: cssVariableTheme.shape.borderRadius.md,

      '&[data-orientation="vertical"]': {
        flexDirection: 'column',
      },

      '&[data-disabled]': {
        pointerEvents: 'none',
        opacity: cssVariableTheme.action.disabledOpacity,
      },

      // Grouped appearance: box-shadow border + reset border-radius.
      // Uses [data-value] attribute selector for specificity (0,1,2) to
      // override ToggleButton's own CSS at (0,1,1).
      '& button[data-value]': {
        boxShadow: '0px 0px 0px 1px var(--toggle-color-main)',
        borderRadius: '0',
      },

      '&:not([data-orientation="vertical"]) button:first-of-type': {
        borderRadius: `${groupChildRadius} 0 0 ${groupChildRadius}`,
      },
      '&:not([data-orientation="vertical"]) button:last-of-type': {
        borderRadius: `0 ${groupChildRadius} ${groupChildRadius} 0`,
      },

      '&[data-orientation="vertical"] button:first-of-type': {
        borderRadius: `${groupChildRadius} ${groupChildRadius} 0 0`,
      },
      '&[data-orientation="vertical"] button:last-of-type': {
        borderRadius: `0 0 ${groupChildRadius} ${groupChildRadius}`,
      },

      '& button:only-of-type': {
        borderRadius: groupChildRadius,
      },
    },
    render: ({ props, children, useDisposable, useHostProps, useRef }) => {
      const groupRef = useRef<HTMLDivElement>('group')

      // Mutable container so the click handler always reads the latest props
      const state = useDisposable('state', () => ({
        props,
        [Symbol.dispose]: () => {},
      }))
      state.props = props

      useDisposable('click-handler', () => {
        const handleClick = (ev: Event) => {
          const target = (ev.target as HTMLElement).closest('button[data-value]')
          if (!target || target.hasAttribute('disabled')) return

          const clickedValue = target.getAttribute('data-value')
          if (!clickedValue) return

          const currentProps = state.props
          if (currentProps.exclusive) {
            const currentValue = Array.isArray(currentProps.value) ? currentProps.value[0] : currentProps.value
            const newValue = currentValue === clickedValue ? '' : clickedValue
            currentProps.onValueChange?.(newValue)
          } else {
            const currentValues = Array.isArray(currentProps.value)
              ? currentProps.value
              : currentProps.value
                ? [currentProps.value]
                : ([] as string[])
            const newValues = currentValues.includes(clickedValue)
              ? currentValues.filter((v) => v !== clickedValue)
              : [...currentValues, clickedValue]
            currentProps.onValueChange?.(newValues)
          }
        }

        let el: HTMLElement | null = null
        queueMicrotask(() => {
          el = groupRef.current
          el?.addEventListener('click', handleClick)
        })
        return { [Symbol.dispose]: () => el?.removeEventListener('click', handleClick) }
      })

      const { orientation = 'horizontal', disabled, color, size, style } = props
      const selectedValues = Array.isArray(props.value) ? props.value : props.value ? [props.value] : ([] as string[])

      const colors = color ? paletteFullColors[color] : defaultToggleColors
      useHostProps({
        role: 'group',
        'data-orientation': orientation,
        'data-disabled': disabled ? '' : undefined,
        style: {
          '--toggle-color-main': colors.main,
          ...(style as Record<string, string>),
        },
      })

      // Sync data-selected, disabled, and data-size on child buttons.
      // These can't be expressed in CSS because they depend on matching the
      // group's value prop against each button's data-value attribute.
      requestAnimationFrame(() => {
        const buttons = Array.from(groupRef.current?.querySelectorAll('button[data-value]') ?? [])

        buttons.forEach((btn) => {
          const val = btn.getAttribute('data-value')
          if (val && selectedValues.includes(val)) {
            btn.setAttribute('data-selected', '')
          } else {
            btn.removeAttribute('data-selected')
          }

          if (disabled) {
            btn.setAttribute('disabled', '')
          }

          if (size && size !== 'medium') {
            btn.setAttribute('data-size', size)
          } else if (size === 'medium') {
            btn.removeAttribute('data-size')
          }
        })
      })

      return (
        <div ref={groupRef} style={{ display: 'contents' }}>
          {children}
        </div>
      )
    },
  })

// ==========================================
// SegmentedControl
// ==========================================

export type SegmentedControlOption = {
  /** Unique value for this option */
  value: string
  /** Display label */
  label: string | JSX.Element
  /** Whether this option is disabled */
  disabled?: boolean
}

export type SegmentedControlProps = PartialElement<HTMLElement> & {
  /** Available options */
  options: SegmentedControlOption[]
  /** Currently selected value */
  value?: string
  /** Callback when the selected option changes */
  onValueChange?: (value: string) => void
  /** Theme color */
  color?: keyof Palette
  /** Whether the entire control is disabled */
  disabled?: boolean
  /** Size variant */
  size?: ComponentSize
}

const defaultSegmentedColors = {
  main: cssVariableTheme.palette.primary.main,
  mainContrast: cssVariableTheme.palette.primary.mainContrast,
}

export const SegmentedControl = Shade<SegmentedControlProps>({
  customElementName: 'shade-segmented-control',
  css: {
    display: 'inline-flex',
    borderRadius: cssVariableTheme.shape.borderRadius.md,
    background: cssVariableTheme.action.hoverBackground,
    padding: '3px',
    gap: '2px',

    '& .segmented-option': {
      display: 'inline-flex',
      alignItems: 'center',
      justifyContent: 'center',
      padding: `${cssVariableTheme.spacing.sm} ${cssVariableTheme.spacing.lg}`,
      borderRadius: cssVariableTheme.shape.borderRadius.sm,
      border: 'none',
      background: 'transparent',
      color: cssVariableTheme.text.secondary,
      fontSize: cssVariableTheme.typography.fontSize.md,
      fontWeight: cssVariableTheme.typography.fontWeight.medium,
      fontFamily: 'inherit',
      letterSpacing: '0.3px',
      cursor: 'pointer',
      userSelect: 'none',
      whiteSpace: 'nowrap',
      transition: buildTransition(
        ['background', cssVariableTheme.transitions.duration.fast, cssVariableTheme.transitions.easing.default],
        ['color', cssVariableTheme.transitions.duration.fast, cssVariableTheme.transitions.easing.default],
        ['box-shadow', cssVariableTheme.transitions.duration.fast, cssVariableTheme.transitions.easing.default],
      ),
    },

    '& .segmented-option:hover:not(:disabled):not([data-selected])': {
      color: cssVariableTheme.text.primary,
      background: 'color-mix(in srgb, var(--seg-color-main) 8%, transparent)',
    },

    '& .segmented-option[data-selected]': {
      background: cssVariableTheme.background.paper,
      color: 'var(--seg-color-main)',
      fontWeight: cssVariableTheme.typography.fontWeight.semibold,
      boxShadow: cssVariableTheme.shadows.sm,
    },

    '& .segmented-option:disabled': {
      cursor: 'not-allowed',
      opacity: '0.5',
    },

    '&[data-size="small"] .segmented-option': {
      padding: `${cssVariableTheme.spacing.xs} ${cssVariableTheme.spacing.md}`,
      fontSize: cssVariableTheme.typography.fontSize.sm,
    },

    '&[data-size="large"] .segmented-option': {
      padding: `${cssVariableTheme.spacing.md} ${cssVariableTheme.spacing.xl}`,
      fontSize: cssVariableTheme.typography.fontSize.lg,
    },
  },
  render: ({ props, useHostProps }) => {
    const { options, value, onValueChange, color, disabled, size, style } = props

    const colors = color
      ? { main: paletteFullColors[color].main, mainContrast: paletteFullColors[color].mainContrast }
      : defaultSegmentedColors

    useHostProps({
      role: 'radiogroup',
      'data-size': size && size !== 'medium' ? size : undefined,
      style: {
        '--seg-color-main': colors.main,
        '--seg-color-main-contrast': colors.mainContrast,
        ...(style as Record<string, string>),
      },
    })

    const buttons = options.map((option) => {
      const isSelected = value === option.value
      const isDisabled = disabled || option.disabled

      return (
        <button
          type="button"
          className="segmented-option"
          disabled={isDisabled}
          role="radio"
          aria-checked={isSelected ? 'true' : 'false'}
          data-value={option.value}
          {...(isSelected ? { 'data-selected': '' } : {})}
          onclick={() => {
            if (!isDisabled && value !== option.value) {
              onValueChange?.(option.value)
            }
          }}
        >
          {option.label}
        </button>
      )
    })

    return <>{buttons}</>
  },
})
