import { BidiText } from '@/components/foundation/BidiText';
import { classNames } from '@/lib/components/classNames';
import {
  forwardRef,
  type InputHTMLAttributes,
  type SelectHTMLAttributes,
  type TextareaHTMLAttributes,
} from 'react';
import styles from './TextInput.module.css';

export type TextInputType = 'text' | 'email' | 'tel' | 'url' | 'search';

interface TextInputProps extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type'> {
  type?: TextInputType;
  invalid?: boolean;
  describedBy?: string | undefined;
  bidiValue?: boolean;
}

export const TextInput = forwardRef<HTMLInputElement, TextInputProps>(function TextInput(
  { type = 'text', invalid = false, describedBy, bidiValue = false, className, ...props },
  ref,
) {
  const dir = bidiValue || type === 'email' || type === 'tel' || type === 'url' ? 'ltr' : undefined;
  return (
    <input
      ref={ref}
      type={type}
      dir={dir}
      className={classNames(styles.control, className)}
      aria-invalid={invalid || undefined}
      aria-describedby={describedBy}
      {...props}
    />
  );
});

interface TextAreaProps extends TextareaHTMLAttributes<HTMLTextAreaElement> {
  invalid?: boolean;
  describedBy?: string | undefined;
  characterCount?: { current: number; max: number; label: string };
}

export function TextArea({
  invalid = false,
  describedBy,
  characterCount,
  className,
  ...props
}: TextAreaProps) {
  const countId = characterCount && props.id ? `${props.id}-count` : undefined;
  const ids = [describedBy, countId].filter(Boolean).join(' ') || undefined;
  return (
    <div className={styles.textareaGroup}>
      <textarea
        className={classNames(styles.control, styles.textarea, className)}
        aria-invalid={invalid || undefined}
        aria-describedby={ids}
        {...props}
      />
      {characterCount ? (
        <p id={countId} className={styles.count} aria-live="polite">
          <BidiText>{`${characterCount.current}/${characterCount.max}`}</BidiText>{' '}
          {characterCount.label}
        </p>
      ) : null}
    </div>
  );
}

interface SelectProps extends SelectHTMLAttributes<HTMLSelectElement> {
  invalid?: boolean;
  describedBy?: string | undefined;
}

export function Select({
  invalid = false,
  describedBy,
  className,
  children,
  ...props
}: SelectProps) {
  return (
    <select
      className={classNames(styles.control, styles.select, className)}
      aria-invalid={invalid || undefined}
      aria-describedby={describedBy}
      {...props}
    >
      {children}
    </select>
  );
}
