<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import { Camera, X, Check, AlertCircle } from 'lucide-svelte';
	import { validarWebcamDisponivel, capturarWebcamComPreview } from '$lib/utils/webcam';

	interface Props {
		onCapture: (blob: Blob | null) => void;
		onCancel: () => void;
		onError?: () => void;
		autoCapture?: boolean;
		fotoObrigatoria?: boolean; // Se true, não permite continuar sem foto
	}

	let { onCapture, onCancel, onError, autoCapture = false, fotoObrigatoria = false }: Props = $props();

	let videoElement: HTMLVideoElement | null = $state(null);
	let canvasElement: HTMLCanvasElement | null = $state(null);
	let stream: MediaStream | null = $state(null);
	let webcamDisponivel = $state(false);
	let capturando = $state(false);
	let erro = $state<string | null>(null);
	let previewUrl = $state<string | null>(null);
	let videoReady = $state(false);

	// Flag para evitar múltiplas chamadas de play() simultâneas
	let playEmAndamento = $state(false);

	// Efeito para garantir que o vídeo seja exibido quando o stream for atribuído
	$effect(() => {
		if (stream && videoElement && !playEmAndamento) {
			// Sempre atualizar srcObject quando o stream mudar
			if (videoElement.srcObject !== stream) {
				videoElement.srcObject = stream;
			}
			
			// Tentar reproduzir se ainda não estiver pronto e não houver outra chamada em andamento
			if (!videoReady && videoElement.readyState < 2) {
				// Verificar se já não está reproduzindo
				if (!videoElement.paused && videoElement.readyState >= 2) {
					videoReady = true;
					return;
				}

				playEmAndamento = true;
				videoElement.play()
					.then(() => {
						playEmAndamento = false;
						// Aguardar um pouco para garantir que o vídeo esteja realmente reproduzindo
						setTimeout(() => {
							if (videoElement && videoElement.readyState >= 2 && videoElement.videoWidth > 0) {
								videoReady = true;
							}
						}, 300);
					})
					.catch((err) => {
						playEmAndamento = false;
						// Ignorar AbortError - é esperado quando há uma nova requisição de load
						if (err.name !== 'AbortError') {
							console.warn('Erro ao reproduzir vídeo no effect:', err);
						}
					});
			} else if (videoElement.readyState >= 2 && videoElement.videoWidth > 0) {
				videoReady = true;
			}
		}
	});

	onMount(async () => {
		// Aguardar mais tempo para garantir que os elementos estejam no DOM
		await new Promise(resolve => setTimeout(resolve, 300));

		// Verificar suporte
		if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) {
			// Tentar método alternativo (navegadores antigos)
			const getUserMedia = 
				navigator.getUserMedia || 
				(navigator as any).webkitGetUserMedia || 
				(navigator as any).mozGetUserMedia || 
				(navigator as any).msGetUserMedia;
			
			if (!getUserMedia) {
				erro = 'Webcam não suportada';
				if (autoCapture && onError) {
					onError();
				}
				return;
			}
		}

		// Primeiro, tentar acessar a webcam antes de verificar o elemento
		// Isso garante que temos permissão antes de tentar renderizar o vídeo
		try {
			// Tentar diferentes configurações de webcam
			const constraints = [
				{
					video: {
						width: { ideal: 1280 },
						height: { ideal: 720 },
						facingMode: 'user'
					}
				},
				{
					video: {
						width: { ideal: 640 },
						height: { ideal: 480 },
						facingMode: 'user'
					}
				},
				{
					video: {
						facingMode: 'user'
					}
				},
				{
					video: true
				}
			];

			let ultimoErro: Error | null = null;
			let streamObtido = false;
			
			for (const constraint of constraints) {
				try {
					console.log('Tentando acessar webcam com constraint:', constraint);
					const tempStream = await navigator.mediaDevices.getUserMedia(constraint);
					
					// Verificar se o stream tem tracks de vídeo
					if (tempStream.getVideoTracks().length === 0) {
						tempStream.getTracks().forEach(track => track.stop());
						continue;
					}

					console.log('Webcam acessada com sucesso');
					stream = tempStream;
					webcamDisponivel = true;
					streamObtido = true;
					break;
				} catch (err) {
					console.warn('Falha ao acessar webcam com constraint:', constraint, err);
					ultimoErro = err instanceof Error ? err : new Error(String(err));
					continue;
				}
			}

			if (!streamObtido) {
				throw ultimoErro || new Error('Não foi possível acessar a webcam');
			}

			// Agora que temos o stream, aguardar o elemento de vídeo estar disponível
			let tentativas = 0;
			while (!videoElement && tentativas < 30) {
				await new Promise(resolve => setTimeout(resolve, 100));
				tentativas++;
			}

			if (!videoElement) {
				erro = 'Elemento de vídeo não encontrado';
				if (stream) {
					stream.getTracks().forEach(track => track.stop());
					stream = null;
				}
				webcamDisponivel = false;
				if (fotoObrigatoria) {
					return;
				}
				if (autoCapture && onError) {
					onError();
				}
				return;
			}

			// Atribuir stream ao elemento de vídeo
			if (videoElement && stream) {
				videoElement.srcObject = stream;
				
				// Aguardar o vídeo estar pronto com timeout maior
				await new Promise<void>((resolve, reject) => {
					const timeout = setTimeout(() => {
						// Se o vídeo tem dimensões, considerar pronto mesmo sem eventos
						if (videoElement && videoElement.videoWidth > 0 && videoElement.videoHeight > 0) {
							videoReady = true;
							resolve();
						} else {
							reject(new Error('Timeout ao carregar vídeo'));
						}
					}, 15000); // Aumentar timeout para 15 segundos

					const onLoadedMetadata = () => {
						clearTimeout(timeout);
						videoElement?.removeEventListener('loadedmetadata', onLoadedMetadata);
						videoElement?.removeEventListener('playing', onPlaying);
						videoElement?.removeEventListener('loadeddata', onLoadedData);
						videoElement?.removeEventListener('error', onError);
						// Aguardar um pouco mais para garantir que o vídeo esteja realmente visível
						setTimeout(() => {
							videoReady = true;
							resolve();
						}, 200);
					};

					const onLoadedData = () => {
						if (videoElement && videoElement.videoWidth > 0 && videoElement.videoHeight > 0) {
							clearTimeout(timeout);
							videoElement?.removeEventListener('loadedmetadata', onLoadedMetadata);
							videoElement?.removeEventListener('playing', onPlaying);
							videoElement?.removeEventListener('loadeddata', onLoadedData);
							videoElement?.removeEventListener('error', onError);
							videoReady = true;
							resolve();
						}
					};

					const onPlaying = () => {
						clearTimeout(timeout);
						videoElement?.removeEventListener('loadedmetadata', onLoadedMetadata);
						videoElement?.removeEventListener('playing', onPlaying);
						videoElement?.removeEventListener('loadeddata', onLoadedData);
						videoElement?.removeEventListener('error', onError);
						videoReady = true;
						resolve();
					};

					const onError = () => {
						clearTimeout(timeout);
						videoElement?.removeEventListener('loadedmetadata', onLoadedMetadata);
						videoElement?.removeEventListener('playing', onPlaying);
						videoElement?.removeEventListener('loadeddata', onLoadedData);
						videoElement?.removeEventListener('error', onError);
						reject(new Error('Erro ao carregar vídeo'));
					};

					videoElement.addEventListener('loadedmetadata', onLoadedMetadata);
					videoElement.addEventListener('loadeddata', onLoadedData);
					videoElement.addEventListener('playing', onPlaying);
					videoElement.addEventListener('error', onError);

					// Tentar reproduzir apenas se não estiver já reproduzindo
					if (videoElement.paused) {
						playEmAndamento = true;
						videoElement.play()
							.then(() => {
								playEmAndamento = false;
								console.log('Vídeo iniciado, readyState:', videoElement?.readyState);
								// Se já tiver metadata e dimensões, resolver imediatamente
								if (videoElement && videoElement.readyState >= 2 && videoElement.videoWidth > 0) {
									setTimeout(() => {
										onLoadedMetadata();
									}, 300);
								}
							})
							.catch((err) => {
								playEmAndamento = false;
								// Ignorar AbortError - é esperado quando há uma nova requisição de load
								if (err.name !== 'AbortError') {
									console.warn('Erro ao reproduzir vídeo:', err);
								}
								// Continuar mesmo assim se já tiver metadata e dimensões
								if (videoElement && videoElement.readyState >= 2 && videoElement.videoWidth > 0) {
									setTimeout(() => {
										onLoadedMetadata();
									}, 300);
								} else {
									// Aguardar um pouco mais antes de dar erro
									setTimeout(() => {
										if (videoElement && videoElement.videoWidth > 0) {
											onLoadedMetadata();
										} else {
											onError();
										}
									}, 1000);
								}
							});
					} else {
						// Já está reproduzindo, apenas verificar se está pronto
						if (videoElement.readyState >= 2 && videoElement.videoWidth > 0) {
							setTimeout(() => {
								onLoadedMetadata();
							}, 300);
						}
					}
				});

				console.log('Vídeo pronto, dimensões:', videoElement.videoWidth, 'x', videoElement.videoHeight);
			}

			// Se for captura automática, aguardar um pouco e capturar
			if (autoCapture) {
				// Aguardar 1.5 segundos para o vídeo estabilizar
				setTimeout(() => {
					if (videoElement && canvasElement && !capturando && !previewUrl && webcamDisponivel) {
						capturar();
					}
				}, 1500);
			}

			// Sucesso, sair do try
			return;
		} catch (error) {
			console.error('Erro ao acessar webcam:', error);
			const errorMessage = error instanceof Error ? error.message : String(error);
			
			if (errorMessage.includes('Permission denied') || errorMessage.includes('NotAllowedError')) {
				erro = fotoObrigatoria 
					? 'Permissão de webcam negada. É necessário autorizar o acesso à webcam para registrar o ponto.'
					: 'Permissão de webcam negada. Continuando sem foto.';
			} else if (errorMessage.includes('NotFoundError') || errorMessage.includes('DevicesNotFoundError')) {
				erro = fotoObrigatoria
					? 'Nenhuma webcam encontrada. É necessário uma webcam para registrar o ponto.'
					: 'Nenhuma webcam encontrada. Continuando sem foto.';
			} else {
				erro = fotoObrigatoria
					? 'Erro ao acessar webcam. Verifique as permissões e tente novamente.'
					: 'Erro ao acessar webcam. Continuando sem foto.';
			}
			
			webcamDisponivel = false;
			// Se foto é obrigatória, não chamar onError para permitir continuar sem foto
			if (fotoObrigatoria) {
				// Apenas mostrar o erro e aguardar o usuário fechar ou tentar novamente
				return;
			}
			// Se for captura automática e houver erro, chamar onError para continuar sem foto
			if (autoCapture && onError) {
				setTimeout(() => {
					onError();
				}, 500);
			}
		}
	});

	onDestroy(() => {
		if (stream) {
			stream.getTracks().forEach((track) => track.stop());
		}
		if (previewUrl) {
			URL.revokeObjectURL(previewUrl);
		}
	});

	async function capturar() {
		if (!videoElement || !canvasElement) {
			console.error('Elementos de vídeo ou canvas não disponíveis');
			if (autoCapture && onError) {
				onError();
			}
			return;
		}

		// Verificar se o vídeo está pronto e tem dimensões válidas
		if (videoElement.readyState < 2 || videoElement.videoWidth === 0 || videoElement.videoHeight === 0) {
			console.warn('Vídeo ainda não está pronto, aguardando...');
			await new Promise<void>((resolve, reject) => {
				let tentativas = 0;
				const maxTentativas = 50; // 5 segundos
				const checkReady = () => {
					tentativas++;
					if (videoElement && videoElement.readyState >= 2 && videoElement.videoWidth > 0 && videoElement.videoHeight > 0) {
						resolve();
					} else if (tentativas >= maxTentativas) {
						reject(new Error('Timeout aguardando vídeo ficar pronto'));
					} else {
						setTimeout(checkReady, 100);
					}
				};
				checkReady();
			}).catch((error) => {
				console.error('Erro ao aguardar vídeo:', error);
				erro = 'Vídeo não está pronto. Aguarde um momento e tente novamente.';
				capturando = false;
				return; // Retornar aqui para não continuar
			});
			
			// Se chegou aqui, o vídeo está pronto, continuar com a captura
		}

		capturando = true;
		erro = null;

		try {
			// Verificar dimensões do vídeo novamente antes de capturar
			if (!videoElement.videoWidth || !videoElement.videoHeight) {
				throw new Error('Dimensões do vídeo não disponíveis. Aguarde a câmera carregar completamente.');
			}

			// Configurar canvas com as dimensões do vídeo
			canvasElement.width = videoElement.videoWidth;
			canvasElement.height = videoElement.videoHeight;

			// Obter contexto do canvas
			const ctx = canvasElement.getContext('2d');
			if (!ctx) {
				throw new Error('Não foi possível obter contexto do canvas');
			}

			// Limpar canvas antes de desenhar
			ctx.clearRect(0, 0, canvasElement.width, canvasElement.height);
			
			// Desenhar frame atual do vídeo no canvas
			// O vídeo está espelhado no CSS para visualização, mas capturamos normalmente
			ctx.drawImage(videoElement, 0, 0, canvasElement.width, canvasElement.height);

			// Converter para blob
			const blob = await new Promise<Blob | null>((resolve, reject) => {
				canvasElement.toBlob(
					(blob) => {
						if (blob) {
							resolve(blob);
						} else {
							reject(new Error('Falha ao converter canvas para blob'));
						}
					},
					'image/jpeg',
					0.92 // Qualidade ligeiramente reduzida para melhor compatibilidade
				);
			});

			if (blob && blob.size > 0) {
				previewUrl = URL.createObjectURL(blob);
				console.log('Imagem capturada com sucesso, tamanho:', blob.size, 'bytes');
				
				// Parar stream para mostrar preview
				if (stream) {
					stream.getTracks().forEach((track) => track.stop());
					stream = null;
				}

				// Se for captura automática, confirmar automaticamente após um pequeno delay
				if (autoCapture) {
					setTimeout(() => {
						confirmar();
					}, 500);
				}
			} else {
				throw new Error('Blob vazio ou inválido');
			}
		} catch (error) {
			console.error('Erro ao capturar:', error);
			erro = fotoObrigatoria
				? 'Erro ao capturar imagem. Tente novamente.'
				: 'Erro ao capturar imagem. Continuando sem foto.';
			// Se foto é obrigatória, não chamar onError para permitir continuar sem foto
			if (fotoObrigatoria) {
				// Apenas mostrar o erro e permitir que o usuário tente novamente
				capturando = false;
				return;
			}
			// Se for captura automática e houver erro, continuar sem foto
			if (autoCapture && onError) {
				setTimeout(() => {
					onError();
				}, 500);
			}
		} finally {
			capturando = false;
		}
	}

	function confirmar() {
		if (previewUrl) {
			// Converter preview URL de volta para blob
			fetch(previewUrl)
				.then((res) => res.blob())
				.then((blob) => {
					onCapture(blob);
				})
				.catch((error) => {
					console.error('Erro ao converter preview:', error);
					erro = 'Erro ao processar imagem';
				});
		}
	}

	function cancelar() {
		if (previewUrl) {
			URL.revokeObjectURL(previewUrl);
			previewUrl = null;
		}
		onCancel();
	}

	async function recapturar() {
		if (previewUrl) {
			URL.revokeObjectURL(previewUrl);
			previewUrl = null;
		}
		// Reiniciar webcam
		try {
			stream = await navigator.mediaDevices.getUserMedia({
				video: {
					width: { ideal: 1280 },
					height: { ideal: 720 },
					facingMode: 'user'
				}
			});

			if (videoElement) {
				videoElement.srcObject = stream;
				await videoElement.play();
			}
		} catch (error) {
			console.error('Erro ao reiniciar webcam:', error);
			erro = 'Erro ao reiniciar webcam';
		}
	}
