import { clsx } from 'clsx';
import React, { FunctionComponent, ReactNode, useState, useEffect, useMemo } from 'react';

import { PromoCardProps } from './PromoCard';
import PromoCardContext from './PromoCardContext';

export type AriaRoleRadioGroup = 'radiogroup';

export interface PromoCardGroupProps {
  /**
   * Optional class name(s) to add to the group container.
   */
  className?: string;
  /**
   * The PromoCard components to display inside the group.
   */
  children: ReactNode;
  /**
   * The default checked for the group.
   */
  defaultChecked?: string;
  /**
   * Optional ID to add to the group container.
   */
  id?: string;
  /**
   * Whether the group is disabled or not.
   */
  isDisabled?: boolean;
  /**
   * Optional label to display above the group.
   */
  label?: string;
  /**
   * Optional function to call when the group value changes.
   */
  onChange?: (value: string) => void;
  /**
   * Optional ID to add to the group container for testing purposes.
   */
  testId?: string;
}

/**
 * PromoCardGroup component.
 *
 * A PromoCardGroup is a container for PromoCard components that allows the user to select one or more
 * cards from a group. It can be used to display a set of related options, and can be customized with
 * various props to suit different use cases.
 *
 * @param {ReactNode} children - The PromoCard components to display inside the group.
 * @param {string} className - Optional class name(s) to add to the group container.
 * @param {string} defaultChecked - The default value for the group.
 * @param {string} id - Optional ID to add to the group container.
 * @param {boolean} isDisabled=false - Whether the group is disabled or not.
 * @param {string} label - Optional label to display above the group.
 * @param {Function} onChange - Optional function to call when the group value changes.
 * @param {string} testId - Optional ID to add to the group container for testing purposes.
 * @returns {React.JSX.Element} - The PromoCardGroup component.
 */
const PromoCardGroup: FunctionComponent<PromoCardGroupProps> = ({
  children,
  className,
  defaultChecked = '',
  id,
  isDisabled = false,
  onChange = () => {},
  testId,
}) => {
  const [state, setState] = useState<string>(defaultChecked);
  const [containerRole, setContainerRole] = useState<string | null>(null);

  /**
   * The context value for the PromoCardGroup.
   *
   * The context value is an object that contains the current state of the
   * group, whether the group is disabled or not, and a function to call when
   * the group value changes. This value is used to provide context to child
   * PromoCard components, allowing them to interact with the group and update
   * its state.
   */
  const contextValue = useMemo(() => {
    const handleOnChange = (value: string) => {
      setState(value);
      onChange(value);
    };

    return { state, isDisabled, onChange: handleOnChange };
  }, [state, isDisabled, onChange]);

  const commonClasses = clsx(
    {
      'np-CardGroup': true,
      'is-disabled': isDisabled,
    },
    className,
  );

  const commonProps = {
    className: commonClasses,
    id,
    'data-testid': testId,
    role: containerRole as AriaRoleRadioGroup | undefined, // Add the role attribute here
  };

  useEffect(() => {
    setState(defaultChecked);

    // Collect an array of types from the children PromoCard components
    const types =
      React.Children.map(children, (child) => {
        if (React.isValidElement<PromoCardProps>(child) && child.props.type) {
          return child.props.type;
        }
        return null;
      })?.filter((type): type is 'radio' | 'checkbox' => type !== null && type !== undefined) ?? [];

    // Check if all types are the same
    const allTypesAreTheSame = types.every((type) => type === types[0]);

    // If all types are the same and the type is 'radio', set the container role
    setContainerRole(allTypesAreTheSame && types[0] === 'radio' ? 'radiogroup' : null);
  }, [defaultChecked, children]);

  return (
    <PromoCardContext.Provider value={contextValue}>
      <div {...commonProps}>{children}</div>
    </PromoCardContext.Provider>
  );
};

export default React.memo(PromoCardGroup);
