'use client'

import * as SelectPrimitive from '@radix-ui/react-select'
import React from 'react'
import {
  SvgCheck,
  SvgTaillessLineArrowDown1,
  type SvgIcon,
} from '@chainlink/blocks-icons'
import { cn } from '../../utils/cn'
import {
  DROPDOWN_ELEMENT_HEIGHT,
  DROPDOWN_LIST_MAX_HEIGHT,
  type DropdownSize,
} from '../dropdownHeights'
import { inputVariants } from '../Input'
import { ClearIcon } from '../Input/icons'
import {
  getDropdownItemBaseClassName,
  getDropdownItemIndicatorOffsetClassName,
  getInputControlContentTextClassName,
  getInputControlTextClassName,
  getInputControlValueSlotClassName,
} from '../inputControlStyles'
import {
  useScrollArrows,
  ScrollArrowUp,
  ScrollArrowDown,
} from '../ScrollArrows'

export type SelectSize = DropdownSize

export type SelectIcon = React.ReactElement | undefined

// Size context for Select components
const SelectSizeContext = React.createContext<SelectSize>('default')

// Clear button: value + setter (controlled uses props, uncontrolled uses internal state).
const SelectClearContext = React.createContext<{
  value: string
  onValueChange: (value: string) => void
  clearable: boolean
}>({ value: '', onValueChange: () => {}, clearable: true })

type SelectProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Root
> & {
  /** Visual density applied to the trigger and items. Defaults to `'default'`. */
  size?: SelectSize
  /** When `false`, the hover-to-clear button is not shown. Defaults to `true`. */
  clearable?: boolean
}

/**
 * Root component. Manages open state and the selected value. Supports both
 * controlled (`value` + `onValueChange`) and uncontrolled (`defaultValue`) modes.
 * Provides `size` and `clearable` context to all child components.
 */
const Select = ({
  size = 'default',
  clearable = true,
  children,
  value: valueProp,
  onValueChange: onValueChangeProp,
  defaultValue,
  ...props
}: SelectProps) => {
  const [internalValue, setInternalValue] = React.useState(defaultValue ?? '')
  const isControlled = valueProp !== undefined
  const value = isControlled ? (valueProp ?? '') : internalValue
  const onValueChange = React.useCallback(
    (v: string) => {
      if (!isControlled) setInternalValue(v)
      onValueChangeProp?.(v)
    },
    [isControlled, onValueChangeProp],
  )
  return (
    <SelectSizeContext.Provider value={size}>
      <SelectClearContext.Provider value={{ value, onValueChange, clearable }}>
        <SelectPrimitive.Root
          data-slot="select"
          value={value}
          onValueChange={onValueChange}
          {...props}
        >
          {children}
        </SelectPrimitive.Root>
      </SelectClearContext.Provider>
    </SelectSizeContext.Provider>
  )
}
Select.displayName = 'Select'

// Hook to use size context
const useSelectSize = () => {
  const context = React.useContext(SelectSizeContext)
  if (context === undefined) {
    throw new Error('useSelectSize must be used within a Select component')
  }
  return context
}

type SelectGroupProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Group
> & {
  ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Group>>
}

/**
 * Groups related items inside `SelectContent`. Pair with `SelectLabel` to add
 * a visible heading above the group.
 */
const SelectGroup = ({ ref, ...props }: SelectGroupProps) => (
  <SelectPrimitive.Group ref={ref} data-slot="select-group" {...props} />
)
SelectGroup.displayName = SelectPrimitive.Group.displayName

type SelectValueProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Value
> & {
  ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Value>>
}

/**
 * Renders the selected value inside `SelectTrigger`. Shows `placeholder` when
 * nothing is selected.
 */
const SelectValue = ({
  ref,
  className,
  ...props
}: SelectValueProps & { className?: string }) => (
  <SelectPrimitive.Value
    ref={ref}
    data-slot="select-value"
    className={className}
    {...props}
  />
)
SelectValue.displayName = SelectPrimitive.Value.displayName

type SelectIconComponentProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Icon
> &
  Pick<SvgIcon, 'color'> & {
    ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Icon>>
  }

/**
 * The dropdown caret icon. Included automatically in the default `SelectTrigger`.
 * Add it manually when using `asChild` on `SelectTrigger` to retain the arrow
 * inside a custom element.
 */
const SelectIconInner = ({
  className,
  color = 'currentColor',
  ref,
  ...props
}: SelectIconComponentProps): React.ReactElement => {
  const size = useSelectSize()

  return (
    <SelectPrimitive.Icon
      ref={ref}
      asChild
      data-slot="select-icon"
      data-size={size}
      className={cn(
        'text-input-muted-foreground transition-transform duration-100 data-[size=default]:h-4 data-[size=sm]:h-3 data-[size=xs]:h-3 data-[size=default]:w-4 data-[size=sm]:w-3 data-[size=xs]:w-3 group-data-[state=open]:rotate-180 group-data-[disabled]:!text-input-muted-more-foreground',
        className,
      )}
      {...props}
    >
      <SvgTaillessLineArrowDown1 color={color} data-slot="select-caret" />
    </SelectPrimitive.Icon>
  )
}
SelectIconInner.displayName = 'SelectIcon'

