<script lang="ts">
	import tooltipPosition from '$stores/tooltipPosition';
	import { fade } from 'svelte/transition';

	export let id: string;
	export let pin: string | undefined = undefined;
	export let content: string;
	export let disable: boolean = false;
	export let pinPosition: 'top' | 'bottom' | 'left' | 'right' = 'top';
	export let offset = 10;
	export let tooltipTransitionTime = 200;
	export let tooltipHideDelay = 10;
	export let debounceDelay = 100;
	export let tooltipOpacityTransitionTime = 200;

	let tooltipDiv: HTMLDivElement | null = null;
	let hideTimeout: number | null = null;
	let debounceTimer: number | null = null;
	let opacity = 0;

	const createTooltip = () => {
		if (!tooltipDiv) {
			tooltipDiv = document.createElement('div');
			tooltipDiv.style.position = 'absolute';
			tooltipDiv.style.zIndex = '1000';
			opacity = 0;
			tooltipDiv.style.pointerEvents = 'none';
			tooltipDiv.style.whiteSpace = 'nowrap';

			tooltipDiv.classList.add('card');
			tooltipDiv.classList.add('card-small');
			document.body.appendChild(tooltipDiv);
		}
	};

	const updateTooltipPosition = (x: number, y: number) => {
		if (tooltipDiv) {
			if (pin) {
				const pinnedElement = document.getElementById(pin);

				if (pinnedElement) {
					const rect = pinnedElement.getBoundingClientRect();
					const tooltipWidth = tooltipDiv.offsetWidth;
					const tooltipHeight = tooltipDiv.offsetHeight;
					let top = rect.top - tooltipHeight - offset;
					let left = rect.left + rect.width / 2 - tooltipWidth / 2;

					// pin pos
					if (pinPosition === 'top') {
						top = rect.top - tooltipHeight - offset;
						left = rect.left + rect.width / 2 - tooltipWidth / 2;
					} else if (pinPosition === 'bottom') {
						top = rect.top + rect.height + offset;
						left = rect.left + rect.width / 2 - tooltipWidth / 2;
					} else if (pinPosition === 'left') {
						top = rect.top + rect.height / 2 - tooltipHeight / 2;
						left = rect.left - tooltipWidth - offset;
					} else if (pinPosition === 'right') {
						top = rect.top + rect.height / 2 - tooltipHeight / 2;
						left = rect.left + rect.width + offset;
					}

					if (left < 0) left = offset;
					if (left + tooltipWidth > window.innerWidth)
						left = window.innerWidth - tooltipWidth - offset;
					if (top < 0) top = rect.top + rect.height + offset;

					tooltipPosition.set({
						x: left,
						y: top + window.scrollY
					});

					return;
				}
			}

			const tooltipWidth = tooltipDiv.offsetWidth;
			const tooltipHeight = tooltipDiv.offsetHeight;
			let top = y - tooltipHeight - offset;
			let left = x - tooltipWidth / 2;

			if (left < 0) left = offset;
			if (left + tooltipWidth > window.innerWidth) left = window.innerWidth - tooltipWidth - offset;
			if (top < 0) top = y + offset;

			tooltipPosition.set({
				x: left,
				y: top
			});
		}
	};

	const showTooltip = (content: string, x: number, y: number) => {
		if (hideTimeout !== null) {
			clearTimeout(hideTimeout);

			hideTimeout = null;
		}

		createTooltip();

		if (tooltipDiv) {
			tooltipDiv.innerHTML = content.replace(/\n/g, '<br>');

			updateTooltipPosition(x, y);
			setTimeout(() => {
				if (tooltipDiv) {
					opacity = 1;
				}
			}, 10);
		}
	};

	const hideTooltip = () => {
		setTimeout(() => {
			if (tooltipDiv) {
				opacity = 0;

				hideTimeout = window.setTimeout(() => {
					if (tooltipDiv) {
						document.body.removeChild(tooltipDiv);

						tooltipDiv = null;
					}
				}, tooltipTransitionTime);
			}
		}, tooltipHideDelay);
	};

	const handleMouseEnter = (event: MouseEvent) => {
		if (disable) return;

		if (hideTimeout !== null) {
			clearTimeout(hideTimeout);

			hideTimeout = null;
		}

		if (!tooltipDiv) showTooltip(content, event.pageX, event.pageY);
	};

	const handleMouseMove = (event: MouseEvent) => {
		if (debounceTimer !== null) clearTimeout(debounceTimer);

		debounceTimer = window.setTimeout(() => {
			if (tooltipDiv && opacity === 1) updateTooltipPosition(event.pageX, event.pageY);
		}, debounceDelay);
	};

	const handleMouseLeave = () => {
		hideTooltip();
	};
</script>

<div
	{id}
	data-tooltippin={pin}
	on:mouseenter={handleMouseEnter}
	on:mousemove={handleMouseMove}
	on:mouseleave={handleMouseLeave}
	role="tooltip"
>
	<slot />
</div>

{#if tooltipDiv}
	<div
		class="tooltip card card-small"
		style={`left: ${$tooltipPosition.x}px; top: ${$tooltipPosition.y}px; opacity: ${opacity}; --tooltip-opacity-transition-time: ${tooltipOpacityTransitionTime}ms;`}
	>
		{#key content}
			<span
				in:fade={{ duration: tooltipTransitionTime }}
				out:fade={{ duration: tooltipTransitionTime }}
			>
				{@html content}
			</span>
		{/key}
	</div>
{/if}

<style>
	.tooltip {
		position: absolute;
		z-index: 1000;
		transition: opacity var(--tooltip-opacity-transition-time) ease-in-out;
		pointer-events: none;
		white-space: nowrap;
	}
</style>
