import { h, type FunctionComponent } from 'preact'
import { useCallback, useEffect, useRef, useState } from 'preact/hooks'
import cn from 'classnames'
import mergeStyles from '../utils/merge_styles.ts'

type Props = {
  autoFocus?: boolean
  className?: string
  cols?: number
  defaultValue?: string
  disabled?: boolean
  label: string
  name: string
  placeholder?: string
  required?: boolean
  rows: number
  showValidity?: boolean
}
export default function textareaFactory(styles: {
  base?: string
  touched?: string
  element?: string
  label?: string
  statusIcon?: string
}): FunctionComponent<Props> {
  if (Array.isArray(styles)) {
    styles = mergeStyles(styles)
  }

  const Textarea = ({
    autoFocus,
    className,
    cols,
    defaultValue,
    disabled,
    label,
    name,
    placeholder,
    required,
    rows,
    showValidity = true,
  }: Props) => {
    const [touched, setTouched] = useState(false)
    const textareaRef = useRef<HTMLTextAreaElement>(null)

    const onBlur = useCallback(() => setTouched(true), [])

    useEffect(() => {
      const form = textareaRef.current!.form!
      const resetTouched = setTouched.bind(null, false)

      form.addEventListener('reset', resetTouched)

      return () => form.removeEventListener('reset', resetTouched)
    }, [])

    return (
      <div className={cn(styles.base, touched && styles.touched, className)}>
        <textarea
          autoFocus={autoFocus}
          className={styles.element}
          cols={cols}
          disabled={disabled}
          name={name}
          onBlur={onBlur}
          placeholder={placeholder ?? label}
          ref={textareaRef}
          defaultValue={defaultValue}
          required={required}
          rows={rows}
        />

        {showValidity && <i className={styles.statusIcon} />}

        {/**
         * <label> comes after <textarea> to be able to use next sibling CSS selector
         * (see public input.module.scss). Use `flexbox` and `order` if <label> needs to be
         * displayed before <textarea> (see admin input.module.scss)
         */}
        {label && (
          <label className={styles.label} htmlFor={name}>
            {label}
          </label>
        )}
      </div>
    )
  }

  return Textarea
}
