import {
  forwardRef,
  useId,
  type InputHTMLAttributes,
  type SelectHTMLAttributes,
  type TextareaHTMLAttributes,
  type ReactNode,
} from "react";
import { cx } from "./utils";
import { Icon, type IconName } from "./Icon";

type FieldRenderArg = {
  /** id du contrôle (à passer à <input>/<textarea>/<select>) */
  id: string;
  /** id du message à passer en aria-describedby (peut être undefined) */
  describedBy: string | undefined;
  /** L'état d'invalidité, à passer en aria-invalid */
  invalid: boolean | undefined;
};

type FieldProps = {
  label?: ReactNode;
  hint?: ReactNode;
  error?: ReactNode;
  success?: ReactNode;
  required?: boolean;
  className?: string;
  id?: string;
  children: (arg: FieldRenderArg) => ReactNode;
};

export function Field({
  label,
  hint,
  error,
  success,
  required,
  className,
  id: idProp,
  children,
}: FieldProps) {
  const auto = useId();
  const id = idProp ?? auto;
  const messageId = `${id}-message`;
  const hasMessage = Boolean(error || success || hint);
  const describedBy = hasMessage ? messageId : undefined;
  const state = error ? "error" : success ? "success" : undefined;
  return (
    <div className={cx("mmg-field", state && `mmg-field--${state}`, className)}>
      {label && (
        <label
          className={cx(
            "mmg-field__label",
            required && "mmg-field__label--required",
          )}
          htmlFor={id}
        >
          {label}
        </label>
      )}
      {children({ id, describedBy, invalid: error ? true : undefined })}
      {error ? (
        <span id={messageId} className="mmg-field__error" role="alert">
          {error}
        </span>
      ) : success ? (
        <span id={messageId} className="mmg-field__success">
          {success}
        </span>
      ) : hint ? (
        <span id={messageId} className="mmg-field__hint">
          {hint}
        </span>
      ) : null}
    </div>
  );
}

/* — Input ————————————————————————————— */
export type InputProps = Omit<InputHTMLAttributes<HTMLInputElement>, "size"> & {
  label?: ReactNode;
  hint?: ReactNode;
  error?: ReactNode;
  success?: ReactNode;
  size?: "sm" | "md" | "lg";
  prefixIcon?: IconName;
  suffixIcon?: IconName;
};

export const Input = forwardRef<HTMLInputElement, InputProps>(function Input(
  { label, hint, error, success, size = "md", prefixIcon, suffixIcon, required, className, ...rest },
  ref,
) {
  const cls = cx(
    "mmg-input",
    size !== "md" && `mmg-input--${size}`,
    className,
  );
  return (
    <Field label={label} hint={hint} error={error} success={success} required={required}>
      {({ id, describedBy, invalid }) =>
        prefixIcon || suffixIcon ? (
          <span className={cx("mmg-input-wrap", suffixIcon && "mmg-input-wrap--with-suffix")}>
            {prefixIcon && (
              <span className="mmg-input-wrap__icon"><Icon name={prefixIcon} /></span>
            )}
            <input
              ref={ref}
              id={id}
              className={cls}
              required={required}
              aria-describedby={describedBy}
              aria-invalid={invalid}
              {...rest}
            />
            {suffixIcon && (
              <span className="mmg-input-wrap__icon mmg-input-wrap__icon--suffix">
                <Icon name={suffixIcon} />
              </span>
            )}
          </span>
        ) : (
          <input
            ref={ref}
            id={id}
            className={cls}
            required={required}
            aria-describedby={describedBy}
            aria-invalid={invalid}
            {...rest}
          />
        )
      }
    </Field>
  );
});

/* — Textarea ————————————————————————————— */
export type TextareaProps = TextareaHTMLAttributes<HTMLTextAreaElement> & {
  label?: ReactNode;
  hint?: ReactNode;
  error?: ReactNode;
  success?: ReactNode;
  fieldSize?: "sm" | "md" | "lg";
};
export const Textarea = forwardRef<HTMLTextAreaElement, TextareaProps>(
  function Textarea(
    { label, hint, error, success, fieldSize = "md", required, className, ...rest },
    ref,
  ) {
    const cls = cx(
      "mmg-textarea",
      fieldSize !== "md" && `mmg-textarea--${fieldSize}`,
      className,
    );
    return (
      <Field label={label} hint={hint} error={error} success={success} required={required}>
        {({ id, describedBy, invalid }) => (
          <textarea
            ref={ref}
            id={id}
            className={cls}
            required={required}
            aria-describedby={describedBy}
            aria-invalid={invalid}
            {...rest}
          />
        )}
      </Field>
    );
  },
);

/* — Select ————————————————————————————— */
export type SelectOption = { value: string; label: string; disabled?: boolean };
export type SelectProps = Omit<SelectHTMLAttributes<HTMLSelectElement>, "size"> & {
  label?: ReactNode;
  hint?: ReactNode;
  error?: ReactNode;
  success?: ReactNode;
  size?: "sm" | "md" | "lg";
  options: SelectOption[];
  placeholder?: string;
};
export const Select = forwardRef<HTMLSelectElement, SelectProps>(function Select(
  { label, hint, error, success, size = "md", options, placeholder, required, className, ...rest },
  ref,
) {
  const cls = cx("mmg-select", size !== "md" && `mmg-select--${size}`, className);
  return (
    <Field label={label} hint={hint} error={error} success={success} required={required}>
      {({ id, describedBy, invalid }) => (
        <select
          ref={ref}
          id={id}
          className={cls}
          required={required}
          aria-describedby={describedBy}
          aria-invalid={invalid}
          {...rest}
        >
          {placeholder && <option value="">{placeholder}</option>}
          {options.map((o) => (
            <option key={o.value} value={o.value} disabled={o.disabled}>
              {o.label}
            </option>
          ))}
        </select>
      )}
    </Field>
  );
});

/* — Checkbox / Radio ————————————————————————————— */
type CheckProps = InputHTMLAttributes<HTMLInputElement> & {
  label: ReactNode;
};
export const Checkbox = forwardRef<HTMLInputElement, CheckProps>(function Checkbox(
  { label, className, ...rest },
  ref,
) {
  return (
    <label className={cx("mmg-check", className)}>
      <input ref={ref} type="checkbox" className="mmg-check__input" {...rest} />
      <span className="mmg-check__label">{label}</span>
    </label>
  );
});
export const Radio = forwardRef<HTMLInputElement, CheckProps>(function Radio(
  { label, className, ...rest },
  ref,
) {
  return (
    <label className={cx("mmg-check", className)}>
      <input ref={ref} type="radio" className="mmg-check__input" {...rest} />
      <span className="mmg-check__label">{label}</span>
    </label>
  );
});

/* — Switch ————————————————————————————— */
export const Switch = forwardRef<HTMLInputElement, CheckProps>(function Switch(
  { label, className, ...rest },
  ref,
) {
  return (
    <label className={cx("mmg-switch", className)}>
      <input ref={ref} type="checkbox" role="switch" className="mmg-switch__input" {...rest} />
      <span className="mmg-check__label">{label}</span>
    </label>
  );
});