// Create a properly-typed export that maintains the value 'SelectIcon'
const SelectIconElement = SelectIconInner

type SelectTriggerProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Trigger
> & {
  /** Replace the trigger with a custom element (e.g. `Button`). The child must forward its ref. */
  asChild?: boolean
  width?: 'responsive' | 'hug' | 'full'
  ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Trigger>>
}

/**
 * The button that opens the dropdown. Renders the Blocks input style by default.
 * Pass `asChild` to replace it with a custom element such as `Button` or a plain
 * `button` — the child must forward its ref.
 */
const SelectTrigger = ({
  className,
  children,
  asChild = false,
  width,
  ref,
  ...props
}: SelectTriggerProps) => {
  const size = useSelectSize()
  const { value, onValueChange, clearable } =
    React.useContext(SelectClearContext)

  // Icon rotation styles for asChild case where custom icons might be provided
  const iconRotationStyles =
    'group [&_[data-slot=select-icon]]:transition-transform [&_[data-slot=select-icon]]:duration-100 [&_[data-slot=select-icon]]:group-data-[state=open]:rotate-180'

  if (asChild) {
    return (
      <SelectPrimitive.Trigger
        ref={ref}
        asChild
        data-slot="select-trigger"
        data-size={size}
        data-placeholder={value === '' || undefined}
        className={cn(iconRotationStyles, className)}
        {...props}
      >
        {children}
      </SelectPrimitive.Trigger>
    )
  }

  return (
    <SelectPrimitive.Trigger
      ref={ref}
      data-slot="select-trigger"
      data-size={size}
      data-placeholder={value === '' || undefined}
      className={cn(
        inputVariants({ size, width }),
        'group flex items-center justify-between whitespace-nowrap text-foreground',
        '[&_[data-slot=select-value]]:border-none [&_[data-slot=select-value]]:bg-transparent [&_[data-slot=select-value]]:shadow-none',
        '[&_[data-slot=select-value]]:font-sans [&_[data-slot=select-value]]:transition-none',
        value === ''
          ? '[&_[data-slot=select-value]]:!text-input-muted-foreground'
          : '[&_[data-slot=select-value]]:text-foreground',
        getInputControlValueSlotClassName('select-value', size),
        'hover:border-input-border-hover data-[state=open]:border-input-border-active [&.hover]:border-input-border-hover',
        '[&_[data-slot=select-value]]:line-clamp-1',
        'data-[disabled]:cursor-not-allowed data-[disabled]:border-input-border-disabled data-[disabled]:bg-input-disabled data-[disabled]:!text-input-muted-more-foreground',
        '[&[data-disabled]_[data-slot=select-value]]:!text-input-muted-more-foreground',
        className,
      )}
      style={{
        boxSizing: 'border-box',
        height: DROPDOWN_ELEMENT_HEIGHT[size],
        minHeight: DROPDOWN_ELEMENT_HEIGHT[size],
      }}
      {...props}
    >
      {children}
      <div className="flex items-center gap-2">
        {clearable && (
          <span
            className="flex items-center group-data-[disabled]:hidden group-data-[placeholder]:hidden"
            onClick={(e) => e.stopPropagation()}
            onMouseDown={(e) => e.stopPropagation()}
            onPointerDown={(e) => e.stopPropagation()}
          >
            <span
              role="button"
              tabIndex={0}
              data-slot="select-clear"
              aria-label="Clear selection"
              className={cn(
                'pointer-events-none cursor-pointer opacity-0 transition-opacity',
                'group-hover:pointer-events-auto group-hover:opacity-100',
                'group-data-[state=open]:pointer-events-auto group-data-[state=open]:opacity-100',
                'focus:pointer-events-auto focus:opacity-100',
                'text-input-muted-foreground hover:text-muted-foreground',
                'focus-visible:rounded-sm focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring',
                'size-4 [&_svg]:h-full [&_svg]:w-full',
              )}
              onClick={(e) => {
                e.stopPropagation()
                onValueChange?.('')
              }}
              onKeyDown={(e) => {
                if (e.key === 'Enter' || e.key === ' ') {
                  e.preventDefault()
                  onValueChange?.('')
                }
              }}
            >
              <ClearIcon />
            </span>
          </span>
        )}
        <SelectIconElement />
      </div>
    </SelectPrimitive.Trigger>
  )
}
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName

type SelectContentProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Content
> & {
  container?: HTMLElement | null
  ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Content>>
}

/**
 * The dropdown panel that contains `SelectItem` elements. Renders inside a
 * portal and aligns to the trigger width by default.
 */
