import { type Ref } from 'react';
import {
  Button as AriaButton,
  Select as AriaSelect,
  type SelectProps as AriaSelectProps,
  FieldError,
  ListBox,
  ListBoxItem,
  Popover,
  SelectValue,
} from 'react-aria-components';

import clsx from 'clsx';
import { tv } from 'tailwind-variants';

export type SelectOption = {
  label: string;
  value: string | number;
  isDisabled?: boolean;
};

const selectStyle = tv({
  base: 'flex items-center text-start gap-4 w-full bg-white text-sm text-dabeeo-black-34 border border-dabeeo-gray-be cursor-default px-3 h-9 min-w-[120px] outline-0',
  variants: {
    isDisabled: {
      true: 'text-dabeeo-gray-99 bg-dabeeo-gray-eb border-dabeeo-gray-be cursor-not-allowed',
      false:
        'hover:text-dabeeo-black-34 hover:border-dabeeo-black-34 data-pressed:text-dabeeo-black-34 data-pressed:border-dabeeo-black-34',
    },
    isReadOnly: {
      true: 'text-dabeeo-black-34 bg-dabeeo-yellow-secondary cursor-auto',
    },
    isFocused: {
      true: 'border-dabeeo-black-34',
    },
  },
});

export interface SelectProps<T extends SelectOption>
  extends Omit<AriaSelectProps<T>, 'selectionMode' | 'onChange' | 'children'> {
  items?: Iterable<T>;
  onChange?: (value: T['value']) => void;
  maxHeight?: number;
  placement?: 'bottom' | 'top';
  ref?: Ref<HTMLButtonElement>;
  isReadOnly?: boolean;
}

export const Select = <T extends SelectOption>(props: SelectProps<T>) => {
  const { className, items, onChange, maxHeight, placement = 'bottom', ref, isReadOnly, ...restProps } = props;

  return (
    <AriaSelect
      selectionMode="single"
      aria-label="선택"
      className={clsx('group/select', className)}
      {...restProps}
      onChange={(key) => {
        onChange?.(key as T['value']);
      }}
    >
      <AriaButton
        ref={ref}
        className={(renderProps) =>
          selectStyle({ ...renderProps, isDisabled: props.isDisabled || isReadOnly, isReadOnly })}
        {...{ isDisabled: props.isDisabled || isReadOnly }}
      >
        <SelectValue className="flex-1 text-sm data-placeholder:text-dabeeo-gray-99">
          {({ selectedText, defaultChildren }) => selectedText || defaultChildren}
        </SelectValue>
        <svg
          xmlns="http://www.w3.org/2000/svg"
          width="10"
          height="5"
          viewBox="0 0 10 5"
          fill="none"
          className={clsx('group-data-open/select:rotate-180 transition', isReadOnly && 'hidden')}
        >
          <path d="M0 0H10L5 5L0 0Z" fill="#BEBEBE" />
        </svg>
      </AriaButton>
      <FieldError className="mt-0.5 text-dabeeo-red text-xs" />
      <Popover className="w-(--trigger-width)" offset={0.5} placement={placement}>
        <ListBox
          items={items}
          style={{ maxHeight: maxHeight ? `${maxHeight}px` : 'inherit' }}
          className={clsx(
            'w-full outline-0 box-border border-l border-r border-dabeeo-gray-be max-h-[inherit] bg-white overflow-auto',
            placement === 'top' ? 'border-t' : 'border-b'
          )}
        >
          {(item) => (
            <ListBoxItem
              id={item.value}
              className="outline-0 px-3 py-2 text-sm font-semibold text-dabeeo-black-34 data-selected:text-dabeeo-navy-main data-selected:bg-dabeeo-yellow-secondary data-selected:font-bold data-selected:hover:bg-dabeeo-gray-eb focus:bg-dabeeo-gray-eb hover:bg-dabeeo-gray-eb data-disabled:text-dabeeo-gray-be"
              isDisabled={!!item.isDisabled}
            >
              {item.label}
            </ListBoxItem>
          )}
        </ListBox>
      </Popover>
    </AriaSelect>
  );
};
