import { h, type FunctionComponent, type PointerEventHandler } from 'preact'
import cn from 'classnames'

type Styles<C extends string, D extends string, S extends string> = {
  base: string
  autoHeight?: string
  fullWidth?: string
  icon?: string
  invert?: string
} & Record<C, string> &
  Record<D, string> &
  Record<S, string>

type Props<C extends string, D extends string, I extends string, S extends string> = {
  autoHeight?: boolean
  className?: string
  color?: C
  design?: D
  disabled?: boolean
  fullWidth?: boolean
  icon?: I
  iconSize?: string
  invert?: boolean
  onClick?: PointerEventHandler<HTMLButtonElement>
  size?: S
  tabIndex?: number
  tag?: string
  title?: string
  type?: 'button' | 'reset' | 'submit'
}

type Options<C extends string, D extends string, I extends string, S extends string> = {
  defaults: Partial<Props<C, D, I, S>>
  icons: Record<I, string>
  styles: Styles<C, D, S>
}

export default function buttonFactory<
  C extends string = never,
  D extends string = never,
  I extends string = never,
  S extends string = never,
>({ defaults, icons, styles }: Options<C, D, I, S>) {
  const Button: FunctionComponent<Props<C, D, I, S>> = ({
    autoHeight = defaults?.autoHeight,
    children,
    className,
    color = defaults?.color,
    design = defaults?.design,
    disabled,
    fullWidth = defaults?.fullWidth,
    icon = defaults?.icon,
    iconSize,
    invert = defaults?.invert,
    onClick,
    size = defaults?.size,
    tabIndex,
    title,
    type,
  }) => (
    <button
      className={cn(
        styles.base,
        design && styles[design],
        size && styles[size],
        color && styles[color],
        autoHeight && styles.autoHeight,
        fullWidth && styles.fullWidth,
        invert && styles.invert,
        className,
      )}
      style={iconSize && { '--icon-size': iconSize }}
      disabled={disabled}
      onClick={onClick}
      tabIndex={tabIndex}
      title={title}
      type={type}
    >
      {icon && <i className={styles.icon} style={{ maskImage: `url(${icons[icon] || icon})` }} />}
      {children && <span>{children}</span>}
    </button>
  )

  return Button
}