const SelectContent = ({
  className,
  children,
  position = 'popper',
  container,
  ref,
  ...props
}: SelectContentProps) => {
  const size = useSelectSize()
  const {
    scrollRef,
    containerRef,
    showScrollUp,
    showScrollDown,
    handleScroll,
    startAutoScroll,
    stopAutoScroll,
    scrollToStart,
    scrollToEnd,
  } = useScrollArrows()

  return (
    <SelectPrimitive.Portal container={container}>
      <SelectPrimitive.Content
        ref={ref}
        data-slot="select-content"
        className={cn(
          'pointer-events-auto',
          getInputControlTextClassName(size),
          getInputControlContentTextClassName(size),
          'text-muted-foreground',
          'relative z-50 min-w-[8rem] overflow-hidden rounded border border-solid border-input-border-active bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2',
          position === 'popper' &&
            'min-w-[var(--radix-select-trigger-width)] data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1',
          className,
        )}
        position={position}
        {...props}
      >
        <div
          ref={containerRef as React.RefObject<HTMLDivElement>}
          className="relative overflow-hidden"
        >
          <SelectPrimitive.Viewport
            ref={scrollRef as React.RefObject<HTMLDivElement>}
            onScroll={handleScroll}
            tabIndex={-1}
            className={cn(
              'overflow-y-auto overscroll-contain outline-none [scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
              showScrollUp && 'pt-6',
              showScrollDown && 'pb-6',
            )}
            style={{ maxHeight: DROPDOWN_LIST_MAX_HEIGHT[size] }}
          >
            {children}
          </SelectPrimitive.Viewport>
          {showScrollUp && (
            <ScrollArrowUp
              onHoverStart={() => startAutoScroll('up')}
              onHoverEnd={stopAutoScroll}
              onJump={scrollToStart}
            />
          )}
          {showScrollDown && (
            <ScrollArrowDown
              onHoverStart={() => startAutoScroll('down')}
              onHoverEnd={stopAutoScroll}
              onJump={scrollToEnd}
            />
          )}
        </div>
      </SelectPrimitive.Content>
    </SelectPrimitive.Portal>
  )
}
SelectContent.displayName = SelectPrimitive.Content.displayName

type SelectLabelProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Label
> & {
  ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Label>>
}

/**
 * A non-interactive label for a `SelectGroup`. Renders as a small bold heading
 * above the group's items.
 */
const SelectLabel = ({ className, ref, ...props }: SelectLabelProps) => (
  <SelectPrimitive.Label
    ref={ref}
    data-slot="select-label"
    className={cn('px-2 py-1.5 font-semibold', className)}
    {...props}
  />
)
SelectLabel.displayName = SelectPrimitive.Label.displayName

type SelectItemProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Item
> & {
  /** Optional icon rendered to the left of the label. */
  Icon?: React.ReactElement | undefined
  ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Item>>
}

/**
 * An individual option inside `SelectContent`. The selected item is highlighted
 * with a checkmark. Pass an `Icon` to show an icon to the left of the label.
 */
const SelectItem = ({
  className,
  children,
  Icon,
  ref,
  ...props
}: SelectItemProps) => {
  const size = useSelectSize()

  return (
    <SelectPrimitive.Item
      ref={ref}
      data-slot="select-item"
      data-size={size}
      className={cn(
        getDropdownItemBaseClassName(size),
        'text-muted-foreground',
        'focus:text-accent-foreground focus:bg-accent',
        'data-[disabled]:pointer-events-none data-[disabled]:cursor-not-allowed data-[disabled]:text-input-muted-more-foreground',
        'data-[state=checked]:!text-brand [&[data-state=checked]_span]:text-brand [&[data-state=checked]_svg]:!text-brand',
        '[&:not([data-state=checked])_svg]:text-muted-foreground [&_span]:text-muted-foreground',
        '[&_img]:shrink-0',
        className,
      )}
      style={{
        boxSizing: 'border-box',
        height: DROPDOWN_ELEMENT_HEIGHT[size],
        minHeight: DROPDOWN_ELEMENT_HEIGHT[size],
      }}
      {...props}
    >
      <span
        className={cn(
          'absolute flex h-3.5 w-3.5 items-center justify-center',
          getDropdownItemIndicatorOffsetClassName(size),
        )}
      >
        <SelectPrimitive.ItemIndicator>
          <SvgCheck color="brand" className="size-4" />
        </SelectPrimitive.ItemIndicator>
      </span>
      {Icon && Icon}
      <span className="truncate pr-6">
        <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
      </span>
    </SelectPrimitive.Item>
  )
}
SelectItem.displayName = SelectPrimitive.Item.displayName

type SelectSeparatorProps = React.ComponentPropsWithoutRef<
  typeof SelectPrimitive.Separator
> & {
  ref?: React.Ref<React.ElementRef<typeof SelectPrimitive.Separator>>
}

/**
 * A horizontal rule for visually separating groups of items inside `SelectContent`.
 */
const SelectSeparator = ({
  className,
  ref,
  ...props
}: SelectSeparatorProps) => (
  <SelectPrimitive.Separator
    ref={ref}
    data-slot="select-separator"
    className={cn('-mx-1 my-1 h-px bg-muted', className)}
    {...props}
  />
)
SelectSeparator.displayName = SelectPrimitive.Separator.displayName

export {
  Select,
  SelectGroup,
  SelectValue,
  SelectTrigger,
  SelectContent,
  SelectLabel,
  SelectItem,
  SelectSeparator,
  SelectIconElement as SelectIcon,
}
