import "./Toolbar.css";

import { useEffect, useLayoutEffect, useRef, useState } from "react";
import { createPortal } from "react-dom";

export type ToolbarPortalSlot = "breadcrumbs" | "search" | "actions";

export type ToolbarPortalProps = {
	slot: ToolbarPortalSlot;
	children: React.ReactNode;
};

export function ToolbarPortal({ slot, children }: ToolbarPortalProps) {
	// Target element IDs: ob-toolbar-breadcrumbs, ob-toolbar-search, ob-toolbar-actions
	const target = document.getElementById(`ob-toolbar-${slot}`);
	if (!target) return null;
	return createPortal(children, target);
}

export type BreadcrumbItem = {
	label: string;
	href?: string;
};

export type ToolbarBreadcrumbsProps = {
	items: BreadcrumbItem[];
};

function BreadcrumbsInner({ items }: ToolbarBreadcrumbsProps) {
	let [firstVisible, setFirstVisible] = useState(0);
	let [overflowOpen, setOverflowOpen] = useState(false);
	let navRef = useRef<HTMLElement>(null);
	let listRef = useRef<HTMLOListElement>(null);
	let overflowItemRef = useRef<HTMLLIElement>(null);

	// Detect overflow: increment firstVisible until the list fits within the nav.
	// Runs after every render so it keeps pushing items into overflow until the
	// list stops overflowing or only the last item (current page) remains.
	useLayoutEffect(() => {
		let nav = navRef.current;
		let list = listRef.current;
		if (!nav || !list) return;

		if (list.scrollWidth > nav.clientWidth && firstVisible < items.length - 1) {
			setFirstVisible((prev) => prev + 1);
		}
	});

	// ResizeObserver resets firstVisible to 0 on container resize so that the
	// overflow calculation starts fresh at the new container width.
	useEffect(() => {
		let nav = navRef.current;
		if (!nav) return;

		let observer = new ResizeObserver(() => {
			setFirstVisible(0);
		});

		observer.observe(nav);
		return () => observer.disconnect();
	}, []);

	// Close the overflow dropdown when the user clicks outside of it.
	useEffect(() => {
		if (!overflowOpen) return;

		function handleClickOutside(event: MouseEvent) {
			if (overflowItemRef.current && !overflowItemRef.current.contains(event.target as Node)) {
				setOverflowOpen(false);
			}
		}

		document.addEventListener("mousedown", handleClickOutside);
		return () => document.removeEventListener("mousedown", handleClickOutside);
	}, [overflowOpen]);

	let hiddenItems = items.slice(0, firstVisible);
	let visibleItems = items.slice(firstVisible);

	return (
		<nav className="ob-breadcrumbs" aria-label="Breadcrumb" ref={navRef}>
			<ol className="ob-breadcrumbs__list" ref={listRef}>
				{firstVisible > 0 && (
					<li className="ob-breadcrumbs__item ob-breadcrumbs__overflow" ref={overflowItemRef}>
						<button
							type="button"
							aria-label="Show hidden breadcrumbs"
							aria-expanded={overflowOpen}
							aria-haspopup="true"
							onClick={() => setOverflowOpen((prev) => !prev)}
						>
							&hellip;
						</button>
						{overflowOpen && (
							<ul className="ob-breadcrumbs__overflow-menu" role="menu">
								{hiddenItems.map((item, index) => (
									// oxlint-disable-next-line react/no-array-index-key -- stable list with no reordering
									<li key={index} role="menuitem">
										{item.href ? <a href={item.href}>{item.label}</a> : <span>{item.label}</span>}
									</li>
								))}
							</ul>
						)}
					</li>
				)}
				{visibleItems.map((item, index) => {
					let actualIndex = firstVisible + index;
					let isCurrent = actualIndex === items.length - 1;
					return (
						<li key={actualIndex} className="ob-breadcrumbs__item">
							{isCurrent ? (
								<span aria-current="page">{item.label}</span>
							) : item.href ? (
								<a href={item.href}>{item.label}</a>
							) : (
								<span>{item.label}</span>
							)}
						</li>
					);
				})}
			</ol>
		</nav>
	);
}

export function ToolbarBreadcrumbs({ items }: ToolbarBreadcrumbsProps) {
	return (
		<ToolbarPortal slot="breadcrumbs">
			<BreadcrumbsInner items={items} />
		</ToolbarPortal>
	);
}
