"use client";

import classNames from "classnames/dedupe";
import { memo } from "react";
import type {
  HTMLInputTypeAttribute,
  ReactElement,
  ReactNode,
  Ref,
  RefAttributes,
} from "react";
import type { UseTextFieldProps } from "../../hooks";
import { useTextField, useValidationClasses } from "../../hooks";
import type { DOMProps, FocusEvents } from "../../types";
import { ErrorMessage } from "../ErrorMessage";
import { Label } from "../Label";
import { Stack } from "../Stack";
import { adornmentWithClassName } from "./adornmentWithClassName";
import "./TextField.css";

export type TextFieldElementType = HTMLInputElement;
export interface TextFieldProps
  extends
    DOMProps,
    FocusEvents,
    UseTextFieldProps,
    RefAttributes<TextFieldElementType> {
  className?: string;
  errorMessage?: string;
  children?: ReactNode;
  label?: string;
  type?: Exclude<
    HTMLInputTypeAttribute,
    | "button"
    | "checkbox"
    | "color"
    | "date"
    | "datetime-local"
    | "file"
    | "image"
    | "month"
    | "radio"
    | "range"
    | "reset"
    | "submit"
    | "week"
  >;
  prefixInside?: ReactElement;
  prefixOutside?: ReactElement;
  suffixInside?: ReactElement;
  suffixOutside?: ReactElement;
}

export type TextFieldRef = Ref<TextFieldElementType>;

const TextFieldInner = ({ ref, ...props }: TextFieldProps) => {
  const {
    isDisabled,
    type = "text",
    isInvalid,
    className,
    label,
    errorMessage,
    children,
    isRequired,
    prefixInside,
    prefixOutside,
    suffixInside,
    suffixOutside,
    autoComplete,
    isReadOnly,
    ...otherProps
  } = props;

  const resolvedAutoComplete =
    autoComplete ??
    (type === "email" ? "email" : type === "tel" ? "tel" : undefined);

  const { inputProps, labelProps, errorMessageProps } = useTextField({
    ...props,
    autoComplete: resolvedAutoComplete,
    "aria-errormessage": errorMessage,
  });

  const hidden = type === "hidden";

  const validationClasses = useValidationClasses({ isInvalid });

  const textfieldClasses = {
    "--is-disabled": isDisabled,
    "--is-required": typeof isRequired === "boolean" && isRequired,
    "--is-optional": typeof isRequired === "boolean" && !isRequired,
    "--is-hidden": hidden,
    [className || ""]: true,
  };

  const sharedClasses = classNames(validationClasses, textfieldClasses);

  const labelClasses = classNames(
    {
      "--is-disabled": isDisabled,
    },
    validationClasses,
  );

  const containerClasses = classNames(
    "mobius",
    "mobius-text-field",
    sharedClasses,
  );

  const inputClasses = classNames(
    "mobius",
    "mobius-text-field__input",
    sharedClasses,
  );

  const inputWrapperClasses = classNames(
    "mobius-text-field__input-wrapper",
    sharedClasses,
  );

  return (
    <Stack gap="xs" className={containerClasses}>
      {label && !hidden && (
        <Label {...labelProps} className={labelClasses}>
          {label}
        </Label>
      )}
      <div className="mobius-text-field__inner-container">
        {adornmentWithClassName(
          prefixOutside,
          labelClasses,
          "mobius-text-field__prefix-outside",
        )}
        <div className={inputWrapperClasses}>
          {adornmentWithClassName(
            prefixInside,
            labelClasses,
            "mobius-text-field__prefix-inside",
          )}
          <input
            {...otherProps}
            {...inputProps}
            ref={ref}
            type={type}
            className={inputClasses}
          />
          {adornmentWithClassName(
            suffixInside,
            labelClasses,
            "mobius-text-field__suffix-inside",
          )}
        </div>
        {adornmentWithClassName(
          suffixOutside,
          labelClasses,
          "mobius-text-field__suffix-outside",
        )}
      </div>
      {children && (
        <div className="mobius-text-field__children">{children}</div>
      )}

      <ErrorMessage {...errorMessageProps} errorMessage={errorMessage} />
    </Stack>
  );
};

const TextField = memo(TextFieldInner);
TextField.displayName = "TextField";
export { TextField };
