<script>
	import { onMount, onDestroy } from 'svelte';

	/** @type {boolean} */
	export let open = false;

	/** @type {() => void} */
	export let onClose;

	let dragOffset = 0;
	let dragging = false;
	let locked = false;

	/** @type {number|null} */
	let startY = null;
	/** @type {number|null} */
	let startX = null;
	/** @type {number|null} */
	let lastY = null;
	/** @type {number} */
	let lastTime = 0;
	/** @type {number} */
	let velocity = 0;

	const DISMISS_THRESHOLD = 150;
	const VELOCITY_THRESHOLD = 0.5;

	/** @type {HTMLDivElement|null} */
	let sheetEl = null;

	// Body scroll lock
	$: if (open) {
		document.documentElement.style.overflow = 'hidden';
	} else {
		document.documentElement.style.overflow = '';
		dragOffset = 0;
		dragging = false;
		locked = false;
	}

	/**
	 * @param {KeyboardEvent} e
	 */
	function handleKeydown(e) {
		if (!open) return;
		if (e.key === 'Escape') {
			e.preventDefault();
			onClose();
		}
		// Focus trap
		if (e.key === 'Tab' && sheetEl) {
			const focusable = sheetEl.querySelectorAll(
				'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
			);
			if (focusable.length === 0) return;
			const first = /** @type {HTMLElement} */ (focusable[0]);
			const last = /** @type {HTMLElement} */ (focusable[focusable.length - 1]);
			if (e.shiftKey && document.activeElement === first) {
				e.preventDefault();
				last.focus();
			} else if (!e.shiftKey && document.activeElement === last) {
				e.preventDefault();
				first.focus();
			}
		}
	}

	onMount(() => {
		document.addEventListener('keydown', handleKeydown);
	});

	onDestroy(() => {
		document.removeEventListener('keydown', handleKeydown);
		document.documentElement.style.overflow = '';
	});

	/**
	 * @param {TouchEvent} e
	 */
	function handleDragStart(e) {
		const touch = e.touches[0];
		startY = touch.clientY;
		startX = touch.clientX;
		lastY = touch.clientY;
		lastTime = Date.now();
		locked = false;
		dragging = false;
		velocity = 0;
	}

	/**
	 * @param {TouchEvent} e
	 */
	function handleDragMove(e) {
		if (startY === null || startX === null) return;
		const touch = e.touches[0];
		const deltaY = touch.clientY - startY;
		const deltaX = touch.clientX - startX;

		if (!locked && !dragging) {
			if (Math.abs(deltaY) > 10 && Math.abs(deltaY) > Math.abs(deltaX) * 2) {
				dragging = true;
				locked = true;
			} else if (Math.abs(deltaX) > 10) {
				startY = null;
				startX = null;
				return;
			}
		}

		if (dragging) {
			if (e.cancelable) e.preventDefault();
			// Only allow dragging down (positive)
			dragOffset = Math.max(0, deltaY);

			// Track velocity
			const now = Date.now();
			const dt = now - lastTime;
			if (dt > 0 && lastY !== null) {
				velocity = (touch.clientY - lastY) / dt;
			}
			lastY = touch.clientY;
			lastTime = now;
		}
	}

	function handleDragEnd() {
		if (!dragging) {
			startY = null;
			startX = null;
			return;
		}

		if (dragOffset > DISMISS_THRESHOLD || velocity > VELOCITY_THRESHOLD) {
			onClose();
		}
		dragOffset = 0;
		dragging = false;
		startY = null;
		startX = null;
	}

	function handleScrimClick() {
		onClose();
	}

	$: transitioning = !dragging && dragOffset === 0;
</script>

<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions -->
<div class="sheet-scrim" class:open on:click={handleScrimClick}>
	<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-static-element-interactions a11y-interactive-supports-focus -->
	<div
		bind:this={sheetEl}
		class="sheet"
		class:open
		class:transitioning
		style:transform={open
			? `translateY(${dragOffset}px)`
			: undefined}
		on:click|stopPropagation
		role="dialog"
		aria-modal="true"
	>
		<div
			class="sheet-handle"
			on:touchstart={handleDragStart}
			on:touchmove={handleDragMove}
			on:touchend={handleDragEnd}
		>
			<div class="handle-bar"></div>
		</div>
		<div class="sheet-content">
			<slot />
		</div>
	</div>
</div>

<style>
	.sheet-scrim {
		position: fixed;
		inset: 0;
		z-index: 50;
		background: rgba(0, 0, 0, 0.5);
		opacity: 0;
		pointer-events: none;
		transition: opacity 0.3s ease;
	}

	.sheet-scrim.open {
		opacity: 1;
		pointer-events: auto;
	}

	.sheet {
		position: fixed;
		bottom: 0;
		left: 0;
		right: 0;
		max-height: 85dvh;
		background: var(--bg-primary);
		border-radius: 1rem 1rem 0 0;
		box-shadow: var(--shadow-lg);
		transform: translateY(100%);
		display: flex;
		flex-direction: column;
		z-index: 51;
	}

	.sheet.transitioning {
		transition: transform 0.3s ease-out;
	}

	.sheet.open {
		transform: translateY(0);
	}

	.sheet-handle {
		display: flex;
		justify-content: center;
		padding: var(--spacing-sm) 0;
		cursor: grab;
		touch-action: none;
		flex-shrink: 0;
	}

	.handle-bar {
		width: 2.5rem;
		height: 0.25rem;
		background: var(--text-tertiary);
		border-radius: 9999px;
	}

	.sheet-content {
		flex: 1;
		overflow-y: auto;
		overscroll-behavior: contain;
		padding: 0 var(--spacing-md) var(--spacing-lg);
	}

	@media (min-width: 769px) {
		.sheet {
			max-width: 480px;
			left: 50%;
			margin-left: -240px;
		}
	}
</style>
