<script lang="ts">
	import { invalidateAll } from '$app/navigation';

	interface Props {
		entryId: string;
		onClose: () => void;
		onCapture?: (imageId: string) => void;
	}

	let { entryId, onClose, onCapture }: Props = $props();

	// Stream and capture state
	let stream: MediaStream | null = $state(null);
	let capturedBlob: Blob | null = $state(null);
	let capturedPreviewUrl: string | null = $state(null);
	let error: string | null = $state(null);
	let isUploading = $state(false);
	let facingMode: 'environment' | 'user' = $state('environment');

	// Element refs
	let videoElement: HTMLVideoElement;
	let canvasElement: HTMLCanvasElement;

	// Feature detection
	const hasCameraSupport =
		typeof navigator !== 'undefined' && !!navigator.mediaDevices?.getUserMedia;

	const isSecureContext = typeof window !== 'undefined' && window.isSecureContext;

	async function startCamera() {
		// Check browser support
		if (!hasCameraSupport) {
			error = 'Camera not supported in this browser. Use the file upload option instead.';
			return;
		}

		// Check secure context (HTTPS required except localhost)
		if (!isSecureContext) {
			error = 'Camera requires HTTPS connection.';
			return;
		}

		// Stop existing stream if any
		if (stream) {
			stream.getTracks().forEach((track) => track.stop());
			stream = null;
		}

		error = null;

		// Set up timeout for camera start
		const timeoutId = setTimeout(() => {
			if (!stream) {
				error = 'Camera took too long to start. Please try again.';
			}
		}, 10000);

		try {
			stream = await navigator.mediaDevices.getUserMedia({
				video: {
					facingMode,
					width: { ideal: 1920 },
					height: { ideal: 1080 }
				},
				audio: false
			});

			clearTimeout(timeoutId);

			if (videoElement) {
				videoElement.srcObject = stream;
				await videoElement.play();
			}
		} catch (err) {
			clearTimeout(timeoutId);

			if (err instanceof DOMException) {
				if (err.name === 'NotAllowedError') {
					error = 'Camera access denied. Check browser settings to allow camera.';
				} else if (err.name === 'NotFoundError') {
					error = 'No camera found on this device. Use file upload instead.';
				} else {
					error = 'Could not access camera: ' + err.message;
				}
			} else {
				error = 'Could not access camera.';
			}
		}
	}

	function stopCamera() {
		if (stream) {
			stream.getTracks().forEach((track) => track.stop());
			stream = null;
		}
		if (videoElement) {
			videoElement.srcObject = null;
		}
	}

	async function capturePhoto() {
		if (!videoElement || !canvasElement) return;

		const ctx = canvasElement.getContext('2d');
		if (!ctx) return;

		// Set canvas dimensions to match video
		canvasElement.width = videoElement.videoWidth;
		canvasElement.height = videoElement.videoHeight;

		// Draw current video frame to canvas
		ctx.drawImage(videoElement, 0, 0);

		// Convert canvas to blob
		return new Promise<void>((resolve) => {
			canvasElement.toBlob(
				(blob) => {
					if (blob) {
						capturedBlob = blob;
						capturedPreviewUrl = URL.createObjectURL(blob);
						// Stop camera to save battery while reviewing
						stopCamera();
					}
					resolve();
				},
				'image/jpeg',
				0.9
			);
		});
	}

	async function retake() {
		// Revoke preview URL to free memory
		if (capturedPreviewUrl) {
			URL.revokeObjectURL(capturedPreviewUrl);
		}
		capturedBlob = null;
		capturedPreviewUrl = null;

		// Restart camera
		await startCamera();
	}

	async function confirmAndUpload() {
		if (!capturedBlob) return;

		isUploading = true;

		try {
			const formData = new FormData();
			formData.append('image', capturedBlob, 'camera-capture.jpg');
			formData.append('entryId', entryId);

			const response = await fetch('?/uploadImage', {
				method: 'POST',
				body: formData
			});

			const result = await response.json();

			if (result.type === 'success' && result.data?.imageId) {
				onCapture?.(result.data.imageId);
				await invalidateAll();
				handleClose();
			} else {
				error = result.data?.error || 'Failed to upload photo.';
				isUploading = false;
			}
		} catch {
			error = 'Failed to upload photo. Please try again.';
			isUploading = false;
		}
	}

	function switchCamera() {
		facingMode = facingMode === 'environment' ? 'user' : 'environment';
		startCamera();
	}

	function handleClose() {
		stopCamera();

		// Revoke any preview URL
		if (capturedPreviewUrl) {
			URL.revokeObjectURL(capturedPreviewUrl);
			capturedPreviewUrl = null;
		}

		capturedBlob = null;
		onClose();
	}

	// Start camera on mount
	$effect(() => {
		startCamera();

		// Cleanup on unmount
		return () => {
			stopCamera();
			if (capturedPreviewUrl) {
				URL.revokeObjectURL(capturedPreviewUrl);
			}
		};
	});
