"use client";

import type { ChangeEvent } from "react";
import { useRef, useId, useState, useEffect } from "react";
import classNames from "classnames/dedupe";
import { squareTick, square } from "@simplybusiness/icons";
import { ErrorMessage } from "../ErrorMessage";
import type { CheckboxElementType, CheckboxProps } from "./types";
import { spaceDelimitedList } from "../../utils/spaceDelimitedList";
import { useValidationClasses } from "../../hooks";
import { Icon } from "../Icon";
import { Stack } from "../Stack";
import "./Checkbox.css";

export const Checkbox = ({ ref, ...props }: CheckboxProps) => {
  const {
    id,
    label,
    isDisabled,
    isRequired,
    isInvalid,
    onChange,
    className,
    errorMessage,
    selected,
    defaultSelected = false,
    isReadOnly,
    isLastItem,
    name,
    value,
    ["aria-describedby"]: ariaDescribedBy,
    ...rest
  } = props;
  const [checked, setChecked] = useState<boolean>(defaultSelected);
  const fallbackRef = useRef<HTMLInputElement>(null);
  const refObj = ref || fallbackRef;
  const inputId = useId();

  const isControlled = typeof selected === "boolean";
  useEffect(() => {
    if (isControlled) {
      setChecked(selected);
    }
  }, [selected, isControlled]);

  const validationClasses = useValidationClasses({ isInvalid });
  const sharedClasses = classNames(
    {
      "--is-disabled": isDisabled,
      "--is-selected": checked,
      "--is-required": typeof isRequired === "boolean" && isRequired,
      "--is-optional": typeof isRequired === "boolean" && !isRequired,
    },
    validationClasses,
  );
  // Append an additional wrapper class name to differenitate from input
  const wrapperClasses = classNames(
    "mobius",
    "mobius-checkbox",
    sharedClasses,
    className,
  );
  const labelClasses = classNames("mobius-checkbox__label", sharedClasses);
  const inputClasses = classNames("mobius-checkbox__input", sharedClasses);
  const iconClasses = classNames("mobius-checkbox__icon", sharedClasses);
  const errorMessageId = useId();
  const shouldErrorMessageShow = errorMessage ? errorMessageId : undefined;
  const describedBy = spaceDelimitedList([
    shouldErrorMessageShow,
    ariaDescribedBy,
  ]);
  const labelId = useId();

  const handleChange = (event: ChangeEvent<CheckboxElementType>) => {
    setChecked(!checked);
    if (onChange) {
      onChange(event, isLastItem);
    }
  };

  return (
    <Stack gap="xs" className={wrapperClasses}>
      <label className={labelClasses}>
        <input
          aria-describedby={describedBy}
          aria-errormessage={shouldErrorMessageShow}
          aria-invalid={isInvalid}
          aria-labelledby={labelId}
          readOnly={isReadOnly}
          disabled={isDisabled}
          ref={refObj}
          className={inputClasses}
          onChange={handleChange}
          type="checkbox"
          checked={checked}
          required={isRequired}
          id={id || inputId}
          name={name}
          value={value}
          {...rest}
        />
        <Icon
          icon={checked ? squareTick : square}
          size="md"
          className={iconClasses}
        />
        <span id={labelId} className="mobius-checkbox__visible-label">
          {label}
        </span>
      </label>
      <ErrorMessage id={errorMessageId} errorMessage={errorMessage} />
    </Stack>
  );
};

Checkbox.displayName = "Checkbox";
