import { ReactNode, useCallback, useEffect, useRef, useState } from "react";

const FADE = 28; // px of taper at each overflowing edge

/**
 * Horizontally scrollable row of chips/tabs. Fades whichever edge still has
 * content behind it, so a clipped item reads as "scroll for more" rather than
 * as a layout bug — and contains its overscroll so a sideways swipe never
 * triggers the browser's back gesture.
 */
export function ScrollRow({
	children,
	className = "",
	wrapperClassName = "",
	bleed = true,
}: {
	children: ReactNode;
	/** Applied to the scrolling element (layout of the items). */
	className?: string;
	/** Applied to the positioned wrapper (borders, margins). */
	wrapperClassName?: string;
	/** Extend to the screen edges on phones so the row reads as scrollable. */
	bleed?: boolean;
}) {
	const ref = useRef<HTMLDivElement>(null);
	const [edges, setEdges] = useState({ start: false, end: false });

	const update = useCallback(() => {
		const el = ref.current;
		if (!el) return;
		const max = el.scrollWidth - el.clientWidth;
		setEdges({ start: el.scrollLeft > 4, end: max > 4 && el.scrollLeft < max - 4 });
	}, []);

	useEffect(() => {
		update();
		const el = ref.current;
		if (!el || typeof ResizeObserver === "undefined") return;
		const ro = new ResizeObserver(update);
		ro.observe(el);
		for (const child of Array.from(el.children)) ro.observe(child);
		return () => ro.disconnect();
	}, [update]);

	const mask = `linear-gradient(to right, ${
		edges.start ? `transparent 0, black ${FADE}px` : "black 0"
	}, ${edges.end ? `black calc(100% - ${FADE}px), transparent 100%` : "black 100%"})`;

	return (
		<div className={`relative ${bleed ? "-mx-3 sm:mx-0" : ""} ${wrapperClassName}`}>
			<div
				ref={ref}
				onScroll={update}
				style={{ maskImage: mask, WebkitMaskImage: mask }}
				className={`no-scrollbar flex overflow-x-auto overscroll-x-contain ${
					bleed ? "px-3 sm:px-0" : ""
				} ${className}`}
			>
				{children}
			</div>
		</div>
	);
}