</script>

<div class="fixed inset-0 z-50 bg-black flex flex-col">
	<!-- Header with close and switch camera buttons -->
	<div class="flex justify-between items-center p-4">
		<button
			type="button"
			onclick={handleClose}
			class="w-10 h-10 rounded-full bg-black/50 text-white flex items-center justify-center"
			aria-label="Close camera"
		>
			<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
				<path
					stroke-linecap="round"
					stroke-linejoin="round"
					stroke-width="2"
					d="M6 18L18 6M6 6l12 12"
				/>
			</svg>
		</button>

		{#if !error && !capturedPreviewUrl}
			<button
				type="button"
				onclick={switchCamera}
				class="w-10 h-10 rounded-full bg-black/50 text-white flex items-center justify-center"
				aria-label="Switch camera"
			>
				<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
					<path
						stroke-linecap="round"
						stroke-linejoin="round"
						stroke-width="2"
						d="M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
					/>
				</svg>
			</button>
		{/if}
	</div>

	<!-- Main content area -->
	<div class="flex-1 flex items-center justify-center overflow-hidden">
		{#if error}
			<div class="text-center p-6">
				<p class="text-white text-lg mb-4">{error}</p>
				<button
					type="button"
					onclick={handleClose}
					class="px-6 py-3 bg-white text-black rounded-lg font-medium"
				>
					Close
				</button>
			</div>
		{:else if capturedPreviewUrl}
			<!-- Preview captured photo -->
			<img
				src={capturedPreviewUrl}
				alt="Captured photo preview"
				class="max-h-full max-w-full object-contain"
			/>
		{:else}
			<!-- Live camera view -->
			<!-- svelte-ignore a11y_media_has_caption -->
			<video
				bind:this={videoElement}
				autoplay
				playsinline
				muted
				class="max-h-full max-w-full object-contain"
			></video>
		{/if}
	</div>

	<!-- Hidden canvas for capture -->
	<canvas bind:this={canvasElement} class="hidden"></canvas>

	<!-- Action buttons -->
	<div class="p-4 pb-8 flex justify-center gap-4">
		{#if capturedPreviewUrl}
			<!-- Retake / Confirm buttons -->
			<button
				type="button"
				onclick={retake}
				disabled={isUploading}
				class="px-6 py-3 bg-white/20 text-white rounded-lg font-medium disabled:opacity-50"
			>
				Retake
			</button>
			<button
				type="button"
				onclick={confirmAndUpload}
				disabled={isUploading}
				class="px-6 py-3 bg-white text-black rounded-lg font-medium disabled:opacity-50"
			>
				{isUploading ? 'Uploading...' : 'Use Photo'}
			</button>
		{:else if !error}
			<!-- Capture button - large circle -->
			<button
				type="button"
				onclick={capturePhoto}
				class="w-16 h-16 rounded-full bg-white border-4 border-white/50 flex items-center justify-center active:scale-95 transition-transform"
				aria-label="Capture photo"
			>
				<span class="w-12 h-12 rounded-full bg-white"></span>
			</button>
		{/if}
	</div>
</div>
