'use client';

import type { ChangeEvent, ChangeEventHandler } from 'react';
import * as React from 'react';
import { classNames } from '@vkontakte/vkjs';
import { useAdaptivity } from '../../hooks/useAdaptivity';
import { useExternRef } from '../../hooks/useExternRef';
import { useMergeProps } from '../../hooks/useMergeProps';
import { callMultiple } from '../../lib/callMultiple';
import { getFormFieldModeFromSelectType } from '../../lib/select';
import { useIsomorphicLayoutEffect } from '../../lib/useIsomorphicLayoutEffect';
import { warnOnce } from '../../lib/warnOnce';
import type { HasAlign, HasDataAttribute, HasRootRef } from '../../types';
import { DropdownIcon } from '../DropdownIcon/DropdownIcon';
import { FormField, type FormFieldProps } from '../FormField/FormField';
import { RootComponent } from '../RootComponent/RootComponent';
import type { SelectType } from '../Select/Select';
import { SelectTypography } from '../SelectTypography/SelectTypography';
import styles from '../Select/Select.module.css';

const warn = warnOnce('NativeSelect');

const densityClassNames = {
  none: styles.densityNone,
  compact: styles.densityCompact,
};

export type SelectValue = Exclude<
  React.SelectHTMLAttributes<HTMLSelectElement>['value'],
  undefined
> | null;

export type NativeSelectValue = Exclude<SelectValue, null>;

export const NOT_SELECTED = {
  NATIVE: '__vkui_internal_Select_not_selected__',
  CUSTOM: null,
};

/**
 * @visibleName NativeSelect
 */
export const remapFromSelectValueToNativeValue = (value: SelectValue): NativeSelectValue =>
  value === NOT_SELECTED.CUSTOM ? NOT_SELECTED.NATIVE : value;

export const remapFromNativeValueToSelectValue = (value: NativeSelectValue): SelectValue =>
  value === NOT_SELECTED.NATIVE ? NOT_SELECTED.CUSTOM : value;

export type NativeHTMLSelectProps = Omit<
  React.SelectHTMLAttributes<HTMLSelectElement>,
  'multiple' | 'value' | 'defaultValue' | 'onChange'
>;

export interface NativeSelectProps
  extends Pick<
      React.SelectHTMLAttributes<HTMLSelectElement>,
      'disabled' | 'required' | 'autoFocus' | 'name' | 'form' | 'onFocus' | 'onBlur' | 'onClick'
    >,
    Omit<
      React.HTMLAttributes<HTMLDivElement>,
      'defaultValue' | 'onChange' | 'onFocus' | 'onBlur' | 'onClick'
    >,
    HasRootRef<HTMLDivElement>,
    HasAlign,
    Pick<FormFieldProps, 'before' | 'status'> {
  /**
   * Свойства, которые можно прокинуть внутрь компонента:
   * - `root`: свойства для прокидывания в корень компонента;
   * - `select`: свойства для прокидывания в нативный `select`.
   */
  slotProps?:
    | {
        root?:
          | (Omit<React.HTMLAttributes<HTMLDivElement>, 'children'> &
              HasDataAttribute &
              HasRootRef<HTMLDivElement>)
          | undefined;
        select?:
          | (NativeHTMLSelectProps & HasRootRef<HTMLSelectElement> & HasDataAttribute)
          | undefined;
      }
    | undefined;
  /**
   * @deprecated Since 7.9.0. Вместо этого используйте `slotProps={ select: { getRootRef: ... } }`.
   */
  getRef?: React.Ref<HTMLSelectElement> | undefined;
  /**
   * Выбранное значение.
   *
   * > ⚠️  Важно: При прокидывании `undefined` компонент будет считаться `Uncontrolled`.
   * >
   * > Не используйте `undefined`, чтобы показать невыбранное состояние. Вместо этого используйте `null`.
   */
  value?: SelectValue | undefined;
  /**
   * См. `value`.
   */
  defaultValue?: SelectValue | undefined;
  /**
   * Обработчик, срабатывающий при изменении выбранного значения.
   * Вторым параметром прокидывается новое значение.
   *
   * > ⚠️ Лучше использовать второй параметр при работе с компонентом.
   */
  onChange?: ((e: ChangeEvent<HTMLSelectElement>, newValue: SelectValue) => void) | undefined;
  /**
   * Текст-подсказка при отсутствии выбранного значения.
   */
  placeholder?: string | undefined;
  /**
   * Флаг для включения многострочного режима.
   */
  multiline?: boolean | undefined;
  /**
   * Тип селекта, влияющий на отображение.
   */
  selectType?: SelectType | undefined;
  /**
   * Иконка раскрывающегося списка.
   */
  icon?: React.ReactNode | undefined;
}

