"use client";

import classNames from "classnames/dedupe";
import {
  type ChangeEvent,
  type ReactElement,
  Children,
  cloneElement,
  isValidElement,
  useEffect,
  useId,
  useRef,
  useState,
} from "react";
import { useValidationClasses } from "../../hooks";
import { spaceDelimitedList } from "../../utils/spaceDelimitedList";
import { ErrorMessage } from "../ErrorMessage";
import { Label } from "../Label";
import { Checkbox } from "./Checkbox";
import type { CheckboxElementType, CheckboxGroupProps } from "./types";
import "./CheckboxGroup.css";

export const CheckboxGroup = ({ ref, ...props }: CheckboxGroupProps) => {
  const {
    label,
    isDisabled = false,
    isRequired,
    isInvalid,
    orientation = "vertical",
    onChange,
    className,
    errorMessage,
    children,
    defaultValue = [],
    isReadOnly,
    itemsPerRow,
    lastItemDisables = false,
    ...rest
  } = props;
  const [selected, setSelected] = useState<string[]>(defaultValue);

  // Handle controlled behavior - update state when defaultValue prop changes
  // Use a ref to track if we've initialized to avoid infinite re-renders
  const isInitializedRef = useRef(false);
  const prevDefaultValueRef = useRef<string[]>(defaultValue);

  useEffect(() => {
    // Only update if defaultValue has actually changed (shallow comparison)
    const hasChanged =
      defaultValue.length !== prevDefaultValueRef.current.length ||
      defaultValue.some(
        (val, index) => val !== prevDefaultValueRef.current[index],
      );

    if (hasChanged || !isInitializedRef.current) {
      setSelected(defaultValue);
      prevDefaultValueRef.current = defaultValue;
      isInitializedRef.current = true;
    }
  }, [defaultValue]);

  const checkboxGroupClasses = classNames(
    "mobius",
    "mobius-checkbox-group",
    className,
    {
      "--is-horizontal": orientation === "horizontal",
      "--is-vertical": orientation === "vertical",
      "--is-required": typeof isRequired === "boolean" && isRequired,
      "--is-optional": typeof isRequired === "boolean" && !isRequired,
    },
  );
  const validationClasses = useValidationClasses({ isInvalid });
  const labelClasses = classNames(
    {
      "--is-disabled": isDisabled,
    },
    validationClasses,
  );
  const errorMessageId = useId();
  const shouldErrorMessageShow = errorMessage ? errorMessageId : undefined;
  const describedBy = spaceDelimitedList([
    shouldErrorMessageShow,
    props["aria-describedby"],
  ]);
  const labelId = useId();

  const handleChange = (
    event: ChangeEvent<CheckboxElementType>,
    isLastItem = false,
  ) => {
    const {
      target: { value, checked },
    } = event;
    let newValue = [...selected];

    if (!checked) {
      newValue = selected.filter(item => item !== value);
    }

    if (checked) {
      newValue = [...selected, value];
    }

    if (checked && lastItemDisables && isLastItem) {
      newValue = [value];
    }

    setSelected(newValue);
    onChange?.(newValue);
  };

  const childrenArray = Children.toArray(children);
  const lastCheckbox = childrenArray
    .filter(
      child =>
        isValidElement(child) && (child as ReactElement).type === Checkbox,
    )
    .pop() as ReactElement<CheckboxElementType> | undefined;
  const lastCheckboxIsChecked =
    lastCheckbox && selected.includes(lastCheckbox.props.value);

  return (
    <div
      {...rest}
      aria-labelledby={props["aria-labelledby"] || labelId}
      ref={ref}
      className={checkboxGroupClasses}
      role="group"
      style={
        {
          "--checkbox-items-per-row": itemsPerRow || Children.count(children),
        } as React.CSSProperties
      }
    >
      {label && (
        <Label elementType="span" id={labelId} className={labelClasses}>
          {label}
        </Label>
      )}
      <div className="mobius-checkbox-group__wrapper">
        {childrenArray.map(child => {
          if (isValidElement(child)) {
            // lastItemDisables support
            const isLastItem = child === lastCheckbox;
            const isChildDisabled =
              isDisabled ||
              (lastItemDisables && lastCheckboxIsChecked && !isLastItem);

            const childProps = child.props as {
              value: string;
              onChange?: (event: ChangeEvent<HTMLInputElement>) => void;
            };

            return cloneElement(
              child as ReactElement,
              {
                isDisabled: isChildDisabled,
                isRequired,
                isReadOnly,
                isInvalid,
                isLastItem,
                selected: selected.includes(childProps.value),
                onChange: childProps.onChange || handleChange,
                "aria-describedby": describedBy,
              } as Record<string, unknown>,
            );
          }

          return child;
        })}
      </div>
      {errorMessage && (
        <ErrorMessage id={errorMessageId} errorMessage={errorMessage} />
      )}
    </div>
  );
};

CheckboxGroup.displayName = "CheckboxGroup";
