"use client";

import classNames from "classnames/dedupe";
import type { ReactNode, RefAttributes } from "react";
import { useEffect, useState, useId } from "react";
import { usePrefersReducedMotion } from "@simplybusiness/mobius-hooks";
import type { DOMProps } from "../../types";

import type {
  AccordionElementType,
  AccordionLinkElementType,
} from "./AccordionLink";
import { AccordionLink } from "./AccordionLink";
import "./Accordion.css";

export interface AccordionProps
  extends DOMProps, RefAttributes<AccordionElementType> {
  /** Custom class name for setting specific CSS */
  className?: string;
  /** Link text to show accordion content */
  showText?: string | ReactNode;
  /** Link text to hide accordion content */
  hideText?: string | ReactNode;
  /** Whether header is above or below content */
  headerPosition?: "top" | "bottom";
  /** Whether to expand the accordion initially */
  startOpen?: boolean | undefined;
  /** Semantic heading for title link (default is span) */
  linkElementType?: AccordionLinkElementType;
  /** Callback that fires when the accordion link is clicked (fires before toggle) */
  onClick?: (e?: React.MouseEvent) => void;
  /** Callback that fires each time the accordion is opened */
  onOpen?: () => void;
  /** Callback that fires each time the accordion is closed */
  onClose?: () => void;
  /** Callback that fires each time the accordion state changes */
  onChange?: (state: boolean) => void;
  children?: ReactNode;
  headerChildren?: ReactNode;
}

export const Accordion = ({ ref, ...props }: AccordionProps) => {
  const {
    showText = "See more",
    hideText = "See less",
    headerPosition = "top",
    startOpen = false,
    linkElementType,
    onClick,
    onOpen,
    onClose,
    onChange = () => {},
    headerChildren,
    ...rest
  } = props;

  const [accordionState, setAccordionState] = useState({
    open: startOpen,
    withAnimation: false,
  });
  const prefersReducedMotion = usePrefersReducedMotion();

  const containerClasses = classNames(
    "mobius",
    "mobius-accordion",
    props.className,
    {
      "--should-animate": accordionState.withAnimation && !prefersReducedMotion,
      "--is-open": accordionState.open,
    },
  );
  const contentContainerClasses = classNames(
    "mobius-accordion__content-container",
    {
      "--is-open": accordionState.open,
    },
  );
  const contentClasses = classNames("mobius-accordion__content", {
    "--is-open": accordionState.open,
  });

  const linkText = accordionState.open ? hideText : showText;

  const handleChange = () => {
    // Fire events
    if (!accordionState.open && onOpen) {
      onOpen();
    }
    if (accordionState.open && onClose) {
      onClose();
    }
    if (onChange) {
      onChange(!accordionState.open);
    }
    setAccordionState({
      open: !accordionState.open,
      withAnimation: true,
    });
  };

  useEffect(() => {
    setAccordionState({
      open: startOpen,
      withAnimation: true,
    });
  }, [startOpen]);

  // Generate unique IDs for accessibility
  const baseId = useId();
  const accordionId = `accordion-header-${baseId}`;
  const contentId = `accordion-panel-${baseId}`;

  return (
    <div ref={ref} {...rest} className={containerClasses}>
      {headerPosition === "top" && (
        <AccordionLink
          text={linkText}
          elementType={linkElementType}
          toggle={handleChange}
          onClick={onClick}
          ariaExpanded={accordionState.open}
          headerChildren={headerChildren}
          accordionId={accordionId}
          contentId={contentId}
        />
      )}
      <div
        id={contentId}
        className={contentContainerClasses}
        aria-hidden={!accordionState.open}
        aria-labelledby={accordionId}
        role="region"
      >
        <div className={contentClasses}>{props.children}</div>
      </div>
      {headerPosition === "bottom" && (
        <AccordionLink
          text={linkText}
          toggle={handleChange}
          onClick={onClick}
          ariaExpanded={accordionState.open}
          headerChildren={headerChildren}
          accordionId={accordionId}
          contentId={contentId}
        />
      )}
    </div>
  );
};

Accordion.displayName = "Accordion";