/**
 * @see https://vkui.io/components/native-select
 */
export const NativeSelect = ({
  // NativeSelectProps
  children,
  align,
  placeholder,
  multiline,
  selectType = 'default',
  status,
  icon = <DropdownIcon />,
  before,
  onChange,
  value,
  defaultValue,
  getRef,

  // Select props
  disabled,
  required,
  autoFocus,
  id,
  name,
  form,
  onClick,
  onFocus,
  onBlur,

  slotProps,
  ...restProps
}: NativeSelectProps): React.ReactNode => {
  /* istanbul ignore if: не проверяем в тестах */
  if (process.env.NODE_ENV === 'development' && getRef) {
    warn('Свойство `getRef` устаревшее, используйте `slotProps={ select: { getRootRef: ... } }`');
  }

  const [title, setTitle] = React.useState('');
  const [empty, setEmpty] = React.useState(false);
  const { density = 'none' } = useAdaptivity();

  const { className, style, getRootRef, ...rootRest } = useMergeProps(restProps, slotProps?.root);

  const { getRootRef: getSelectRef, ...selectRest } = useMergeProps(
    { getRootRef: getRef, disabled, required, autoFocus, id, name, form, onClick, onFocus, onBlur },
    slotProps?.select,
  );

  const selectRef = useExternRef(getSelectRef);

  const checkSelectedOption = () => {
    const selectedOption = selectRef.current?.options[selectRef.current.selectedIndex];
    if (selectedOption) {
      setTitle(selectedOption.text);
      setEmpty(selectedOption.value === NOT_SELECTED.NATIVE && placeholder != null);
    }
  };

  const _onChange: ChangeEventHandler<HTMLSelectElement> = (e) => {
    const newValue = remapFromNativeValueToSelectValue(e.target.value);
    if (e.target.value === NOT_SELECTED.NATIVE) {
      e.target.value = '';
    }
    if (e.currentTarget.value === NOT_SELECTED.NATIVE) {
      e.currentTarget.value = '';
    }
    onChange?.(e, newValue);
  };
  useIsomorphicLayoutEffect(checkSelectedOption, [children]);

  return (
    <FormField
      Component="div"
      className={classNames(
        styles.host,
        'vkuiInternalNativeSelect',
        before && styles.hasBefore,
        empty && styles.empty,
        multiline && styles.multiline,
        align === 'center' && styles.alignCenter,
        align === 'right' && styles.alignRight,
        density !== 'regular' && densityClassNames[density],
        className,
      )}
      style={style}
      getRootRef={getRootRef}
      disabled={selectRest.disabled}
      before={before}
      after={icon}
      status={status}
      mode={getFormFieldModeFromSelectType(selectType)}
      {...rootRest}
    >
      <RootComponent
        Component="select"
        baseClassName={styles.el}
        value={value !== undefined ? remapFromSelectValueToNativeValue(value) : value}
        defaultValue={
          defaultValue !== undefined
            ? remapFromSelectValueToNativeValue(defaultValue)
            : defaultValue
        }
        onChange={callMultiple(_onChange, checkSelectedOption)}
        getRootRef={selectRef}
        {...selectRest}
      >
        {placeholder && <option value={NOT_SELECTED.NATIVE}>{placeholder}</option>}
        {children}
      </RootComponent>
      <div className={styles.container} aria-hidden>
        <SelectTypography className={styles.title} selectType={selectType}>
          {title}
        </SelectTypography>
      </div>
    </FormField>
  );
};