</script>

<div class="flex flex-col items-center gap-4 p-4 w-full">
	{#if !webcamDisponivel && !erro}
		<div class="text-warning flex items-center gap-2">
			<Camera class="h-5 w-5" />
			<span>Verificando webcam...</span>
		</div>
		{#if !autoCapture && !fotoObrigatoria}
			<div class="flex gap-2">
				<button class="btn btn-primary" onclick={cancelar}>Continuar sem foto</button>
			</div>
		{:else if fotoObrigatoria}
			<div class="alert alert-info max-w-md">
				<AlertCircle class="h-5 w-5" />
				<span>A captura de foto é obrigatória para registrar o ponto.</span>
			</div>
		{/if}
		{:else if erro && !webcamDisponivel}
		<div class="alert alert-error max-w-md">
			<AlertCircle class="h-5 w-5" />
			<span>{erro}</span>
		</div>
		{#if fotoObrigatoria}
			<div class="alert alert-warning max-w-md">
				<span>Não é possível registrar o ponto sem capturar uma foto. Verifique as permissões da webcam e tente novamente.</span>
			</div>
			<div class="flex gap-2">
				<button class="btn btn-primary" onclick={async () => {
					erro = null;
					webcamDisponivel = false;
					videoReady = false;
					// Limpar stream anterior se existir
					if (stream) {
						stream.getTracks().forEach(track => track.stop());
						stream = null;
					}
					// Tentar reiniciar a webcam
					try {
						if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia && videoElement) {
							stream = await navigator.mediaDevices.getUserMedia({ video: true });
							if (stream.getVideoTracks().length > 0) {
								webcamDisponivel = true;
								if (videoElement) {
									videoElement.srcObject = stream;
									await videoElement.play();
								}
							} else {
								stream.getTracks().forEach(track => track.stop());
								stream = null;
								erro = 'Nenhuma webcam encontrada. É necessário uma webcam para registrar o ponto.';
							}
						} else {
							erro = 'Webcam não disponível. Verifique as permissões e tente novamente.';
						}
					} catch (e) {
						console.error('Erro ao tentar novamente:', e);
						const errorMessage = e instanceof Error ? e.message : String(e);
						if (errorMessage.includes('Permission denied') || errorMessage.includes('NotAllowedError')) {
							erro = 'Permissão de webcam negada. É necessário autorizar o acesso à webcam para registrar o ponto.';
						} else if (errorMessage.includes('NotFoundError') || errorMessage.includes('DevicesNotFoundError')) {
							erro = 'Nenhuma webcam encontrada. É necessário uma webcam para registrar o ponto.';
						} else {
							erro = 'Erro ao acessar webcam. Verifique as permissões e tente novamente.';
						}
					}
				}}>Tentar Novamente</button>
				<button class="btn btn-error" onclick={cancelar}>Fechar</button>
			</div>
		{:else if autoCapture}
			<div class="text-sm text-base-content/70 text-center">
				O registro será feito sem foto.
			</div>
		{:else}
			<div class="flex gap-2">
				<button class="btn btn-primary" onclick={cancelar}>Continuar sem foto</button>
			</div>
		{/if}
	{:else if previewUrl}
		<!-- Preview da imagem capturada -->
		<div class="flex flex-col items-center gap-4 w-full">
			{#if autoCapture}
				<!-- Modo automático: mostrar apenas preview sem botões -->
				<div class="text-sm text-base-content/70 mb-2 text-center">
					Foto capturada automaticamente...
				</div>
			{/if}
			<img
				src={previewUrl}
				alt="Preview"
				class="border-primary max-h-[60vh] max-w-full rounded-lg border-2 object-contain"
			/>
			{#if !autoCapture}
				<!-- Botões apenas se não for automático -->
				<div class="flex gap-2 flex-wrap justify-center">
					<button class="btn btn-success" onclick={confirmar}>
						<Check class="h-5 w-5" />
						Confirmar
					</button>
					<button class="btn btn-outline" onclick={recapturar}>
						<Camera class="h-5 w-5" />
						Recapturar
					</button>
					<button class="btn btn-error" onclick={cancelar}>
						<X class="h-5 w-5" />
						Cancelar
					</button>
				</div>
			{/if}
		</div>
	{:else}
		<!-- Webcam ativa -->
		<div class="flex flex-col items-center gap-4 w-full">
			{#if autoCapture}
				<div class="text-sm text-base-content/70 mb-2 text-center">
					Capturando foto automaticamente...
				</div>
			{:else}
				<div class="text-sm text-base-content/70 mb-2 text-center">
					Posicione-se na frente da câmera e clique em "Capturar Foto"
				</div>
			{/if}
			<div class="relative w-full flex justify-center">
				<video
					bind:this={videoElement}
					autoplay
					playsinline
					muted
					class="border-primary max-h-[60vh] max-w-full rounded-lg border-2 object-contain bg-black {!videoReady ? 'opacity-50' : ''}"
					style="min-width: 320px; min-height: 240px; transform: scaleX(-1);"
				></video>
				<canvas bind:this={canvasElement} class="hidden"></canvas>
				{#if !videoReady && webcamDisponivel}
					<div class="absolute inset-0 flex flex-col items-center justify-center bg-black/70 rounded-lg gap-2">
						<span class="loading loading-spinner loading-lg text-white"></span>
						<span class="text-white text-sm">Carregando câmera...</span>
					</div>
				{:else if videoReady && webcamDisponivel}
					<div class="absolute bottom-2 left-1/2 transform -translate-x-1/2">
						<div class="badge badge-success gap-2">
							<Camera class="h-4 w-4" />
							Câmera ativa
						</div>
					</div>
				{/if}
			</div>
			{#if erro}
				<div class="alert alert-error max-w-md">
					<span>{erro}</span>
				</div>
			{/if}
			{#if !autoCapture}
				<!-- Botões sempre visíveis quando não for automático -->
				<div class="flex gap-2 flex-wrap justify-center">
					<button 
						class="btn btn-primary btn-lg" 
						onclick={capturar} 
						disabled={capturando || !videoReady || !webcamDisponivel}
					>
						{#if capturando}
							<span class="loading loading-spinner loading-sm"></span>
							Capturando...
						{:else}
							<Camera class="h-5 w-5" />
							Capturar Foto
						{/if}
					</button>
					<button class="btn btn-outline" onclick={cancelar}>
						<X class="h-5 w-5" />
						Cancelar
					</button>
				</div>
			{/if}
		</div>
	{/if}
</div>
