import { useRef, useState, type ComponentPropsWithoutRef, type ReactNode } from 'react';
import { ChevronRight } from 'lucide-react';

import { cn } from '@/lib/utils';

/**
 * A fold, built on `<details>` so the browser gives it keyboard and
 * screen-reader semantics for free.
 *
 * Four of these existed — reasoning, tool steps, a rejected proposal, an
 * activity group — and each had independently rediscovered the same two
 * browser facts:
 *
 *  - `list-style: none` removes Chrome's marker but not Safari's, which draws
 *    its own from `::-webkit-details-marker`. Without both rules one browser
 *    shows two triangles.
 *  - A flex `<summary>` drops the native marker in Chrome and keeps it in
 *    Firefox, so the chevron has to be an element we render ourselves.
 *
 * `onExpand` receives the element at the one moment worth having: inside the
 * click handler, where `open` still holds its pre-click value. That is how a
 * transcript pinned to its newest message can tell an expansion from a
 * collapse and scroll the revealed content back into view — an expansion adds
 * height below the fold, and a follow-the-tail scroller reads that as new
 * content and jumps past the very thing the user asked to see.
 */
export function Disclosure({
  summary,
  children,
  onExpand,
  defaultOpen = false,
  open,
  onOpenChange,
  className,
  summaryClassName,
  contentClassName,
  ...props
}: Omit<ComponentPropsWithoutRef<'details'>, 'onToggle' | 'open' | 'children' | 'className'> & {
  summary: ReactNode;
  children: ReactNode;
  /** Called with the `<details>` element as it is about to open, never as it closes. */
  onExpand?: (element: HTMLDetailsElement) => void;
  defaultOpen?: boolean;
  /** Supply with `onOpenChange` to drive the fold from outside. */
  open?: boolean;
  onOpenChange?: (open: boolean) => void;
  className?: string;
  summaryClassName?: string;
  contentClassName?: string;
}) {
  const ref = useRef<HTMLDetailsElement>(null);
  const [uncontrolledOpen, setUncontrolledOpen] = useState(defaultOpen);
  const isOpen = open ?? uncontrolledOpen;

  return (
    <details
      ref={ref}
      open={isOpen}
      onToggle={(event) => {
        const next = event.currentTarget.open;
        if (open === undefined) setUncontrolledOpen(next);
        onOpenChange?.(next);
      }}
      className={cn('group min-w-0', className)}
      {...props}
    >
      <summary
        onClick={() => {
          const element = ref.current;
          // `open` is still the pre-click value here, so `false` means the
          // click is about to open it.
          if (element && !element.open) onExpand?.(element);
        }}
        className={cn(
          'flex min-h-11 min-w-0 cursor-pointer list-none items-center gap-2 py-2',
          'text-sm font-medium transition-colors duration-1 ease-enter hover:text-fg',
          'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand',
          'focus-visible:ring-offset-2 focus-visible:ring-offset-bg',
          '[&::-webkit-details-marker]:hidden',
          summaryClassName,
        )}
      >
        <ChevronRight
          className="size-4 shrink-0 text-muted transition-transform duration-1 ease-enter group-open:rotate-90"
          aria-hidden
        />
        <span className="min-w-0 flex-1">{summary}</span>
      </summary>
      <div className={cn('min-w-0', contentClassName)}>{children}</div>
    </details>
  );
}
