/**
 * The "keep scrolling" cue for the Company ID page. On a short screen the
 * first paint ends right after the ring and the completeness card, with no
 * partially visible block below to say the page continues - merchants stopped
 * there and never reached the rings or the setup rail.
 */

import React, { useCallback, useEffect, useRef, useState } from 'react';
import { LuChevronDown } from 'react-icons/lu';

/** Hide the cue once the merchant has clearly started scrolling. */
const SCROLLED_AWAY_PX = 40;

/** Below this much hidden content the page is effectively all visible. */
const WORTH_CUEING_PX = 160;

interface ScrollCueProps {
  /** Short line telling the merchant what waits below. */
  label: string;
}

/**
 * Find the ancestor that actually scrolls. The plugin shell scrolls its
 * nested `main`, not the window, so the cue cannot read window scroll.
 * A candidate must both declare a scrolling overflow AND overflow its box:
 * the Company ID page root sets `overflow-x: hidden`, which makes the
 * computed `overflow-y` resolve to `auto` even though the element never
 * scrolls, and stopping there would leave the cue permanently hidden. When
 * nothing overflows yet, the OUTERMOST in-app candidate wins (the shell's
 * `main`, never the document itself), so a page that only grows tall after
 * mount still measures the container it will actually scroll.
 *
 * @param {HTMLElement | null} from - Node to start walking up from.
 * @returns {HTMLElement | null} The scrolling ancestor, or null.
 */
function findScroller(from: HTMLElement | null): HTMLElement | null {
  let node = from?.parentElement ?? null;
  let fallback: HTMLElement | null = null;
  while (node) {
    const overflowY = window.getComputedStyle(node).overflowY;
    const scrollable = overflowY === 'auto' || overflowY === 'scroll';
    if (scrollable && node.scrollHeight > node.clientHeight) return node;
    if (
      scrollable &&
      node !== document.body &&
      node !== document.documentElement
    ) {
      fallback = node;
    }
    node = node.parentElement;
  }
  // Nothing above us scrolls: the document itself does, which is the
  // case inside an embedded admin. Without this the cue resolved to
  // null and never appeared at all.
  return (
    fallback ??
    (document.scrollingElement as HTMLElement | null) ??
    document.documentElement
  );
}

/**
 * The scroller's own child that holds the page, so a ResizeObserver watches
 * the block that actually grows as the record lands.
 *
 * @param {HTMLElement} scroller - The scrolling ancestor.
 * @param {HTMLElement} anchor - The cue's in-flow anchor node.
 * @returns {Element | null} The direct child wrapping the anchor.
 */
function scrollerContent(
  scroller: HTMLElement,
  anchor: HTMLElement
): Element | null {
  let node: HTMLElement | null = anchor;
  while (node && node.parentElement !== scroller) {
    node = node.parentElement;
  }

  return node ?? scroller.firstElementChild;
}

/**
 * The cue: a centered pill over the bottom of the scrolling area that bobs
 * until the merchant scrolls, and scrolls the page down when clicked.
 *
 * @param {ScrollCueProps} props - The label shown inside the pill.
 * @returns {JSX.Element} The cue element (empty anchor while hidden).
 */
export function ScrollCue({ label }: ScrollCueProps): JSX.Element {
  const anchorRef = useRef<HTMLDivElement | null>(null);
  const scrollerRef = useRef<HTMLElement | null>(null);
  const [visible, setVisible] = useState(false);
  // Fixed to the viewport, but centered on the scrolling area rather than the
  // window: the sidebar and the copilot panel both shift that area sideways.
  const [box, setBox] = useState<{ left: number; width: number } | null>(null);

  useEffect(() => {
    const anchor = anchorRef.current;
    const scroller = findScroller(anchor);
    scrollerRef.current = scroller;
    if (!scroller) return;

    const update = () => {
      const rect = scroller.getBoundingClientRect();
      setBox({ left: rect.left, width: rect.width });
      const hidden = scroller.scrollHeight - scroller.clientHeight;
      setVisible(
        hidden > WORTH_CUEING_PX && scroller.scrollTop < SCROLLED_AWAY_PX
      );
    };

    update();
    scroller.addEventListener('scroll', update, { passive: true });
    window.addEventListener('resize', update);
    // The page grows as the record lands, so re-measure on content resize
    // instead of trusting the mount-time height.
    const content = anchor ? scrollerContent(scroller, anchor) : null;
    const observer = content ? new ResizeObserver(update) : null;
    if (content && observer) observer.observe(content);

    return () => {
      scroller.removeEventListener('scroll', update);
      window.removeEventListener('resize', update);
      observer?.disconnect();
    };
  }, []);

  const scrollDown = useCallback(() => {
    const scroller = scrollerRef.current;
    if (!scroller) return;
    const reduced = window.matchMedia(
      '(prefers-reduced-motion: reduce)'
    ).matches;
    scroller.scrollBy({
      top: Math.round(scroller.clientHeight * 0.82),
      behavior: reduced ? 'auto' : 'smooth',
    });
  }, []);

  return (
    <div ref={anchorRef} aria-hidden={!visible}>
      {visible && box && (
        <div
          style={{
            position: 'fixed',
            bottom: 18,
            left: box.left,
            width: box.width,
            zIndex: 30,
            display: 'flex',
            justifyContent: 'center',
            pointerEvents: 'none',
          }}
        >
          <button
            type="button"
            onClick={scrollDown}
            className="pointer-events-auto inline-flex cursor-pointer items-center gap-2 rounded-full border border-[#e1e3e5] bg-white/95 py-2 pl-4 pr-3 text-[12.5px] font-medium text-[#202223] shadow-[0_8px_28px_rgba(0,0,0,0.16)] backdrop-blur transition-colors hover:border-[#C8175D]/60"
          >
            {label}
            <LuChevronDown
              size={15}
              className="scroll-cue-bob text-[#C8175D]"
            />
          </button>
        </div>
      )}
    </div>
  );
}
