import classnames from 'classnames';
import React, { useState, useEffect, useCallback } from 'react';
import IReactComponentProps from '../../../common/structures/IReactComponentProps';
import styles from './ScrollShadow.scss';
import { FunctionGeneric } from '../../../common/structures/Generics';

interface IProps extends IReactComponentProps {
	// Callback for accessing the scrollable content div ref from a parent - element will be passed on change
	refCallback?: FunctionGeneric;
	shadowClassName?: string;
	/** When this switches from false to true, the scrollable content will be scrolled to the top */
	scrollToTopTrigger?: boolean;
	/** Show a thin bottom border on the scroll container when not scrolling, useful for fixed buttons */
	showBottomBorderWhenScrolled?: boolean;
}

export const ScrollShadow = (props: IProps) => {
	const {
		refCallback,
		shadowClassName,
		children,
		className,
		scrollToTopTrigger,
		showBottomBorderWhenScrolled,
		...otherProps
	} = props;

	const [scrollableContent, setScrollableContent] = useState<HTMLDivElement | null>(null);
	const [showScrollShadow, setShowScrollShadow] = useState(false);

	const canScroll = (el: Element) => el.scrollHeight > el.clientHeight;

	const handleScroll = (e: any) => {
		const hideShadow = !canScroll(e.target) || e.target.scrollHeight - (e.target.scrollTop + 1) <= e.target.clientHeight;
		setShowScrollShadow(!hideShadow);
	};

	useEffect(() => {
		if (scrollToTopTrigger && scrollableContent) {
			scrollableContent.scrollTop = 0;
		}
	}, [scrollToTopTrigger])

	useEffect(() => {
		if (!scrollableContent) return;

		setShowScrollShadow(canScroll(scrollableContent));

		scrollableContent.addEventListener('scroll', handleScroll, { passive: true });

		const resizeObserver = new ResizeObserver((events) => {
			for (const event of events) {
				handleScroll(event);
			}
		});

		resizeObserver.observe(scrollableContent);

		return () => {
			scrollableContent.removeEventListener('scroll', handleScroll);
			resizeObserver.disconnect();
		};
	}, [scrollableContent]);

	const setScrollableContentRef = useCallback(
		(element: HTMLDivElement | null) => {
			setScrollableContent(element);
			if (refCallback) refCallback(element);
		},
		[refCallback]
	);

	return (
		<>
			<div
				className={classnames(styles.ScrollableContent_ScrollingDiv, className)}
				{...otherProps}
				ref={setScrollableContentRef}
			>
				{children}
			</div>
			<div className={classnames(styles.ScrollShadow__Container, shadowClassName, {
				[styles.ScrollShadow__BottomBorder]: showBottomBorderWhenScrolled && !showScrollShadow,
			})}>
				<div
					className={classnames(styles.ScrollShadow, {
						[styles.ScrollShadow__Show]: showScrollShadow,
					})}
				/>
			</div>
		</>
	);
};
