import { useEffect, useMemo, useRef, useState } from "react";
import { Document, Page, pdfjs } from "react-pdf";
import "./PdfViewer.css";

// Use new URL(..., import.meta.url) — understood by Rolldown/Rollup on all platforms,
// including Windows where ?raw query strings are invalid in file paths.
pdfjs.GlobalWorkerOptions.workerSrc = new URL("pdfjs-dist/build/pdf.worker.min.mjs", import.meta.url).toString();

type PdfViewerProps = { url?: string; file?: string | Blob | ArrayBuffer };

export function PdfViewer({ url, file }: PdfViewerProps) {
	let [numPages, setNumPages] = useState(0);
	let [error, setError] = useState<string | null>(null);
	let containerRef = useRef<HTMLDivElement | null>(null);
	let [containerWidth, setContainerWidth] = useState<number>(0);
	let [containerHeight, setContainerHeight] = useState<number>(0);
	let [scale, setScale] = useState<number>(1);
	let [naturalPageSize, setNaturalPageSize] = useState<{
		w: number;
		h: number;
	} | null>(null);

	const pages = useMemo(() => Array.from({ length: numPages }, (_, i) => i + 1), [numPages]);

	// Observe container size to support fit-to-page calculations
	useEffect(() => {
		let el = containerRef.current;
		if (!el) return;
		let ro = new ResizeObserver((entries) => {
			let cr = entries[0].contentRect;
			setContainerWidth(Math.max(1, Math.floor(cr.width)));
			setContainerHeight(Math.max(1, Math.floor(cr.height)));
		});
		ro.observe(el);
		setContainerWidth(Math.max(1, Math.floor(el.clientWidth)));
		setContainerHeight(Math.max(1, Math.floor(el.clientHeight)));
		return () => ro.disconnect();
	}, []);

	// Intercept browser zoom gestures and zoom the PDF instead
	useEffect(() => {
		let el = containerRef.current;
		if (!el) return;

		const clamp = (v: number, min: number, max: number) => Math.min(max, Math.max(min, v));
		const zoomBy = (delta: number) => {
			// Smooth exponential zoom for trackpads/mice
			const factor = Math.exp(delta * 0.001);
			setScale((s) => clamp(s * factor, 0.25, 5));
		};

		const onWheel = (e: WheelEvent) => {
			if (e.ctrlKey) {
				// ctrl+wheel is usually browser zoom; override for PDF zoom
				e.preventDefault();
				zoomBy(-e.deltaY);
			}
		};

		const onKeyDown = (e: KeyboardEvent) => {
			if (!e.ctrlKey) return;
			const key = e.key;
			if (key === "+" || key === "=" || key === "Add") {
				e.preventDefault();
				setScale((s) => clamp(s * 1.1, 0.25, 5));
			} else if (key === "-" || key === "_" || key === "Subtract") {
				e.preventDefault();
				setScale((s) => clamp(s / 1.1, 0.25, 5));
			} else if (key === "0") {
				e.preventDefault();
				setScale(1);
			}
		};

		el.addEventListener("wheel", onWheel, { passive: false });
		window.addEventListener("keydown", onKeyDown, { passive: false });
		return () => {
			el.removeEventListener("wheel", onWheel as EventListener);
			window.removeEventListener("keydown", onKeyDown as EventListener);
		};
	}, []);

	// When source changes, reset auto-fit baseline
	useEffect(() => {
		setNaturalPageSize(null);
		setScale(1);
	}, [url, file]);

	// Compute base width to fit full page into container, then apply zoom scale.
	// If we don't yet know page's natural size, default to fit-to-width.
	const fitBaseWidth = useMemo(() => {
		if (!naturalPageSize) return containerWidth || 1000;
		const { w, h } = naturalPageSize;
		if (w <= 0 || h <= 0) return containerWidth || 1000;
		// width to fit by height: containerHeight * (w/h)
		const widthByHeight = (containerHeight || 1000) * (w / h);
		// Fit to page means respecting both constraints
		return Math.min(Math.max(1, containerWidth || 1000), Math.max(1, widthByHeight));
	}, [naturalPageSize, containerWidth, containerHeight]);

	const pageWidth = Math.max(1, Math.round(fitBaseWidth * scale));

	return (
		<div ref={containerRef} className="pdfViewerContainer">
			<div className="pdfViewerContent">
				<Document
					file={file ?? url}
					onLoadSuccess={({ numPages }) => setNumPages(numPages)}
					onLoadError={(e) => setError((e as Error)?.message ?? "Failed to load PDF")}
				>
					{pages.map((pageNumber) => (
						<Page
							key={pageNumber}
							pageNumber={pageNumber}
							renderTextLayer={false}
							renderAnnotationLayer={false}
							width={pageWidth}
							onLoadSuccess={(p) => {
								// Capture natural page size once for fit-to-page calculation
								try {
									// pdf.js viewport at scale 1
									const vp = p.getViewport({ scale: 1 });
									const w = Number(vp?.width) || Number((p as any).width) || 0;
									const h = Number(vp?.height) || Number((p as any).height) || 0;
									if (w > 0 && h > 0 && !naturalPageSize) {
										setNaturalPageSize({ w, h });
									}
								} catch {
									// Ignore, fallback to fit-to-width when unknown
								}
							}}
						/>
					))}
				</Document>
			</div>
			{error && <div style={{ color: "#b00020", padding: 8 }}>Error loading PDF: {error}</div>}
		</div>
	);
}
