import type { ReactNode, SelectHTMLAttributes } from 'react';
import { useCallback, useId, useMemo, useRef } from 'react';
import type { SelectOption } from '../types';
import './select.css';

export type SelectProps = {
  label?: string;
  options: SelectOption[];
  placeholder?: string;
  error?: boolean;
  errorMessage?: string;
  helperText?: string;
  multiple?: boolean;
  size?: number;
  showPrefixIcon?: boolean;
  prefixIcon?: ReactNode;
  filterIcon?: ReactNode;
  iconBackground?: 'none' | 'rounded' | 'square';
  onSelectionChange?: (value: string | string[]) => void;
} & Omit<SelectHTMLAttributes<HTMLSelectElement>, 'size' | 'onChange'> & {
    onChange?: SelectHTMLAttributes<HTMLSelectElement>['onChange'];
  };

export function Select({
  label = '',
  options,
  placeholder = '',
  disabled = false,
  error = false,
  errorMessage = '',
  helperText = '',
  required,
  'aria-label': ariaLabelProp,
  'aria-describedby': ariaDescribedByProp,
  multiple = false,
  size: sizeAttr,
  showPrefixIcon = false,
  prefixIcon,
  filterIcon,
  iconBackground = 'none',
  value,
  defaultValue,
  onSelectionChange,
  onChange,
  className,
  id: idProp,
  ...rest
}: SelectProps) {
  const uid = useId();
  const id = idProp ?? `ccl-select-${uid.replace(/:/g, '')}`;
  const labelId = `${id}-label`;
  const hintId = `${id}-hint`;
  const errorId = `${id}-error`;
  const selectRef = useRef<HTMLSelectElement>(null);

  const describedBy = useMemo(() => {
    const ids: string[] = [];
    if (ariaDescribedByProp) ids.push(ariaDescribedByProp);
    if (helperText) ids.push(hintId);
    if (error && errorMessage) ids.push(errorId);
    return ids.length ? ids.join(' ') : undefined;
  }, [ariaDescribedByProp, helperText, hintId, error, errorMessage, errorId]);

  const onContainerClick = useCallback(
    (e: React.MouseEvent) => {
      if (e.target instanceof HTMLSelectElement) return;
      const el = selectRef.current;
      if (!el || disabled) return;
      e.preventDefault();
      e.stopPropagation();
      el.focus({ preventScroll: true });
      requestAnimationFrame(() => {
        const rect = el.getBoundingClientRect();
        const cx = rect.left + rect.width / 2;
        const cy = rect.top + rect.height / 2;
        for (const type of ['mousedown', 'mouseup', 'click'] as const) {
          el.dispatchEvent(
            new MouseEvent(type, {
              bubbles: true,
              cancelable: true,
              clientX: cx,
              clientY: cy,
              view: window,
            })
          );
        }
      });
    },
    [disabled]
  );

  const allOptions =
    placeholder && !multiple
      ? [{ value: '', label: placeholder, disabled: true } as SelectOption, ...options]
      : options;

  return (
    <div className="ccl-select-wrapper">
      {label ? (
        <label htmlFor={id} id={labelId} className="ccl-select__label">
          {label}
          {required ? (
            <span className="ccl-select__required" aria-label="required">
              *
            </span>
          ) : null}
        </label>
      ) : null}

      <div
        className="ccl-select-container"
        onClick={onContainerClick}
        onKeyDown={(e) => {
          if (e.key === 'Enter' || e.key === ' ') {
            e.preventDefault();
            onContainerClick(e as unknown as React.MouseEvent);
          }
        }}
        tabIndex={0}
        role="button"
      >
        {showPrefixIcon ? (
          <div className={`ccl-select__prefix-icon ccl-select__prefix-icon--${iconBackground}`}>
            {prefixIcon ?? <span className="ccl-select__default-prefix-icon" aria-hidden>🔍</span>}
          </div>
        ) : null}

        <select
          ref={selectRef}
          id={id}
          className={['ccl-select', showPrefixIcon ? 'ccl-select--with-prefix' : '', className]
            .filter(Boolean)
            .join(' ')}
          disabled={disabled}
          multiple={multiple}
          size={sizeAttr}
          required={required}
          aria-invalid={error}
          aria-label={ariaLabelProp}
          aria-describedby={describedBy}
          aria-required={required}
          value={value}
          defaultValue={defaultValue}
          onChange={(e) => {
            onChange?.(e);
            const target = e.target as HTMLSelectElement;
            if (multiple) {
              const selectedValues = Array.from(target.selectedOptions).map((o) => o.value);
              onSelectionChange?.(selectedValues);
            } else {
              onSelectionChange?.(target.value);
            }
          }}
          {...rest}
        >
          {allOptions.map((opt) => (
            <option key={opt.value} value={opt.value} disabled={opt.disabled}>
              {opt.label}
            </option>
          ))}
        </select>

        {filterIcon ? <div className="ccl-select__dropdown-icon">{filterIcon}</div> : null}
      </div>

      {helperText ? (
        <span id={hintId} className="ccl-select__helper">
          {helperText}
        </span>
      ) : null}

      {error && errorMessage ? (
        <span id={errorId} className="ccl-select__error" role="alert">
          {errorMessage}
        </span>
      ) : null}
    </div>
  );
}
