<script lang="ts">
	import { onMount, onDestroy } from 'svelte';
	import { useQuery, useConvexClient } from 'convex-svelte';
	import { api } from '@sgse-app/backend/convex/_generated/api';
	import {
		Clock,
		Filter,
		Download,
		Printer,
		BarChart3,
		Users,
		CheckCircle2,
		XCircle,
		TrendingUp,
		TrendingDown,
		FileText,
		X,
		Calendar
	} from 'lucide-svelte';
	import type { Id } from '@sgse-app/backend/convex/_generated/dataModel';
	import { formatarHoraPonto, getTipoRegistroLabel, formatarDataDDMMAAAA } from '$lib/utils/ponto';
	import {
		adicionarLogo,
		adicionarCabecalho,
		adicionarDadosFuncionario,
		adicionarResumoPeriodo,
		adicionarSaldosPeriodo,
		adicionarLegenda,
		adicionarRodape,
		verificarNovaPagina,
		type SectionsPDF,
		type FuncionarioPDF,
		type ConfigPontoPDF
	} from '$lib/utils/fichaPontoPDF';
	// Importar módulos extraídos
	import type { TipoDia, SaldoDiario, RegistroPonto, DiaFichaPonto, ResumoPeriodo } from '$lib/utils/ponto/tipos';
	import { formatarDataParaExibicao, formatarDataParaBackend, formatarSaldoHoras, formatarSaldoDiario, formatarMinutos, formatarHoras } from '$lib/utils/ponto/formatacao';
	import { validarPeriodo } from '$lib/utils/ponto/validacao';
	import { calcularSaldosParciais, calcularSaldoDiario, calcularSaldosPorPar, calcularSaldoComparativoPorPar } from '$lib/utils/ponto/calculos';
	import { agruparRegistrosPorFuncionario, gerarDiasPeriodo, gerarRegistrosEsperados, processarDadosFichaPonto } from '$lib/utils/ponto/processamento';
	import { registroFoiMarcado } from '$lib/utils/ponto/validacao';
	import { gerarPDFComSelecao } from '$lib/utils/ponto/pdf/geradorPDF';
	import { imprimirDetalhesRegistro } from '$lib/utils/ponto/pdf/geradorDetalhesPDF';
	import HeaderRegistroPontos from '$lib/components/ponto/registro-pontos/HeaderRegistroPontos.svelte';
	import EstatisticasCards from '$lib/components/ponto/registro-pontos/EstatisticasCards.svelte';
	import GraficoEstatisticas from '$lib/components/ponto/registro-pontos/GraficoEstatisticas.svelte';
	import { maskDate, validateDate, onlyDigits } from '$lib/utils/masks';
	import LocalizacaoIcon from '$lib/components/ponto/LocalizacaoIcon.svelte';
	import SaldoDiarioBadge from '$lib/components/ponto/SaldoDiarioBadge.svelte';
	import SaldoDiarioComparativoBadge from '$lib/components/ponto/SaldoDiarioComparativoBadge.svelte';
	import jsPDF from 'jspdf';
	import autoTable from 'jspdf-autotable';
	import logoGovPE from '$lib/assets/logo_governo_PE.png';
	import PrintPontoModal from '$lib/components/ponto/PrintPontoModal.svelte';
	import { toast } from 'svelte-sonner';
	import { Chart, registerables } from 'chart.js';
	import Papa from 'papaparse';







	const client = useConvexClient();

	// ============================================
	// INTERFACES TYPESCRIPT
	// ============================================
	// Tipos importados de $lib/utils/ponto/tipos

	// Estados
	// Expandir período padrão para últimos 30 dias para facilitar visualização
	const hoje = new Date();
	const trintaDiasAtras = new Date(hoje);
	trintaDiasAtras.setDate(hoje.getDate() - 30);

	// Funções de formatação importadas de $lib/utils/ponto/formatacao
	// Wrapper para formatarDataParaBackend que precisa de onlyDigits e validateDate
	function formatarDataParaBackendWrapper(data: string): string {
		return formatarDataParaBackend(data, onlyDigits, validateDate);
	}

	let dataInicioInterno = $state(trintaDiasAtras.toISOString().split('T')[0]!);
	let dataFimInterno = $state(hoje.toISOString().split('T')[0]!);

	// Valores para exibição (dd/mm/yyyy)
	let dataInicioExibicao = $state(formatarDataParaExibicao(dataInicioInterno));
	let dataFimExibicao = $state(formatarDataParaExibicao(dataFimInterno));

	// Valores para backend (yyyy-mm-dd) - derivados dos valores internos
	const dataInicio = $derived(dataInicioInterno);
	const dataFim = $derived(dataFimInterno);
	let funcionarioIdFiltro = $state<Id<'funcionarios'> | ''>('');
	let statusFiltro = $state<'todos' | 'dentro' | 'fora'>('todos');
	let localizacaoFiltro = $state<'todos' | 'dentro' | 'fora'>('todos');
	let carregando = $state(false);
	let mostrarModalImpressao = $state(false);
	let funcionarioParaImprimir = $state<Id<'funcionarios'> | ''>('');
	let mostrarModalDetalhes = $state(false);
	let registroDetalhesId = $state<Id<'registrosPonto'> | ''>('');
	let chartCanvas: HTMLCanvasElement;
	let chartInstance: Chart | null = null;

	// Parâmetros reativos para queries
	// Nota: Apenas filtros de data são aplicados no backend para performance
	// Filtros de funcionário, status e localização são aplicados no frontend
	const registrosParams = $derived({
		dataInicio,
		dataFim
		// Removido funcionarioId para carregar todos os registros e filtrar no frontend
	});
	const estatisticasParams = $derived({
		dataInicio,
		dataFim,
		funcionarioId:
			funcionarioIdFiltro && funcionarioIdFiltro !== '' ? funcionarioIdFiltro : undefined
	});

	// Queries
	const funcionariosQuery = useQuery(api.funcionarios.getAll, {});
	const registrosQuery = useQuery(api.pontos.listarRegistrosPeriodo, registrosParams);
	const estatisticasQuery = useQuery(api.pontos.obterEstatisticas, estatisticasParams);
	const configQuery = useQuery(api.configuracaoPonto.obterConfiguracao, {});

	const funcionarios = $derived(funcionariosQuery?.data || []);
	const registros = $derived(registrosQuery?.data || []);
	const estatisticas = $derived(estatisticasQuery?.data);
	const config = $derived(configQuery?.data);

	// Debug: Log dos dados recebidos
	$effect(() => {
		if (registrosQuery !== undefined) {
			console.log('[Frontend] registrosQuery:', {
				isLoading: registrosQuery?.isLoading,
				error: registrosQuery?.error,
				dataLength: registrosQuery?.data?.length ?? 0,
				params: registrosParams
			});
		}
		if (registros && registros.length > 0) {
			console.log('[Frontend] Primeiros registros:', registros.slice(0, 3));
		}
	});

	// Dados do gráfico baseados nas estatísticas
	const chartData = $derived.by(() => {
		if (!estatisticas) return null;

		return {
			labels: ['Estatísticas de Registros'],
			datasets: [
				{
					label: 'Dentro do Prazo',
					data: [estatisticas.dentroDoPrazo || 0],
					backgroundColor: 'rgba(34, 197, 94, 0.8)',
					borderColor: 'rgba(34, 197, 94, 1)',
					borderWidth: 2
				},
				{
					label: 'Fora do Prazo',
					data: [estatisticas.foraDoPrazo || 0],
					backgroundColor: 'rgba(239, 68, 68, 0.8)',
					borderColor: 'rgba(239, 68, 68, 1)',
					borderWidth: 2
				}
			]
		};
	});

	// Função para criar/atualizar o gráfico
	function criarGrafico() {
		if (!chartCanvas || !estatisticas || !chartData) {
			return;
		}

		const ctx = chartCanvas.getContext('2d');
		if (!ctx) {
			return;
		}

		// Destruir gráfico anterior se existir
		if (chartInstance) {
			chartInstance.destroy();
			chartInstance = null;
		}

		try {
			chartInstance = new Chart(ctx, {
				type: 'bar',
				data: chartData,
				options: {
					responsive: true,
					maintainAspectRatio: false,
					plugins: {
						legend: {
							display: true,
							position: 'top',
							labels: {
								color: 'hsl(var(--bc))',
								font: {
									size: 12,
									family: "'Inter', sans-serif"
								},
								usePointStyle: true,
								padding: 15
							}
						},
						tooltip: {
							backgroundColor: 'rgba(0, 0, 0, 0.85)',
							titleColor: '#fff',
							bodyColor: '#fff',
							borderColor: 'hsl(var(--p))',
							borderWidth: 1,
							padding: 12,
							callbacks: {
								label: function (context) {
									const label = context.dataset.label || '';
									const value = context.parsed.y;
									const total = estatisticas.totalRegistros;
									const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : '0.0';
									return `${label}: ${value} (${percentage}%)`;
								}
							}
						}
					},
					scales: {
						x: {
							stacked: true,
							grid: {
								display: false
							},
							ticks: {
								color: 'hsl(var(--bc))',
								font: {
									size: 12
								}
							}
						},
						y: {
							stacked: true,
							beginAtZero: true,
							grid: {
								color: 'rgba(0, 0, 0, 0.05)'
							},
							ticks: {
								color: 'hsl(var(--bc))',
								font: {
									size: 11
								},
								stepSize: 1
							}
						}
					},
					animation: {
						duration: 1000,
						easing: 'easeInOutQuart'
					},
					interaction: {
						mode: 'index',
						intersect: false
					}
				}
			});
		} catch (error) {
			console.error('Erro ao criar gráfico:', error);
		}
	}

	// Inicializar gráfico quando canvas e dados estiverem disponíveis
	$effect(() => {
		if (chartCanvas && estatisticas && chartData) {
			// Destruir gráfico anterior se existir
			if (chartInstance) {
				chartInstance.destroy();
				chartInstance = null;
			}

			// Aguardar um pouco para garantir que o canvas está renderizado
			const timeoutId = setTimeout(() => {
				try {
					criarGrafico();
				} catch (error) {
					console.error('Erro ao criar gráfico no effect:', error);
				}
			}, 200);

			return () => {
				clearTimeout(timeoutId);
			};
		}
	});

	// Também tentar criar quando o canvas for montado
	onMount(() => {
		Chart.register(...registerables);
		// Tentar criar o gráfico após um pequeno delay para garantir que tudo está renderizado
		const timeoutId = setTimeout(() => {
			if (chartCanvas && estatisticas && chartData && !chartInstance) {
				try {
					criarGrafico();
				} catch (error) {
					console.error('Erro ao criar gráfico no onMount:', error);
				}
			}
		}, 500);

		return () => {
			clearTimeout(timeoutId);
		};
	});

	onDestroy(() => {
		if (chartInstance) {
			chartInstance.destroy();
		}
	});

	// Filtrar registros com base nos filtros avançados
	// Nota: Os filtros de data são aplicados no backend através de registrosParams
	// Os filtros de funcionário, status e localização são aplicados aqui no frontend para garantir funcionamento correto
	const registrosFiltrados = $derived.by(() => {
		if (!registros || registros.length === 0) return [];

		let resultado = [...registros];

		// Filtro de funcionário (aplicado no frontend para garantir que funcione corretamente)
		if (funcionarioIdFiltro && funcionarioIdFiltro !== '') {
			resultado = resultado.filter((r) => {
				return r.funcionarioId === funcionarioIdFiltro;
			});
		}

		// Filtro de status (Dentro/Fora do Prazo)
		if (statusFiltro !== 'todos') {
			resultado = resultado.filter((r) => {
				if (statusFiltro === 'dentro') {
					return r.dentroDoPrazo === true;
				}
				if (statusFiltro === 'fora') {
					return r.dentroDoPrazo === false;
				}
				return true;
			});
		}

		// Filtro de localização (Dentro/Fora do Raio)
		if (localizacaoFiltro !== 'todos') {
			resultado = resultado.filter((r) => {
				// Se não houver informação de localização, excluir do resultado quando filtro está ativo
				if (r.dentroRaioPermitido === undefined || r.dentroRaioPermitido === null) {
					return false;
				}
				if (localizacaoFiltro === 'dentro') {
					return r.dentroRaioPermitido === true;
				}
				if (localizacaoFiltro === 'fora') {
					return r.dentroRaioPermitido === false;
				}
				return true;
			});
		}

		return resultado;
	});

		// Agrupar registros por funcionário e data usando função extraída
	const registrosAgrupados = $derived.by(() => {
		return agruparRegistrosPorFuncionario(registrosFiltrados, config || undefined);
	});

	// Código antigo de agrupamento (comentado para referência)
	/*
	const registrosAgrupadosOld = $derived.by(() => {
		const configData = config;
		const agrupados: Record<
			string,
			{
				funcionario: { nome: string; matricula?: string; descricaoCargo?: string } | null;
				funcionarioId: Id<'funcionarios'>;
				registrosPorData: Record<
					string,
					{
						data: string;
						registros: Array<(typeof registros)[number]>;
						saldoDiario?: {
							saldoMinutos: number;
							horas: number;
							minutos: number;
							positivo: boolean;
						};
						saldoDiarioComparativo?: {
							trabalhadoMinutos: number;
							esperadoMinutos: number;
							diferencaMinutos: number;
						};
					}
				>;
			}
		> = {};

		// Usar Set para evitar registros duplicados
		const registrosProcessados = new Set<string>();

		// Usar registros filtrados ao invés de registros originais
		const registrosParaAgrupar = registrosFiltrados;

		// Verificar se registros é um array válido
		if (!Array.isArray(registrosParaAgrupar) || registrosParaAgrupar.length === 0) {
			return [];
		}

		for (const registro of registrosParaAgrupar) {
			// Verificar se o registro tem os campos necessários
			if (!registro || !registro._id || !registro.funcionarioId || !registro.data) {
				console.warn('⚠️ [DEBUG] Registro inválido ignorado:', registro);
				continue;
			}

			// Criar chave única para evitar duplicatas
			const chaveUnica = `${registro._id}`;
			if (registrosProcessados.has(chaveUnica)) {
				continue; // Pular se já foi processado
			}
			registrosProcessados.add(chaveUnica);

			const key = registro.funcionarioId;
			if (!agrupados[key]) {
				agrupados[key] = {
					funcionario: registro.funcionario,
					funcionarioId: registro.funcionarioId,
					registrosPorData: {}
				};
			}

			const dataKey = registro.data;
			if (!agrupados[key]!.registrosPorData[dataKey]) {
				agrupados[key]!.registrosPorData[dataKey] = {
					data: dataKey,
					registros: [],
					saldoDiario: undefined
				};
			}

			// Verificar se o registro já não está no array antes de adicionar
			const jaExiste = agrupados[key]!.registrosPorData[dataKey]!.registros.some(
				(r) => r._id === registro._id
			);
			if (!jaExiste) {
				agrupados[key]!.registrosPorData[dataKey]!.registros.push(registro);
			}
		}

		// Ordenar registros por data e hora dentro de cada grupo e usar saldo diário da query
		const resultado = Object.values(agrupados);

		// Ordenar grupos por nome do funcionário
		resultado.sort((a, b) => {
			const nomeA = a.funcionario?.nome || '';
			const nomeB = b.funcionario?.nome || '';
			return nomeA.localeCompare(nomeB, 'pt-BR');
		});

		for (const grupo of resultado) {
			// Ordenar datas dentro de cada grupo (mais recente primeiro)
			const datasOrdenadas = Object.keys(grupo.registrosPorData).sort((a, b) => {
				return new Date(b).getTime() - new Date(a).getTime();
			});

			// Criar novo objeto com datas ordenadas
			const registrosPorDataOrdenado: Record<string, (typeof grupo.registrosPorData)[string]> = {};
			for (const dataKey of datasOrdenadas) {
				registrosPorDataOrdenado[dataKey] = grupo.registrosPorData[dataKey]!;
			}
			grupo.registrosPorData = registrosPorDataOrdenado;

			for (const dataKey in grupo.registrosPorData) {
				const grupoData = grupo.registrosPorData[dataKey];
				if (grupoData && grupoData.registros.length > 0) {
					// Ordenar por hora e minuto
					grupoData.registros.sort((a, b) => {
						if (a.hora !== b.hora) {
							return a.hora - b.hora;
						}
						return a.minuto - b.minuto;
					});

					// Calcular saldo diário comparativo usando a soma dos saldos parciais
					if (configData) {
						// Calcular saldos parciais (Par 1, Par 2, etc.)
						const saldosParciais = calcularSaldosParciais(grupoData.registros);

						// Somar todos os saldos parciais para obter o total trabalhado
						let totalTrabalhado = 0;
						for (const saldoParcial of saldosParciais.values()) {
							totalTrabalhado += saldoParcial.saldoMinutos;
						}

						// Calcular carga horária diária total esperada (soma dos dois pares)
						const [horaEntradaConfig, minutoEntradaConfig] = configData.horarioEntrada
							.split(':')
							.map(Number);
						const [horaSaidaAlmocoConfig, minutoSaidaAlmocoConfig] = configData.horarioSaidaAlmoco
							.split(':')
							.map(Number);
						const [horaRetornoAlmocoConfig, minutoRetornoAlmocoConfig] =
							configData.horarioRetornoAlmoco.split(':').map(Number);
						const [horaSaidaConfig, minutoSaidaConfig] = configData.horarioSaida
							.split(':')
							.map(Number);

						// Par 1: entrada -> saida_almoco
						const minutosPar1EsperadoConfig =
							horaSaidaAlmocoConfig * 60 +
							minutoSaidaAlmocoConfig -
							(horaEntradaConfig * 60 + minutoEntradaConfig);
						const minutosPar1EsperadoAjustadoConfig =
							minutosPar1EsperadoConfig < 0
								? minutosPar1EsperadoConfig + 24 * 60
								: minutosPar1EsperadoConfig;

						// Par 2: retorno_almoco -> saida
						const minutosPar2EsperadoConfig =
							horaSaidaConfig * 60 +
							minutoSaidaConfig -
							(horaRetornoAlmocoConfig * 60 + minutoRetornoAlmocoConfig);
						const minutosPar2EsperadoAjustadoConfig =
							minutosPar2EsperadoConfig < 0
								? minutosPar2EsperadoConfig + 24 * 60
								: minutosPar2EsperadoConfig;

						const cargaHorariaDiariaEsperadaMinutos =
							minutosPar1EsperadoAjustadoConfig + minutosPar2EsperadoAjustadoConfig;

						// Calcular diferença em relação à carga horária diária total configurada
						const diferencaMinutos = totalTrabalhado - cargaHorariaDiariaEsperadaMinutos;

						// Armazenar saldo comparativo
						grupoData.saldoDiarioComparativo = {
							trabalhadoMinutos: totalTrabalhado,
							esperadoMinutos: cargaHorariaDiariaEsperadaMinutos,
							diferencaMinutos: diferencaMinutos
						};
					} else {
						// Fallback: usar cálculo simples se não houver configuração
						const primeiroRegistro = grupoData.registros[0];
						if (
							primeiroRegistro &&
							'saldoDiario' in primeiroRegistro &&
							primeiroRegistro.saldoDiario
						) {
							grupoData.saldoDiario = primeiroRegistro.saldoDiario;
						} else {
							grupoData.saldoDiario = calcularSaldoDiario(grupoData.registros);
						}
					}
				}
			}
		}

		// Filtrar grupos que não têm registros após aplicar os filtros
		// Isso garante que apenas funcionários com registros que passam pelos filtros sejam exibidos
		const resultadoFiltrado = resultado.filter((grupo) => {
			// Verificar se o grupo tem pelo menos um registro em alguma data
			const temRegistros = Object.values(grupo.registrosPorData).some(
				(grupoData) => grupoData.registros && grupoData.registros.length > 0
			);
			return temRegistros;
		});

		return resultadoFiltrado;
	});

	// Query para banco de horas de cada funcionário
	const funcionariosComBancoHoras = $derived.by(() => {
		return registrosAgrupados.map((grupo) => grupo.funcionarioId);
	});

	// Funções de formatação importadas de $lib/utils/ponto/formatacao

	// Usar função centralizada formatarDataDDMMAAAA da lib/utils/ponto.ts

	// Obter nome do funcionário selecionado
	const funcionarioSelecionadoNome = $derived.by(() => {
		if (!funcionarioIdFiltro) return null;
		return funcionarios.find((f) => f._id === funcionarioIdFiltro)?.nome || null;
	});

	// Funções de cálculo importadas de $lib/utils/ponto/calculos
	// calcularSaldosParciais, calcularSaldoDiario, calcularSaldosPorPar, calcularSaldoComparativoPorPar

	// Funções importadas de $lib/utils/ponto/processamento e $lib/utils/ponto/validacao
	// gerarDiasPeriodo, gerarRegistrosEsperados, registroFoiMarcado

	const abrirModalImpressao = (funcionarioId: Id<'funcionarios'>) => {
		funcionarioParaImprimir = funcionarioId;
		mostrarModalImpressao = true;
	};

	// ============================================
	// FUNÇÕES AUXILIARES DE FORMATAÇÃO
	// ============================================

	// Funções formatarMinutos e formatarHoras importadas de $lib/utils/ponto/formatacao

	/**
	 * Obter nome do dia da semana em português
	 */
	function obterDiaSemana(data: string): string {
		const dias = ['Domingo', 'Segunda', 'Terça', 'Quarta', 'Quinta', 'Sexta', 'Sábado'];
		const dataObj = new Date(data + 'T00:00:00');
		return dias[dataObj.getDay()] || '';
	}

	/**
	 * Obter cor de fundo baseado no tipo de dia
	 */
	function obterCorFundo(tipoDia: TipoDia, temInconsistencia: boolean): string {
		if (temInconsistencia) return '#FFF3E0'; // Laranja claro
		if (tipoDia === 'atestado') return '#E3F2FD'; // Azul claro
		if (tipoDia === 'ausencia') return '#FFF9C4'; // Amarelo claro
		if (tipoDia === 'abonado') return '#E8F5E9'; // Verde claro
		if (tipoDia === 'nao_computado' || tipoDia === 'ferias') return '#F5F5F5'; // Cinza claro
		return '#FFFFFF'; // Branco
	}

	/**
	 * Obter ícone baseado no tipo de dia
	 */
	function obterIconeTipo(tipoDia: TipoDia): string {
		const icones: Record<TipoDia, string> = {
			normal: '',
			atestado: '🏥',
			ausencia: '🚫',
			licenca: '📋',
			abonado: '✅',
			nao_computado: '⏸',
			ferias: '🏖',
			inconsistente: '⚠'
		};
		return icones[tipoDia] || '';
	}

	// Função validarPeriodo importada de $lib/utils/ponto/validacao

	// ============================================
	// FUNÇÃO DE PROCESSAMENTO DE DADOS PARA FICHA
	// ============================================
	// Função processarDadosFichaPonto importada de $lib/utils/ponto/processamento
	// A função abaixo foi movida para o módulo e está comentada para referência
	/*
	async function processarDadosFichaPontoOld(
		funcionarioId: Id<'funcionarios'>,
		dataInicio: string,
		dataFim: string
	): Promise<{
		dias: DiaFichaPonto[];
		resumo: ResumoPeriodo;
		config: {
			horarioEntrada: string;
			horarioSaidaAlmoco: string;
			horarioRetornoAlmoco: string;
			horarioSaida: string;
		};
	}> {
		// Buscar todos os dados necessários
		const [
			registrosFuncionario,
			atestadosLicencas,
			ausenciasTodas,
			ajustes,
			inconsistencias,
			homologacoes,
			dispensas,
			config
		] = await Promise.all([
			client.query(api.pontos.listarRegistrosPeriodo, {
				funcionarioId,
				dataInicio,
				dataFim
			}),
			client.query(api.atestadosLicencas.listarPorFuncionario, {
				funcionarioId
			}),
			client.query(api.ausencias.listarTodas, {}),
			client.query(api.pontos.listarAjustesBancoHoras, {
				funcionarioId
			}),
			client.query(api.pontos.listarInconsistenciasBancoHoras, {}),
			client.query(api.pontos.listarHomologacoes, {
				funcionarioId
			}),
			client.query(api.pontos.listarDispensas, {
				funcionarioId,
				apenasAtivas: false
			}),
			client.query(api.configuracaoPonto.obterConfiguracao, {})
		]);

		const atestados = atestadosLicencas?.atestados || [];
		const licencas = atestadosLicencas?.licencas || [];
		const ausencias = (ausenciasTodas || []).filter((a) => a.funcionarioId === funcionarioId);

		if (!config) {
			throw new Error('Configuração de ponto não encontrada');
		}

		// Filtrar dados pelo período
		const dataInicioObj = new Date(dataInicio + 'T00:00:00');
		const dataFimObj = new Date(dataFim + 'T23:59:59');

		const atestadosPeriodo = (atestados || []).filter((a) => {
			const inicio = new Date(a.dataInicio);
			const fim = new Date(a.dataFim);
			return inicio <= dataFimObj && fim >= dataInicioObj;
		});

		const ausenciasPeriodo = (ausencias || []).filter((a) => {
			const inicio = new Date(a.dataInicio);
			const fim = new Date(a.dataFim);
			return inicio <= dataFimObj && fim >= dataInicioObj;
		});

		const licencasPeriodo = (licencas || []).filter((l) => {
			const inicio = new Date(l.dataInicio);
			const fim = new Date(l.dataFim);
			return inicio <= dataFimObj && fim >= dataInicioObj;
		});

		const ajustesPeriodo = (ajustes || []).filter((a) => {
			const dataAjuste = new Date(a.dataAplicacao);
			return dataAjuste >= dataInicioObj && dataAjuste <= dataFimObj;
		});

		const inconsistenciasPeriodo = (inconsistencias || []).filter((i) => {
			if (i.funcionarioId !== funcionarioId) return false;
			const dataInconsistencia = new Date(i.dataDetectada);
			return dataInconsistencia >= dataInicioObj && dataInconsistencia <= dataFimObj;
		});

		const dataInicioTimestamp = dataInicioObj.getTime();
		const dataFimTimestamp = dataFimObj.getTime();
		const homologacoesPeriodo = (homologacoes || []).filter((h) => {
			return h.criadoEm >= dataInicioTimestamp && h.criadoEm <= dataFimTimestamp;
		});

		const dispensasPeriodo = (dispensas || []).filter((d) => {
			const dispensaInicio = new Date(d.dataInicio + 'T00:00:00');
			const dispensaFim = new Date(d.dataFim + 'T23:59:59');
			return dispensaInicio <= dataFimObj && dispensaFim >= dataInicioObj;
		});

		// Gerar todos os dias do período
		const diasPeriodo = gerarDiasPeriodo(dataInicio, dataFim);
		const diasProcessados: DiaFichaPonto[] = [];

		// Agrupar registros por data
		const registrosPorData: Record<string, RegistroPonto[]> = {};
		for (const r of registrosFuncionario || []) {
			if (!registrosPorData[r.data]) {
				registrosPorData[r.data] = [];
			}
			registrosPorData[r.data]!.push(r);
		}

		// Processar cada dia
		for (const data of diasPeriodo) {
			const dataObj = new Date(data);
			const regsReais = registrosPorData[data] || [];
			const regsEsperados = gerarRegistrosEsperados(data, config);

			// Verificar atestado
			const atestadoDia =
				atestadosPeriodo.find((a) => {
					const inicio = new Date(a.dataInicio);
					const fim = new Date(a.dataFim);
					return dataObj >= inicio && dataObj <= fim;
				}) || null;

			// Verificar ausência
			const ausenciaDia =
				ausenciasPeriodo.find((a) => {
					const inicio = new Date(a.dataInicio);
					const fim = new Date(a.dataFim);
					return dataObj >= inicio && dataObj <= fim;
				}) || null;

			// Verificar licença
			const licencaDia =
				licencasPeriodo.find((l) => {
					const inicio = new Date(l.dataInicio);
					const fim = new Date(l.dataFim);
					return dataObj >= inicio && dataObj <= fim;
				}) || null;

			// Verificar ajustes do dia
			const ajustesDia = ajustesPeriodo.filter((a) => a.dataAplicacao === data);

			// Verificar inconsistências do dia
			const inconsistenciasDia = inconsistenciasPeriodo.filter((i) => i.dataDetectada === data);

			// Verificar homologações do dia
			const homologacoesDia = homologacoesPeriodo.filter((h) => {
				if (h.registroId) {
					const registro = regsReais.find((r) => r._id === h.registroId);
					return registro !== undefined;
				}
				return false;
			});

			// Verificar dispensa
			const dispensaDia =
				dispensasPeriodo.find((d) => {
					const dispensaInicio = new Date(d.dataInicio + 'T00:00:00');
					const dispensaFim = new Date(d.dataFim + 'T23:59:59');
					return dataObj >= dispensaInicio && dataObj <= dispensaFim;
				}) || null;

			// Calcular saldo diário
			const regsReaisOrdenados = [...regsReais].sort((a, b) => {
				if (a.hora !== b.hora) return a.hora - b.hora;
				return a.minuto - b.minuto;
			});
			const saldosComparativosPorPar = calcularSaldoComparativoPorPar(regsReaisOrdenados, config);

			let saldoDiario: SaldoDiario | null = null;
			let saldoDiarioTotalDiferencaMinutos = 0;
			let saldoDiarioTotalTrabalhadoMinutos = 0;
			let saldoDiarioTotalEsperadoMinutos = 0;

			// Somar saldos dos pares
			const paresProcessados = new Set<number>();
			for (const [, saldo] of saldosComparativosPorPar.entries()) {
				if (!paresProcessados.has(saldo.parIndex)) {
					saldoDiarioTotalDiferencaMinutos += saldo.diferencaMinutos;
					saldoDiarioTotalTrabalhadoMinutos += saldo.trabalhadoMinutos;
					saldoDiarioTotalEsperadoMinutos += saldo.esperadoMinutos;
					paresProcessados.add(saldo.parIndex);
				}
			}

			// Calcular saldo para pares não marcados
			const todosRegistros: Array<{ tipo: string; hora: number; minuto: number; real: boolean }> =
				[];
			for (const reg of regsReais) {
				todosRegistros.push({
					tipo: reg.tipo,
					hora: reg.hora,
					minuto: reg.minuto,
					real: true
				});
			}
			for (const regEsperado of regsEsperados) {
				if (!registroFoiMarcado(regEsperado, regsReais)) {
					todosRegistros.push({
						tipo: regEsperado.tipo,
						hora: regEsperado.hora,
						minuto: regEsperado.minuto,
						real: false
					});
				}
			}

			// Identificar pares não marcados e calcular saldo negativo
			for (let i = 0; i < todosRegistros.length; i++) {
				const reg = todosRegistros[i];
				if ((reg.tipo === 'entrada' || reg.tipo === 'retorno_almoco') && !reg.real) {
					const tipoSaidaEsperado = reg.tipo === 'entrada' ? 'saida_almoco' : 'saida';
					const saidaEsperada = todosRegistros.find((r, idx) => {
						if (idx <= i) return false;
						if (r.tipo !== tipoSaidaEsperado || r.real) return false;
						const minutosEntrada = reg.hora * 60 + reg.minuto;
						const minutosSaidaEsperada = r.hora * 60 + r.minuto;
						const temRegistroRealNoIntervalo = regsReais.some((real) => {
							if (real.tipo !== tipoSaidaEsperado) return false;
							const minutosReal = real.hora * 60 + real.minuto;
							return minutosReal >= minutosEntrada && minutosReal < minutosSaidaEsperada;
						});
						return !temRegistroRealNoIntervalo;
					});

					if (saidaEsperada) {
						let esperadoMinutos: number;
						if (reg.tipo === 'entrada') {
							const [horaEntradaEsperada, minutoEntradaEsperado] = config.horarioEntrada
								.split(':')
								.map(Number);
							const [horaSaidaAlmocoEsperada, minutoSaidaAlmocoEsperado] = config.horarioSaidaAlmoco
								.split(':')
								.map(Number);
							const minutosEntradaEsperada = horaEntradaEsperada * 60 + minutoEntradaEsperado;
							const minutosSaidaEsperadaConfig =
								horaSaidaAlmocoEsperada * 60 + minutoSaidaAlmocoEsperado;
							esperadoMinutos = minutosSaidaEsperadaConfig - minutosEntradaEsperada;
							if (esperadoMinutos < 0) esperadoMinutos += 24 * 60;
						} else {
							const [horaRetornoAlmocoEsperado, minutoRetornoAlmocoEsperado] =
								config.horarioRetornoAlmoco.split(':').map(Number);
							const [horaSaidaEsperada, minutoSaidaEsperado] = config.horarioSaida
								.split(':')
								.map(Number);
							const minutosEntradaEsperada =
								horaRetornoAlmocoEsperado * 60 + minutoRetornoAlmocoEsperado;
							const minutosSaidaEsperadaConfig = horaSaidaEsperada * 60 + minutoSaidaEsperado;
							esperadoMinutos = minutosSaidaEsperadaConfig - minutosEntradaEsperada;
							if (esperadoMinutos < 0) esperadoMinutos += 24 * 60;
						}

						saldoDiarioTotalDiferencaMinutos -= esperadoMinutos;
						saldoDiarioTotalEsperadoMinutos += esperadoMinutos;
					}
				}
			}

			// Aplicar ajustes manuais
			for (const ajuste of ajustesDia) {
				if (ajuste.tipo === 'abonar') {
					saldoDiarioTotalDiferencaMinutos += ajuste.valorMinutos;
				} else if (ajuste.tipo === 'descontar') {
					saldoDiarioTotalDiferencaMinutos -= ajuste.valorMinutos;
				}
			}

			// Calcular diferença final
			const diferencaDiariaCorrigida =
				saldoDiarioTotalTrabalhadoMinutos - saldoDiarioTotalEsperadoMinutos;

			saldoDiario = {
				diferencaMinutos: diferencaDiariaCorrigida,
				trabalhadoMinutos: saldoDiarioTotalTrabalhadoMinutos,
				esperadoMinutos: saldoDiarioTotalEsperadoMinutos
			};

			// Determinar tipo de dia
			let tipoDia: TipoDia = 'normal';
			let computado = true;

			if (dispensaDia) {
				tipoDia = 'nao_computado';
				computado = false;
			} else if (licencaDia) {
				tipoDia = 'licenca';
				computado = false;
			} else if (atestadoDia) {
				tipoDia = 'atestado';
				computado = false;
			} else if (ausenciaDia) {
				tipoDia = 'ausencia';
				computado = false;
			} else if (ajustesDia.some((a) => a.tipo === 'abonar' && a.valorMinutos >= 240)) {
				tipoDia = 'abonado';
			}

			if (inconsistenciasDia.length > 0) {
				tipoDia = 'inconsistente';
			}

			diasProcessados.push({
				data,
				dataFormatada: formatarDataDDMMAAAA(data),
				tipoDia,
				registros: regsReais,
				registrosEsperados: regsEsperados,
				saldoDiario,
				saldoAcumulado: 0, // Será calculado depois
				atestado: atestadoDia
					? {
							_id: atestadoDia._id,
							tipo: atestadoDia.tipo,
							dataInicio: atestadoDia.dataInicio,
							dataFim: atestadoDia.dataFim,
							motivo: atestadoDia.observacoes
						}
					: null,
				ausencia: ausenciaDia
					? {
							_id: ausenciaDia._id,
							motivo: ausenciaDia.motivo,
							dataInicio: ausenciaDia.dataInicio,
							dataFim: ausenciaDia.dataFim,
							status: ausenciaDia.status
						}
					: null,
				licenca: licencaDia
					? {
							_id: licencaDia._id,
							tipo: licencaDia.tipo || 'licenca',
							dataInicio: licencaDia.dataInicio,
							dataFim: licencaDia.dataFim
						}
					: null,
				ajustes: ajustesDia.map((a) => ({
					_id: a._id,
					tipo: a.tipo,
					valorMinutos: a.valorMinutos,
					motivoDescricao: a.motivoDescricao,
					gestorId: a.gestorId
				})),
				inconsistencias: inconsistenciasDia.map((i) => ({
					_id: i._id,
					tipo: i.tipo,
					descricao: i.descricao,
					dataDetectada: i.dataDetectada,
					status: i.status,
					resolvidoPor: i.resolvidoPor,
					resolvidoEm: i.resolvidoEm
				})),
				homologacoes: homologacoesDia.map((h) => ({
					_id: h._id,
					motivoDescricao: h.motivoDescricao,
					gestorId: h.gestorId
				})),
				dispensa: dispensaDia
					? {
							_id: dispensaDia._id,
							motivo: dispensaDia.motivo,
							dataInicio: dispensaDia.dataInicio,
							dataFim: dispensaDia.dataFim,
							ativo: dispensaDia.ativo
						}
					: null,
				computado
			});
		}

		// Calcular saldo acumulado para cada dia
		// TODO: Buscar saldo inicial do backend se necessário
		let saldoAcumulado = 0; // Saldo inicial (será 0 por enquanto, pode ser buscado do backend)

		for (const dia of diasProcessados) {
			if (dia.computado && dia.saldoDiario) {
				saldoAcumulado += dia.saldoDiario.diferencaMinutos;
			}
			dia.saldoAcumulado = saldoAcumulado;
		}

		// Calcular resumo com formatações
		const totalHorasTrabalhadas = diasProcessados
			.filter((d) => d.computado)
			.reduce((acc, d) => acc + (d.saldoDiario?.trabalhadoMinutos || 0), 0);
		const totalHorasEsperadas = diasProcessados
			.filter((d) => d.computado)
			.reduce((acc, d) => acc + (d.saldoDiario?.esperadoMinutos || 0), 0);
		const diferencaTotal = diasProcessados
			.filter((d) => d.computado)
			.reduce((acc, d) => acc + (d.saldoDiario?.diferencaMinutos || 0), 0);
		const saldoPeriodo = diferencaTotal;
		const saldoFinal = diasProcessados.length > 0 ? diasProcessados[diasProcessados.length - 1]!.saldoAcumulado : 0;

		const resumo: ResumoPeriodo = {
			totalDias: diasProcessados.length,
			diasTrabalhados: diasProcessados.filter((d) => d.computado && d.registros.length > 0).length,
			diasComAtestado: diasProcessados.filter((d) => d.atestado !== null).length,
			diasAusentes: diasProcessados.filter((d) => d.ausencia !== null).length,
			diasComLicenca: diasProcessados.filter((d) => d.licenca !== null).length,
			diasAbonados: diasProcessados.filter((d) => d.tipoDia === 'abonado').length,
			diasNaoComputados: diasProcessados.filter((d) => !d.computado).length,
			diasComInconsistencia: diasProcessados.filter((d) => d.inconsistencias.length > 0).length,
			totalHorasTrabalhadas,
			totalHorasEsperadas,
			diferencaTotal,
			saldoInicial: 0, // TODO: Buscar do backend
			saldoFinal,
			saldoPeriodo,
			totalInconsistencias: inconsistenciasPeriodo.length,
			saldoInicialFormatado: formatarMinutos(0),
			saldoPeriodoFormatado: formatarMinutos(saldoPeriodo),
			saldoFinalFormatado: formatarMinutos(saldoFinal),
			totalHorasTrabalhadasFormatado: formatarHoras(totalHorasTrabalhadas),
			totalHorasEsperadasFormatado: formatarHoras(totalHorasEsperadas),
			diferencaTotalFormatado: formatarMinutos(diferencaTotal)
		};

		return {
			dias: diasProcessados,
			resumo,
			config
		};
	}
	*/

	// Função para limpar todos os filtros
	function limparFiltros() {
		const hoje = new Date();
		const trintaDiasAtras = new Date(hoje);
		trintaDiasAtras.setDate(hoje.getDate() - 30);

		const dataInicioStr = trintaDiasAtras.toISOString().split('T')[0]!;
		const dataFimStr = hoje.toISOString().split('T')[0]!;

		dataInicioInterno = dataInicioStr;
		dataFimInterno = dataFimStr;
		dataInicioExibicao = formatarDataParaExibicao(dataInicioStr);
		dataFimExibicao = formatarDataParaExibicao(dataFimStr);
		funcionarioIdFiltro = '';
		statusFiltro = 'todos';
		localizacaoFiltro = 'todos';
		toast.success('Filtros limpos com sucesso!');
	}

	// Função para exportar registros para CSV
	async function exportarCSV() {
		try {
			const registrosParaExportar = registrosFiltrados;

			if (!registrosParaExportar || registrosParaExportar.length === 0) {
				toast.error('Nenhum registro para exportar');
				return;
			}

			// Preparar dados para CSV
			const csvData = registrosParaExportar.map((registro) => {
				const funcionarioNome = registro.funcionario?.nome || 'N/A';
				const funcionarioMatricula = registro.funcionario?.matricula || 'N/A';
				const tipo = config
					? getTipoRegistroLabel(registro.tipo, {
							nomeEntrada: config.nomeEntrada,
							nomeSaidaAlmoco: config.nomeSaidaAlmoco,
							nomeRetornoAlmoco: config.nomeRetornoAlmoco,
							nomeSaida: config.nomeSaida
						})
					: getTipoRegistroLabel(registro.tipo);

				const horario = formatarHoraPonto(registro.hora, registro.minuto);
				const status = registro.dentroDoPrazo ? 'Dentro do Prazo' : 'Fora do Prazo';
				const localizacao =
					registro.dentroRaioPermitido === true
						? 'Dentro do Raio'
						: registro.dentroRaioPermitido === false
							? 'Fora do Raio'
							: 'Não Validado';

				return {
					Data: formatarDataDDMMAAAA(registro.data),
					Funcionário: funcionarioNome,
					Matrícula: funcionarioMatricula,
					Tipo: tipo,
					Horário: horario,
					Status: status,
					Localização: localizacao,
					IP: registro.ipAddress || 'N/A',
					Dispositivo: registro.deviceType || 'N/A'
				};
			});

			// Gerar CSV usando Papa Parse
			const csv = Papa.unparse(csvData, {
				header: true,
				delimiter: ';',
				encoding: 'UTF-8'
			});

			// Adicionar BOM para Excel reconhecer UTF-8 corretamente
			const BOM = '\uFEFF';
			const csvComBOM = BOM + csv;

			// Criar blob e download
			const blob = new Blob([csvComBOM], { type: 'text/csv;charset=utf-8;' });
			const link = document.createElement('a');
			const url = URL.createObjectURL(blob);
			link.setAttribute('href', url);
			link.setAttribute(
				'download',
				`registros-ponto-${formatarDataDDMMAAAA(dataInicio)}-${formatarDataDDMMAAAA(dataFim)}.csv`
			);
			link.style.visibility = 'hidden';
			document.body.appendChild(link);
			link.click();
			document.body.removeChild(link);
			URL.revokeObjectURL(url);

			toast.success('CSV exportado com sucesso!');
		} catch (error) {
			console.error('Erro ao exportar CSV:', error);
			toast.error('Erro ao exportar CSV. Tente novamente.');
		}
	}

	// Wrapper para a função importada gerarPDFComSelecao
	async function gerarPDFComSelecaoWrapper(sections: SectionsPDF) {
		if (!funcionarioParaImprimir) return;

		const funcionarioId = funcionarioParaImprimir;
		
		await gerarPDFComSelecao(
			client,
			sections,
			funcionarioId,
			dataInicio,
			dataFim,
			funcionarios,
			logoGovPE,
			(message: string) => toast.error(message),
			() => {
				mostrarModalImpressao = false;
				funcionarioParaImprimir = '';
				toast.success('PDF gerado com sucesso!');
			},
			(value: boolean) => { carregando = value; }
		);
	}

	// Função antiga comentada (foi movida para módulo)
	/*
	async function gerarPDFComSelecaoOld(sections: SectionsPDF) {
		if (!funcionarioParaImprimir) return;

		const funcionarioId = funcionarioParaImprimir;

		// Verificar se pelo menos uma seção foi selecionada
		if (!Object.values(sections).some((v) => v)) {
			toast.error('Selecione pelo menos uma seção para imprimir');
			return;
		}

		// Validar período
		const validacaoPeriodo = validarPeriodo(dataInicio, dataFim);
		if (!validacaoPeriodo.valido) {
			toast.error(validacaoPeriodo.erro || 'Período inválido');
			return;
		}

		const funcionario = funcionarios.find((f) => f._id === funcionarioId);
		if (!funcionario) {
			toast.error('Funcionário não encontrado');
			return;
		}

		try {
			// Início do bloco try para geração de PDF
			carregando = true;
			// Processar todos os dados necessários
			const {
				dias,
				resumo,
				config: configPonto
			} = await processarDadosFichaPonto(client, funcionarioId, dataInicio, dataFim);

			if (dias.length === 0) {
				toast.error('Nenhum dado encontrado para este funcionário no período selecionado');
				carregando = false;
				return;
			}

			const doc = new jsPDF();

			// Logo e cabeçalho
			let yPosition = await adicionarLogo(doc, logoGovPE);
			yPosition = adicionarCabecalho(doc, yPosition);

			// Dados do Funcionário
			if (sections.dadosFuncionario) {
				yPosition = adicionarDadosFuncionario(doc, yPosition, funcionario, dataInicio, dataFim);
			}

			// Resumo do Período
			yPosition = adicionarResumoPeriodo(doc, yPosition, resumo, formatarHoras, formatarMinutos);

			// Saldos do Período
			yPosition = adicionarSaldosPeriodo(doc, yPosition, resumo, formatarMinutos);

			// Legenda
			yPosition = adicionarLegenda(doc, yPosition);

			// ============================================
			// SEÇÃO: TABELA PRINCIPAL DE REGISTROS
			// ============================================
			if (sections.registrosPonto) {
				if (yPosition > 250) {
					doc.addPage();
					yPosition = 20;
				}

				doc.setFontSize(14);
				doc.setTextColor(41, 128, 185);
				doc.setFont('helvetica', 'bold');
				doc.text('REGISTROS DE PONTO', 15, yPosition);
				yPosition += 10;

				// Função auxiliar para obter cor de fundo baseada no tipo de dia
				const obterCorFundoTipoDia = (tipoDia: TipoDia): number[] => {
					switch (tipoDia) {
						case 'atestado':
							return [230, 240, 255]; // Azul claro
						case 'ausencia':
							return [255, 255, 230]; // Amarelo claro
						case 'abonado':
							return [230, 255, 230]; // Verde claro
						case 'nao_computado':
							return [240, 240, 240]; // Cinza claro
						case 'inconsistente':
							return [255, 240, 230]; // Laranja claro
						default:
							return [255, 255, 255]; // Branco
					}
				};

				// Função auxiliar para obter ícone do tipo de dia
				const obterIconeTipoDia = (dia: DiaFichaPonto): string => {
					if (dia.atestado) return '🏥';
					if (dia.ausencia) return '🚫';
					if (dia.licenca) return '📋';
					if (dia.tipoDia === 'abonado') return '✅';
					if (dia.tipoDia === 'nao_computado') return '⏸';
					if (dia.inconsistencias.length > 0) return '⚠';
					return '';
				};

				// Função auxiliar para obter texto do tipo de dia
				const obterTextoTipoDia = (dia: DiaFichaPonto): string => {
					if (dia.atestado) return 'Atestado';
					if (dia.ausencia) return 'Ausência';
					if (dia.licenca) return 'Licença';
					if (dia.tipoDia === 'abonado') return 'Abonado';
					if (dia.tipoDia === 'nao_computado') return 'Não Computado';
					if (dia.inconsistencias.length > 0) return 'Inconsistente';
					return 'Normal';
				};

				// Função auxiliar para formatar saldo
				const formatarSaldo = (saldo: SaldoDiario | null): string => {
					if (!saldo) return '-';
					const horas = Math.floor(Math.abs(saldo.diferencaMinutos) / 60);
					const minutos = Math.abs(saldo.diferencaMinutos) % 60;
					const sinal = saldo.diferencaMinutos >= 0 ? '+' : '-';
					return `${sinal}${horas}h ${minutos}min`;
				};

				const tableData: Array<
					Array<
						| string
						| {
								content: string;
								styles?: { fillColor?: number[]; textColor?: number[]; fontStyle?: string };
						  }
					>
				> = [];

				// Processar cada dia usando os dados já processados
				for (const dia of dias) {
					const corFundo = obterCorFundoTipoDia(dia.tipoDia);
					const iconeTipoDia = obterIconeTipoDia(dia);
					const textoTipoDia = obterTextoTipoDia(dia);

					// Combinar registros reais e esperados
					const todosRegistros: Array<{
						tipo: string;
						hora: number;
						minuto: number;
						real: boolean;
						dentroDoPrazo?: boolean;
					}> = [];

					// Adicionar registros reais
					for (const reg of dia.registros) {
						todosRegistros.push({
							tipo: reg.tipo,
							hora: reg.hora,
							minuto: reg.minuto,
							real: true,
							dentroDoPrazo: reg.dentroDoPrazo
						});
					}

					// Adicionar registros esperados não marcados
					for (const regEsperado of dia.registrosEsperados) {
						if (!registroFoiMarcado(regEsperado, dia.registros)) {
							todosRegistros.push({
								tipo: regEsperado.tipo,
								hora: regEsperado.hora,
								minuto: regEsperado.minuto,
								real: false
							});
						}
					}

					// Ordenar registros por hora e minuto
					todosRegistros.sort((a, b) => {
						if (a.hora !== b.hora) return a.hora - b.hora;
						return a.minuto - b.minuto;
					});

					// Criar linhas da tabela para cada registro
					for (let i = 0; i < todosRegistros.length; i++) {
						const reg = todosRegistros[i];
						const linha: Array<
							| string
							| {
									content: string;
									styles?: { fillColor?: number[]; textColor?: number[]; fontStyle?: string };
							  }
						> = [];

						// Coluna Data (apenas na primeira linha do dia)
						if (i === 0) {
							linha.push({
								content: dia.dataFormatada,
								styles: { fillColor: corFundo, fontStyle: 'bold' }
							});
						} else {
							linha.push('');
						}

						// Coluna Tipo de Dia (apenas na primeira linha)
						if (i === 0) {
							linha.push({
								content: `${iconeTipoDia} ${textoTipoDia}`,
								styles: { fillColor: corFundo }
							});
						} else {
							linha.push('');
						}

						// Coluna Tipo de Registro
						const tipoLabel = configPonto
							? getTipoRegistroLabel(
									reg.tipo as 'entrada' | 'saida_almoco' | 'retorno_almoco' | 'saida',
									{
										nomeEntrada: configPonto.nomeEntrada,
										nomeSaidaAlmoco: configPonto.nomeSaidaAlmoco,
										nomeRetornoAlmoco: configPonto.nomeRetornoAlmoco,
										nomeSaida: configPonto.nomeSaida
									}
								)
							: getTipoRegistroLabel(reg.tipo as 'entrada' | 'saida_almoco' | 'retorno_almoco' | 'saida');

						linha.push({
							content: `${reg.real ? '✓' : '✗'} ${tipoLabel}`,
							styles: {
								textColor: reg.real ? [0, 128, 0] : [200, 0, 0], // Verde se marcado, vermelho se não
								fillColor: corFundo
							}
						});

						// Coluna Horário
						linha.push({
							content: formatarHoraPonto(reg.hora, reg.minuto),
							styles: {
								textColor: reg.real ? [0, 0, 0] : [200, 0, 0], // Preto se marcado, vermelho se não
								fillColor: corFundo
							}
						});

						// Coluna Saldo Diário (apenas na primeira linha do dia, se houver saldo)
						if (sections.saldoDiario) {
							if (i === 0 && dia.saldoDiario) {
								const saldoFormatado = formatarSaldo(dia.saldoDiario);
								const saldoPositivo = dia.saldoDiario.diferencaMinutos > 0;
								const saldoNegativo = dia.saldoDiario.diferencaMinutos < 0;

								linha.push({
									content: saldoFormatado,
									styles: {
										textColor: saldoPositivo ? [0, 128, 0] : saldoNegativo ? [200, 0, 0] : [0, 0, 0],
										fontStyle: saldoPositivo || saldoNegativo ? 'bold' : 'normal',
										fillColor: corFundo
									}
								});
							} else {
								linha.push('');
							}
						}

						// Coluna Observações (apenas na primeira linha)
						if (i === 0) {
							const observacoes: string[] = [];
							if (dia.atestado) {
								observacoes.push(`Atestado: ${dia.atestado.motivo}`);
							}
							if (dia.ausencia) {
								observacoes.push(`Ausência: ${dia.ausencia.motivo}`);
							}
							if (dia.licenca) {
								observacoes.push(`Licença: ${dia.licenca.tipo}`);
							}
							if (dia.dispensa) {
								observacoes.push(`Dispensa: ${dia.dispensa.motivo}`);
							}
							if (dia.inconsistencias.length > 0) {
								observacoes.push(
									`⚠ ${dia.inconsistencias.length} inconsistência(ões): ${dia.inconsistencias.map((inc) => inc.descricao).join('; ')}`
								);
							}
							if (dia.ajustes.length > 0) {
								observacoes.push(
									`Ajustes: ${dia.ajustes.map((a) => `${a.tipo} ${Math.floor(a.valorMinutos / 60)}h ${a.valorMinutos % 60}min`).join(', ')}`
								);
							}

							linha.push({
								content: observacoes.join(' | ') || '-',
								styles: { fillColor: corFundo, fontSize: 8 }
							});
						} else {
							linha.push('');
						}

						// Coluna Status (Dentro do Prazo)
						linha.push(
							reg.real
								? reg.dentroDoPrazo !== undefined
									? reg.dentroDoPrazo
										? 'Sim'
										: 'Não'
									: 'Não marcado'
								: 'Não marcado'
						);

						tableData.push(linha);
					}
				}

				// Cabeçalhos da tabela
				const headers = ['Data', 'Tipo de Dia', 'Tipo', 'Horário'];
				if (sections.saldoDiario) {
					headers.push('Saldo Diário');
				}
				headers.push('Observações', 'Dentro do Prazo');

				// Salvar a posição Y antes da tabela
				const yPosAntesTabela = yPosition;

				// Gerar tabela usando os dados processados
				autoTable(doc, {
					startY: yPosition,
					head: [headers],
					body: tableData,
					theme: 'grid',
					headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
					styles: { fontSize: 9 },
					didParseCell: function (data) {
						// Aplicar cores baseadas no tipo de dia e status dos registros
						if (data.row.raw) {
							const rowData = data.row.raw as Array<
								| string
								| {
										content: string;
										styles?: { fillColor?: number[]; textColor?: number[]; fontStyle?: string };
								  }
							>;
							// Aplicar estilos de células que têm objetos com styles
							if (rowData[data.column.index] && typeof rowData[data.column.index] === 'object') {
								const cellData = rowData[data.column.index] as {
									content: string;
									styles?: { fillColor?: number[]; textColor?: number[]; fontStyle?: string };
								};
								if (cellData.styles) {
									if (cellData.styles.fillColor) {
										data.cell.styles.fillColor = cellData.styles.fillColor;
									}
									if (cellData.styles.textColor) {
										data.cell.styles.textColor = cellData.styles.textColor;
									}
									if (cellData.styles.fontStyle) {
										data.cell.styles.fontStyle = cellData.styles.fontStyle;
									}
								}
							}
						}
					}
				});

				type JsPDFWithAutoTableRegistros = jsPDF & {
					lastAutoTable?: { finalY: number };
				};
				const finalYRegistros =
					(doc as JsPDFWithAutoTableRegistros).lastAutoTable?.finalY ?? yPosition + 10;
				yPosition = finalYRegistros + 10;

				// Calcular saldo diário total (diferença acumulada de todos os pares)
					// Declarar variáveis ANTES de usá-las
					let saldoDiarioTotalDiferencaMinutos = 0;
					let saldoDiarioTotalTrabalhadoMinutos = 0;
					let saldoDiarioTotalEsperadoMinutos = 0;

					// Criar conjunto de chaves de registros que já foram processados como completos
					// Isso será usado tanto para evitar criar pares incompletos quanto para evitar somar duplicados
					const chavesProcessadasCompletas = new Set<string>();
					const paresCompletosProcessados = new Set<number>(); // Rastrear parIndex já processados

					// Criar conjunto de chaves e somar saldos dos pares completos
					// IMPORTANTE: saldosComparativosPorPar associa o saldo a AMBOS os registros do par (entrada + saída)
					// Então precisamos garantir que somamos apenas UMA VEZ por par, não uma vez por registro
					for (const [index, saldo] of saldosComparativosPorPar.entries()) {
						const regEntrada = regsReaisOrdenados[index];
						if (regEntrada) {
							// Verificar se este parIndex já foi processado
							if (!paresCompletosProcessados.has(saldo.parIndex)) {
								// Primeira vez processando este par, somar o saldo
								saldoDiarioTotalDiferencaMinutos += saldo.diferencaMinutos;
								saldoDiarioTotalTrabalhadoMinutos += saldo.trabalhadoMinutos;
								saldoDiarioTotalEsperadoMinutos += saldo.esperadoMinutos;
								paresCompletosProcessados.add(saldo.parIndex);
							}

							// Adicionar chaves para evitar processamento duplicado depois
							const chaveEntrada = `${regEntrada.tipo}-${regEntrada.hora}-${regEntrada.minuto}`;
							chavesProcessadasCompletas.add(chaveEntrada);
							// Encontrar saída correspondente
							const tipoSaidaEsperado = regEntrada.tipo === 'entrada' ? 'saida_almoco' : 'saida';
							for (let j = index + 1; j < regsReaisOrdenados.length; j++) {
								const regSaida = regsReaisOrdenados[j];
								if (regSaida && regSaida.tipo === tipoSaidaEsperado) {
									const chaveSaida = `${regSaida.tipo}-${regSaida.hora}-${regSaida.minuto}`;
									chavesProcessadasCompletas.add(chaveSaida);
									break;
								}
							}
						}
					}

					// Identificar pares incompletos e calcular saldos esperados
					// IMPORTANTE: Só processar pares que NÃO foram processados como completos
					for (let i = 0; i < todosRegistros.length; i++) {
						const reg = todosRegistros[i];

						// Se é entrada ou retorno_almoco (início de par) e é real
						if ((reg.tipo === 'entrada' || reg.tipo === 'retorno_almoco') && reg.real) {
							// Verificar se este registro já foi processado como par completo
							const chaveRegistro = `${reg.tipo}-${reg.hora}-${reg.minuto}`;
							if (chavesProcessadasCompletas.has(chaveRegistro)) {
								continue; // Já foi processado como par completo, pular
							}

							// Verificar se há saída correspondente real
							const tipoSaidaEsperado = reg.tipo === 'entrada' ? 'saida_almoco' : 'saida';
							const saidaEncontrada = regsReais.find((r) => r.tipo === tipoSaidaEsperado);

							// Se há saída correspondente, verificar se ela também está em chavesProcessadasCompletas
							// Se estiver, significa que este par já foi processado como completo
							if (saidaEncontrada) {
								const chaveSaida = `${saidaEncontrada.tipo}-${saidaEncontrada.hora}-${saidaEncontrada.minuto}`;
								if (chavesProcessadasCompletas.has(chaveSaida)) {
									continue; // Par completo já processado, pular
								}
							}

							if (!saidaEncontrada) {
								// Par incompleto: entrada real sem saída correspondente
								// NÃO calcular tempo trabalhado aqui porque não há saída marcada
								// O tempo trabalhado será 0, e a diferença será negativa (0 - esperado)
								const regEsperado = regsEsperados.find((r) => r.tipo === tipoSaidaEsperado);
								if (regEsperado) {
									// Tempo trabalhado = 0 (não há saída marcada, então não podemos assumir tempo trabalhado)
									const trabalhadoMinutos = 0;

									// Calcular tempo esperado
									let esperadoMinutos: number;
									if (reg.tipo === 'entrada') {
										const [horaEntradaEsperada, minutoEntradaEsperado] = config.horarioEntrada
											.split(':')
											.map(Number);
										const [horaSaidaAlmocoEsperada, minutoSaidaAlmocoEsperado] =
											config.horarioSaidaAlmoco.split(':').map(Number);
										const minutosEntradaEsperada = horaEntradaEsperada * 60 + minutoEntradaEsperado;
										const minutosSaidaEsperadaConfig =
											horaSaidaAlmocoEsperada * 60 + minutoSaidaAlmocoEsperado;
										esperadoMinutos = minutosSaidaEsperadaConfig - minutosEntradaEsperada;
										if (esperadoMinutos < 0) esperadoMinutos += 24 * 60;
									} else {
										const [horaRetornoAlmocoEsperado, minutoRetornoAlmocoEsperado] =
											config.horarioRetornoAlmoco.split(':').map(Number);
										const [horaSaidaEsperada, minutoSaidaEsperado] = config.horarioSaida
											.split(':')
											.map(Number);
										const minutosEntradaEsperada =
											horaRetornoAlmocoEsperado * 60 + minutoRetornoAlmocoEsperado;
										const minutosSaidaEsperadaConfig = horaSaidaEsperada * 60 + minutoSaidaEsperado;
										esperadoMinutos = minutosSaidaEsperadaConfig - minutosEntradaEsperada;
										if (esperadoMinutos < 0) esperadoMinutos += 24 * 60;
									}

									// Calcular diferença (0 - esperado = negativo)
									const diferencaMinutos = -esperadoMinutos;

									const trabalhadoHoras = 0;
									const trabalhadoMinutosResto = 0;
									const esperadoHoras = Math.floor(esperadoMinutos / 60);
									const esperadoMinutosResto = esperadoMinutos % 60;
									const diferencaHoras = Math.floor(Math.abs(diferencaMinutos) / 60);
									const diferencaMinutosResto = Math.abs(diferencaMinutos) % 60;

									// Contar quantos registros fazem parte deste par na lista todosRegistros
									const indexNaListaTodos = todosRegistros.findIndex(
										(r, idx) =>
											idx >= i &&
											r.tipo === reg.tipo &&
											r.hora === reg.hora &&
											r.minuto === reg.minuto
									);

									// Contar registros do par (entrada + saída esperada)
									let tamanhoPar = 1; // entrada
									const saidaEsperadaNaLista = todosRegistros.find(
										(r, idx) => idx > indexNaListaTodos && r.tipo === tipoSaidaEsperado && !r.real
									);
									if (saidaEsperadaNaLista) {
										tamanhoPar++; // saída faltante já está na lista
									}

									if (indexNaListaTodos >= 0) {
										saldosEsperadosPorPar.set(indexNaListaTodos, {
											trabalhadoMinutos,
											trabalhadoHoras,
											trabalhadoMinutosResto,
											esperadoMinutos,
											esperadoHoras,
											esperadoMinutosResto,
											diferencaMinutos,
											diferencaHoras,
											diferencaMinutosResto,
											tamanhoPar,
											incompleto: true
										});
									}
								}
							}
						}
					}

					// Identificar pares completamente não marcados (quando há registros reais mas um par completo não foi marcado)
					if (regsReais.length > 0) {
						for (let i = 0; i < todosRegistros.length; i++) {
							const reg = todosRegistros[i];

							// Se é entrada ou retorno_almoco (início de par) e NÃO é real
							if ((reg.tipo === 'entrada' || reg.tipo === 'retorno_almoco') && !reg.real) {
								// Verificar se a saída correspondente também não foi marcada
								const tipoSaidaEsperado = reg.tipo === 'entrada' ? 'saida_almoco' : 'saida';

								// Buscar a saída esperada que também não foi marcada
								// IMPORTANTE: Garantir que estamos pegando a saída esperada correta do par,
								// não confundindo com saídas esperadas de outros pares ou registros reais
								const saidaEsperada = todosRegistros.find((r, idx) => {
									// Deve estar após a entrada atual
									if (idx <= i) return false;
									// Deve ser do tipo esperado e não real
									if (r.tipo !== tipoSaidaEsperado || r.real) return false;

									// Verificar se há algum registro real do mesmo tipo entre a entrada e esta saída
									// Se houver, esta não é a saída esperada do par não marcado
									const minutosEntrada = reg.hora * 60 + reg.minuto;
									const minutosSaidaEsperada = r.hora * 60 + r.minuto;

									// Verificar se há registro real do mesmo tipo no intervalo entre entrada e saída esperada
									const temRegistroRealNoIntervalo = regsReais.some((real) => {
										if (real.tipo !== tipoSaidaEsperado) return false;
										const minutosReal = real.hora * 60 + real.minuto;
										// Se o registro real está entre a entrada e a saída esperada, não é o par correto
										return minutosReal >= minutosEntrada && minutosReal < minutosSaidaEsperada;
									});

									// Se não há registro real no intervalo, esta é a saída esperada correta
									return !temRegistroRealNoIntervalo;
								});

								if (saidaEsperada) {
									// Par completamente não marcado: calcular saldo negativo
									// Tempo trabalhado = 0, tempo esperado = configurado, diferença = -esperado
									let esperadoMinutos: number;
									if (reg.tipo === 'entrada') {
										const [horaEntradaEsperada, minutoEntradaEsperado] = config.horarioEntrada
											.split(':')
											.map(Number);
										const [horaSaidaAlmocoEsperada, minutoSaidaAlmocoEsperado] =
											config.horarioSaidaAlmoco.split(':').map(Number);
										const minutosEntradaEsperada = horaEntradaEsperada * 60 + minutoEntradaEsperado;
										const minutosSaidaEsperadaConfig =
											horaSaidaAlmocoEsperada * 60 + minutoSaidaAlmocoEsperado;
										esperadoMinutos = minutosSaidaEsperadaConfig - minutosEntradaEsperada;
										if (esperadoMinutos < 0) esperadoMinutos += 24 * 60;
									} else {
										const [horaRetornoAlmocoEsperado, minutoRetornoAlmocoEsperado] =
											config.horarioRetornoAlmoco.split(':').map(Number);
										const [horaSaidaEsperada, minutoSaidaEsperado] = config.horarioSaida
											.split(':')
											.map(Number);
										const minutosEntradaEsperada =
											horaRetornoAlmocoEsperado * 60 + minutoRetornoAlmocoEsperado;
										const minutosSaidaEsperadaConfig = horaSaidaEsperada * 60 + minutoSaidaEsperado;
										esperadoMinutos = minutosSaidaEsperadaConfig - minutosEntradaEsperada;
										if (esperadoMinutos < 0) esperadoMinutos += 24 * 60;
									}

									// Trabalhado = 0, diferença = -esperado
									const trabalhadoMinutos = 0;
									const diferencaMinutos = -esperadoMinutos;

									const trabalhadoHoras = 0;
									const trabalhadoMinutosResto = 0;
									const esperadoHoras = Math.floor(esperadoMinutos / 60);
									const esperadoMinutosResto = esperadoMinutos % 60;
									const diferencaHoras = Math.floor(Math.abs(diferencaMinutos) / 60);
									const diferencaMinutosResto = Math.abs(diferencaMinutos) % 60;

									// Encontrar índice da saída esperada na lista (usando a mesma lógica melhorada)
									const indexSaidaEsperada = todosRegistros.findIndex((r, idx) => {
										if (idx <= i) return false;
										if (r.tipo !== tipoSaidaEsperado || r.real) return false;

										// Verificar se há registro real no intervalo
										const minutosEntrada = reg.hora * 60 + reg.minuto;
										const minutosSaidaEsperada = r.hora * 60 + r.minuto;

										const temRegistroRealNoIntervalo = regsReais.some((real) => {
											if (real.tipo !== tipoSaidaEsperado) return false;
											const minutosReal = real.hora * 60 + real.minuto;
											return minutosReal >= minutosEntrada && minutosReal < minutosSaidaEsperada;
										});

										return !temRegistroRealNoIntervalo;
									});

									// Associar saldo negativo ao início do par (entrada)
									saldosEsperadosPorPar.set(i, {
										trabalhadoMinutos,
										trabalhadoHoras,
										trabalhadoMinutosResto,
										esperadoMinutos,
										esperadoHoras,
										esperadoMinutosResto,
										diferencaMinutos,
										diferencaHoras,
										diferencaMinutosResto,
										tamanhoPar: indexSaidaEsperada >= 0 ? 2 : 1, // entrada + saída
										incompleto: false // Par completo não marcado
									});
								}
							}
						}
					}

					// NOTA: Os saldos dos pares completos já foram somados acima quando criamos chavesProcessadasCompletas
					// As variáveis saldoDiarioTotal* já foram declaradas e inicializadas acima

					// Somar saldos dos pares completamente não marcados e incompletos
					// IMPORTANTE: Não somar pares que já foram processados como completos acima
					// Usar o conjunto chavesProcessadasCompletas criado anteriormente
					for (const [indexNaListaTodos, saldo] of saldosEsperadosPorPar.entries()) {
						const regNaListaTodos = todosRegistros[indexNaListaTodos];

						// Verificar se este registro real já foi processado como par completo
						if (regNaListaTodos && regNaListaTodos.real) {
							const chaveRegistro = `${regNaListaTodos.tipo}-${regNaListaTodos.hora}-${regNaListaTodos.minuto}`;
							if (chavesProcessadasCompletas.has(chaveRegistro)) {
								// Este registro está em chavesProcessadasCompletas
								// Verificar se há uma saída correspondente que também está lá
								// Se ambas estão, significa que o par completo já foi processado
								const tipoSaidaEsperado =
									regNaListaTodos.tipo === 'entrada' ? 'saida_almoco' : 'saida';

								// Procurar saída correspondente em regsReais que também está em chavesProcessadasCompletas
								const saidaCompletaEncontrada = regsReais.find((r) => {
									if (r.tipo !== tipoSaidaEsperado) return false;
									const chaveSaida = `${r.tipo}-${r.hora}-${r.minuto}`;
									return chavesProcessadasCompletas.has(chaveSaida);
								});

								if (saidaCompletaEncontrada) {
									continue; // Par completo já processado (entrada + saída estão em chavesProcessadasCompletas), não somar novamente
								}
							}
						}

						if (saldo.trabalhadoMinutos === 0 && !saldo.incompleto) {
							// Par completamente não marcado: adicionar diferença negativa
							saldoDiarioTotalDiferencaMinutos += saldo.diferencaMinutos;
							saldoDiarioTotalTrabalhadoMinutos += saldo.trabalhadoMinutos; // 0
							saldoDiarioTotalEsperadoMinutos += saldo.esperadoMinutos;
						} else if (saldo.incompleto) {
							// Par incompleto: entrada real sem saída correspondente
							// Só somar se não foi processado como completo acima
							saldoDiarioTotalDiferencaMinutos += saldo.diferencaMinutos;
							saldoDiarioTotalTrabalhadoMinutos += saldo.trabalhadoMinutos;
							saldoDiarioTotalEsperadoMinutos += saldo.esperadoMinutos;
						}
					}

					// Se não há registros reais, calcular saldo esperado baseado na configuração
					if (regsReais.length === 0) {
						// Calcular saldo esperado do dia completo
						const [horaEntrada, minutoEntrada] = config.horarioEntrada.split(':').map(Number);
						const [horaSaidaAlmoco, minutoSaidaAlmoco] = config.horarioSaidaAlmoco
							.split(':')
							.map(Number);
						const [horaRetornoAlmoco, minutoRetornoAlmoco] = config.horarioRetornoAlmoco
							.split(':')
							.map(Number);
						const [horaSaida, minutoSaida] = config.horarioSaida.split(':').map(Number);

						// Par 1: entrada -> saida_almoco
						const minutosPar1Entrada = horaEntrada * 60 + minutoEntrada;
						const minutosPar1Saida = horaSaidaAlmoco * 60 + minutoSaidaAlmoco;
						let saldoPar1 = minutosPar1Saida - minutosPar1Entrada;
						if (saldoPar1 < 0) saldoPar1 += 24 * 60;

						// Par 2: retorno_almoco -> saida
						const minutosPar2Entrada = horaRetornoAlmoco * 60 + minutoRetornoAlmoco;
						const minutosPar2Saida = horaSaida * 60 + minutoSaida;
						let saldoPar2 = minutosPar2Saida - minutosPar2Entrada;
						if (saldoPar2 < 0) saldoPar2 += 24 * 60;

						saldoDiarioTotalTrabalhadoMinutos = 0; // Nenhum tempo trabalhado
						saldoDiarioTotalEsperadoMinutos = saldoPar1 + saldoPar2;
						saldoDiarioTotalDiferencaMinutos = -saldoDiarioTotalEsperadoMinutos; // Diferença negativa (0 - esperado)
					}

					// Calcular diferença corretamente: trabalhado - esperado (não somar diferenças dos pares)
					const diferencaDiariaCorrigida =
						saldoDiarioTotalTrabalhadoMinutos - saldoDiarioTotalEsperadoMinutos;

					// Armazenar saldo diário completo (usado no resumo do banco de horas)
					saldosDiariosPorData[data] = {
						diferencaMinutos: diferencaDiariaCorrigida,
						trabalhadoMinutos: saldoDiarioTotalTrabalhadoMinutos,
						esperadoMinutos: saldoDiarioTotalEsperadoMinutos
					};

					// Calcular carga horária diária total esperada para inicializar saldo acumulado
					const [horaEntrada, minutoEntrada] = config.horarioEntrada.split(':').map(Number);
					const [horaSaidaAlmoco, minutoSaidaAlmoco] = config.horarioSaidaAlmoco
						.split(':')
						.map(Number);
					const [horaRetornoAlmoco, minutoRetornoAlmoco] = config.horarioRetornoAlmoco
						.split(':')
						.map(Number);
					const [horaSaida, minutoSaida] = config.horarioSaida.split(':').map(Number);

					const minutosPar1Esperado =
						horaSaidaAlmoco * 60 + minutoSaidaAlmoco - (horaEntrada * 60 + minutoEntrada);
					const minutosPar1EsperadoAjustado =
						minutosPar1Esperado < 0 ? minutosPar1Esperado + 24 * 60 : minutosPar1Esperado;

					const minutosPar2Esperado =
						horaSaida * 60 + minutoSaida - (horaRetornoAlmoco * 60 + minutoRetornoAlmoco);
					const minutosPar2EsperadoAjustado =
						minutosPar2Esperado < 0 ? minutosPar2Esperado + 24 * 60 : minutosPar2Esperado;

					const cargaHorariaDiariaTotalMinutos =
						minutosPar1EsperadoAjustado + minutosPar2EsperadoAjustado;

					// Inicializar saldo diário acumulado com a carga horária total diária
					let saldoDiarioAcumuladoMinutos = cargaHorariaDiariaTotalMinutos;

					// Rastrear quais pares já foram processados para evitar decrementar múltiplas vezes
					// Usar string como chave: "tipo-parIndex" ou "tipo-indice" para pares incompletos
					const paresProcessadosParaSaldo = new Set<string>();

					// Criar linhas da tabela
					for (let i = 0; i < todosRegistros.length; i++) {
						const reg = todosRegistros[i];
						const linha: Array<string | { content: string; styles?: { fillColor?: number[]; textColor?: number[]; fontStyle?: string } }> = [
							dataFormatada,
							config
								? getTipoRegistroLabel(
										reg.tipo as 'entrada' | 'saida_almoco' | 'retorno_almoco' | 'saida',
										{
											nomeEntrada: config.nomeEntrada,
											nomeSaidaAlmoco: config.nomeSaidaAlmoco,
											nomeRetornoAlmoco: config.nomeRetornoAlmoco,
											nomeSaida: config.nomeSaida
										}
									)
								: getTipoRegistroLabel(
										reg.tipo as 'entrada' | 'saida_almoco' | 'retorno_almoco' | 'saida'
									),
							formatarHoraPonto(reg.hora, reg.minuto)
						];

						// Marcar linha como não marcada para aplicar cor vermelha depois
						if (!reg.real) {
							linha._naoMarcado = true;
						}

						// Saldo Diário por par entrada/saída com cálculo comparativo
						if (sections.saldoDiario) {
							const isInicioPar = reg.tipo === 'entrada' || reg.tipo === 'retorno_almoco';

							if (reg.real && isInicioPar) {
								const indexReal = regsReaisOrdenados.findIndex(
									(r) => r.tipo === reg.tipo && r.hora === reg.hora && r.minuto === reg.minuto
								);

								// Verificar se há saldo esperado (par incompleto)
								const saldoEsperado = saldosEsperadosPorPar.get(i);
								if (saldoEsperado) {
									// Par incompleto: decrementar saldo acumulado

									// Decrementar saldo acumulado apenas uma vez por par
									const chavePar = `${reg.tipo}-incompleto-${i}`;
									if (!paresProcessadosParaSaldo.has(chavePar)) {
										saldoDiarioAcumuladoMinutos -= saldoEsperado.trabalhadoMinutos;
										paresProcessadosParaSaldo.add(chavePar);
									}

									// Calcular saldo acumulado formatado
									// Se saldoDiarioAcumuladoMinutos > 0: ainda falta trabalhar (mostrar como negativo)
									// Se saldoDiarioAcumuladoMinutos < 0: trabalhou mais que o esperado (mostrar como positivo)
									const saldoAcumuladoHoras = Math.floor(
										Math.abs(saldoDiarioAcumuladoMinutos) / 60
									);
									const saldoAcumuladoMinutosResto = Math.abs(saldoDiarioAcumuladoMinutos) % 60;
									// Inverter sinal: positivo quando trabalhou mais, negativo quando ainda falta
									const sinalSaldo = saldoDiarioAcumuladoMinutos < 0 ? '+' : '-';
									const trabalhouMaisQueEsperado = saldoDiarioAcumuladoMinutos < 0;

									// Marcar linha para aplicar cor no saldo
									if (trabalhouMaisQueEsperado) {
										linha._saldoPositivo = true; // Verde: trabalhou mais que o esperado
									} else {
										linha._saldoNegativo = true; // Vermelho: ainda falta trabalhar
									}

									linha.push({
										content: `${saldoEsperado.trabalhadoHoras}h ${saldoEsperado.trabalhadoMinutosResto}min parcial | Saldo: ${sinalSaldo}${saldoAcumuladoHoras}h ${saldoAcumuladoMinutosResto}min`,
										rowSpan: saldoEsperado.tamanhoPar
									});
								} else if (indexReal >= 0) {
									const saldoPar = saldosComparativosPorPar.get(indexReal);
									if (saldoPar) {
										// Decrementar saldo acumulado apenas uma vez por par
										const chavePar = `${reg.tipo}-${saldoPar.parIndex}`;
										if (!paresProcessadosParaSaldo.has(chavePar)) {
											saldoDiarioAcumuladoMinutos -= saldoPar.trabalhadoMinutos;
											paresProcessadosParaSaldo.add(chavePar);
										}

										// Calcular saldo acumulado formatado
										// Se saldoDiarioAcumuladoMinutos > 0: ainda falta trabalhar (mostrar como negativo)
										// Se saldoDiarioAcumuladoMinutos < 0: trabalhou mais que o esperado (mostrar como positivo)
										const saldoAcumuladoHoras = Math.floor(
											Math.abs(saldoDiarioAcumuladoMinutos) / 60
										);
										const saldoAcumuladoMinutosResto = Math.abs(saldoDiarioAcumuladoMinutos) % 60;
										// Inverter sinal: positivo quando trabalhou mais, negativo quando ainda falta
										const sinalSaldo = saldoDiarioAcumuladoMinutos < 0 ? '+' : '-';
										const trabalhouMaisQueEsperado = saldoDiarioAcumuladoMinutos < 0;

										// Marcar linha para aplicar cor no saldo
										if (trabalhouMaisQueEsperado) {
											linha._saldoPositivo = true; // Verde: trabalhou mais que o esperado
										} else {
											linha._saldoNegativo = true; // Vermelho: ainda falta trabalhar
										}

										linha.push({
											content: `${saldoPar.trabalhadoHoras}h ${saldoPar.trabalhadoMinutosResto}min parcial | Saldo: ${sinalSaldo}${saldoAcumuladoHoras}h ${saldoAcumuladoMinutosResto}min`,
											rowSpan: saldoPar.tamanhoPar
										});
									} else {
										linha.push('-');
									}
								} else {
									linha.push('-');
								}
							} else if (reg.real) {
								// Saída real: não adicionar (já coberto pelo rowspan da entrada)
								// Mas verificar se precisa adicionar '-' se não houver par completo
								const tipoEntradaEsperado =
									reg.tipo === 'saida_almoco' ? 'entrada' : 'retorno_almoco';
								const entradaReal = regsReais.find((r) => r.tipo === tipoEntradaEsperado);
								if (!entradaReal) {
									linha.push('-');
								}
							} else {
								// Registro não marcado: verificar se faz parte de um par incompleto ou dia sem registros
								const isInicioPar = reg.tipo === 'entrada' || reg.tipo === 'retorno_almoco';

								if (isInicioPar) {
									// Verificar se há saldo esperado para este par
									const saldoEsperado = saldosEsperadosPorPar.get(i);
									if (saldoEsperado) {
										// Par incompleto ou completamente não marcado: decrementar saldo acumulado

										// Decrementar saldo acumulado apenas uma vez por par
										const chavePar = `${reg.tipo}-esperado-${i}`;
										if (!paresProcessadosParaSaldo.has(chavePar)) {
											// Se não há tempo trabalhado, decrementar o tempo esperado completo
											if (saldoEsperado.trabalhadoMinutos === 0) {
												saldoDiarioAcumuladoMinutos -= saldoEsperado.esperadoMinutos;
											} else {
												saldoDiarioAcumuladoMinutos -= saldoEsperado.trabalhadoMinutos;
											}
											paresProcessadosParaSaldo.add(chavePar);
										}

										// Calcular saldo acumulado formatado
										// Se saldoDiarioAcumuladoMinutos > 0: ainda falta trabalhar (mostrar como negativo)
										// Se saldoDiarioAcumuladoMinutos < 0: trabalhou mais que o esperado (mostrar como positivo)
										const saldoAcumuladoHoras = Math.floor(
											Math.abs(saldoDiarioAcumuladoMinutos) / 60
										);
										const saldoAcumuladoMinutosResto = Math.abs(saldoDiarioAcumuladoMinutos) % 60;
										// Inverter sinal: positivo quando trabalhou mais, negativo quando ainda falta
										const sinalSaldo = saldoDiarioAcumuladoMinutos < 0 ? '+' : '-';
										const trabalhouMaisQueEsperado = saldoDiarioAcumuladoMinutos < 0;

										// Marcar linha para aplicar cor no saldo
										if (trabalhouMaisQueEsperado) {
											linha._saldoPositivo = true; // Verde: trabalhou mais que o esperado
										} else {
											linha._saldoNegativo = true; // Vermelho: ainda falta trabalhar
										}

										// Se par completamente não marcado (trabalhado = 0), mostrar apenas diferença negativa
										if (saldoEsperado.trabalhadoMinutos === 0) {
											linha.push({
												content: `0h 0min parcial | Saldo: ${sinalSaldo}${saldoAcumuladoHoras}h ${saldoAcumuladoMinutosResto}min`,
												rowSpan: saldoEsperado.tamanhoPar
											});
										} else {
											linha.push({
												content: `${saldoEsperado.trabalhadoHoras}h ${saldoEsperado.trabalhadoMinutosResto}min parcial | Saldo: ${sinalSaldo}${saldoAcumuladoHoras}h ${saldoAcumuladoMinutosResto}min`,
												rowSpan: saldoEsperado.tamanhoPar
											});
										}
									} else if (regsReais.length === 0) {
										// Dia sem registros: calcular saldo esperado completo e decrementar saldo acumulado
										if (reg.tipo === 'entrada') {
											// Par 1 completo esperado
											const minutosEntrada = horaEntrada * 60 + minutoEntrada;
											const minutosSaida = horaSaidaAlmoco * 60 + minutoSaidaAlmoco;
											let saldoMinutos = minutosSaida - minutosEntrada;
											if (saldoMinutos < 0) saldoMinutos += 24 * 60;
											const horas = Math.floor(saldoMinutos / 60);
											const minutos = saldoMinutos % 60;

											// Decrementar saldo acumulado apenas uma vez por par
											const chavePar = `entrada-sem-registros-${i}`;
											if (!paresProcessadosParaSaldo.has(chavePar)) {
												saldoDiarioAcumuladoMinutos -= saldoMinutos;
												paresProcessadosParaSaldo.add(chavePar);
											}

											// Calcular saldo acumulado formatado
											// Se saldoDiarioAcumuladoMinutos > 0: ainda falta trabalhar (mostrar como negativo)
											// Se saldoDiarioAcumuladoMinutos < 0: trabalhou mais que o esperado (mostrar como positivo)
											const saldoAcumuladoHoras = Math.floor(
												Math.abs(saldoDiarioAcumuladoMinutos) / 60
											);
											const saldoAcumuladoMinutosResto = Math.abs(saldoDiarioAcumuladoMinutos) % 60;
											// Inverter sinal: positivo quando trabalhou mais, negativo quando ainda falta
											const sinalSaldo = saldoDiarioAcumuladoMinutos < 0 ? '+' : '-';
											const trabalhouMaisQueEsperado = saldoDiarioAcumuladoMinutos < 0;

											// Marcar linha para aplicar cor no saldo
											if (trabalhouMaisQueEsperado) {
												linha._saldoPositivo = true; // Verde: trabalhou mais que o esperado
											} else {
												linha._saldoNegativo = true; // Vermelho: ainda falta trabalhar
											}

											// Para dia sem registros, mostrar 0h trabalhado
											linha.push({
												content: `0h 0min parcial | Saldo: ${sinalSaldo}${saldoAcumuladoHoras}h ${saldoAcumuladoMinutosResto}min`,
												rowSpan: 2 // entrada + saida_almoco
											});
										} else if (reg.tipo === 'retorno_almoco') {
											// Par 2 completo esperado
											const minutosEntrada = horaRetornoAlmoco * 60 + minutoRetornoAlmoco;
											const minutosSaida = horaSaida * 60 + minutoSaida;
											let saldoMinutos = minutosSaida - minutosEntrada;
											if (saldoMinutos < 0) saldoMinutos += 24 * 60;
											const horas = Math.floor(saldoMinutos / 60);
											const minutos = saldoMinutos % 60;

											// Decrementar saldo acumulado apenas uma vez por par
											const chavePar = `retorno_almoco-sem-registros-${i}`;
											if (!paresProcessadosParaSaldo.has(chavePar)) {
												saldoDiarioAcumuladoMinutos -= saldoMinutos;
												paresProcessadosParaSaldo.add(chavePar);
											}

											// Calcular saldo acumulado formatado
											// Se saldoDiarioAcumuladoMinutos > 0: ainda falta trabalhar (mostrar como negativo)
											// Se saldoDiarioAcumuladoMinutos < 0: trabalhou mais que o esperado (mostrar como positivo)
											const saldoAcumuladoHoras = Math.floor(
												Math.abs(saldoDiarioAcumuladoMinutos) / 60
											);
											const saldoAcumuladoMinutosResto = Math.abs(saldoDiarioAcumuladoMinutos) % 60;
											// Inverter sinal: positivo quando trabalhou mais, negativo quando ainda falta
											const sinalSaldo = saldoDiarioAcumuladoMinutos < 0 ? '+' : '-';
											const trabalhouMaisQueEsperado = saldoDiarioAcumuladoMinutos < 0;

											// Marcar linha para aplicar cor no saldo
											if (trabalhouMaisQueEsperado) {
												linha._saldoPositivo = true; // Verde: trabalhou mais que o esperado
											} else {
												linha._saldoNegativo = true; // Vermelho: ainda falta trabalhar
											}

											// Para dia sem registros, mostrar 0h trabalhado
											linha.push({
												content: `0h 0min parcial | Saldo: ${sinalSaldo}${saldoAcumuladoHoras}h ${saldoAcumuladoMinutosResto}min`,
												rowSpan: 2 // retorno_almoco + saida
											});
										} else {
											linha.push('-');
										}
									} else {
										// Há registros reais mas este par não foi marcado completamente
										// Verificar se é um par completamente não marcado
										const tipoSaidaEsperado = reg.tipo === 'entrada' ? 'saida_almoco' : 'saida';

										// Verificar se a saída esperada existe usando a mesma lógica melhorada
										const saidaEsperadaExiste = todosRegistros.some((r, idx) => {
											if (idx <= i) return false;
											if (r.tipo !== tipoSaidaEsperado || r.real) return false;

											// Verificar se há registro real no intervalo
											const minutosEntrada = reg.hora * 60 + reg.minuto;
											const minutosSaidaEsperada = r.hora * 60 + r.minuto;

											const temRegistroRealNoIntervalo = regsReais.some((real) => {
												if (real.tipo !== tipoSaidaEsperado) return false;
												const minutosReal = real.hora * 60 + real.minuto;
												return minutosReal >= minutosEntrada && minutosReal < minutosSaidaEsperada;
											});

											return !temRegistroRealNoIntervalo;
										});

										if (saidaEsperadaExiste) {
											// Par completamente não marcado: calcular saldo negativo e decrementar saldo acumulado
											const saldoEsperadoCompleto = saldosEsperadosPorPar.get(i);
											if (saldoEsperadoCompleto) {
												// Decrementar saldo acumulado apenas uma vez por par
												const chavePar = `${reg.tipo}-nao-marcado-${i}`;
												if (!paresProcessadosParaSaldo.has(chavePar)) {
													saldoDiarioAcumuladoMinutos -= saldoEsperadoCompleto.esperadoMinutos;
													paresProcessadosParaSaldo.add(chavePar);
												}

												// Calcular saldo acumulado formatado
												// Se saldoDiarioAcumuladoMinutos > 0: ainda falta trabalhar (mostrar como negativo)
												// Se saldoDiarioAcumuladoMinutos < 0: trabalhou mais que o esperado (mostrar como positivo)
												const saldoAcumuladoHoras = Math.floor(
													Math.abs(saldoDiarioAcumuladoMinutos) / 60
												);
												const saldoAcumuladoMinutosResto =
													Math.abs(saldoDiarioAcumuladoMinutos) % 60;
												// Inverter sinal: positivo quando trabalhou mais, negativo quando ainda falta
												const sinalSaldo = saldoDiarioAcumuladoMinutos < 0 ? '+' : '-';
												const trabalhouMaisQueEsperado = saldoDiarioAcumuladoMinutos < 0;

												// Marcar linha para aplicar cor no saldo
												if (trabalhouMaisQueEsperado) {
													linha._saldoPositivo = true; // Verde: trabalhou mais que o esperado
												} else {
													linha._saldoNegativo = true; // Vermelho: ainda falta trabalhar
												}

												linha.push({
													content: `0h 0min parcial | Saldo: ${sinalSaldo}${saldoAcumuladoHoras}h ${saldoAcumuladoMinutosResto}min`,
													rowSpan: saldoEsperadoCompleto.tamanhoPar
												});
											} else {
												linha.push('-');
											}
										} else {
											linha.push('-');
										}
									}
								} else {
									// Saída não marcada: verificar se faz parte de um par completamente não marcado
									// Se a entrada correspondente também não foi marcada, o saldo já foi adicionado na linha da entrada
									// Então apenas não adicionar nada aqui (será coberto pelo rowspan)
									const tipoEntradaEsperado =
										reg.tipo === 'saida_almoco' ? 'entrada' : 'retorno_almoco';
									const entradaEsperadaExiste = todosRegistros.some(
										(r, idx) => idx < i && r.tipo === tipoEntradaEsperado && !r.real
									);

									if (!entradaEsperadaExiste) {
										// Saída sem entrada correspondente esperada: não tem saldo
										linha.push('-');
									}
									// Se entrada esperada existe, o saldo já foi adicionado com rowspan na linha da entrada
								}
							}
						}

						linha.push(reg.real ? (reg.dentroDoPrazo ? 'Sim' : 'Não') : 'Não marcado');

						tableData.push(linha);
					}
				}

				const headers = ['Data', 'Tipo', 'Horário'];
				if (sections.saldoDiario) {
					headers.push('Saldo Diário');
				}
				headers.push('Dentro do Prazo');

				// Salvar a posição Y antes da tabela
				const yPosAntesTabela = yPosition;

				autoTable(doc, {
					startY: yPosition,
					head: [headers],
					body: tableData,
					theme: 'grid',
					headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
					styles: { fontSize: 9 },
					didParseCell: function (data) {
						// Aplicar cor vermelha para registros não marcados
						if (data.row.raw && (data.row.raw as any)._naoMarcado) {
							// Aplicar cor vermelha nas colunas Tipo e Horário
							if (data.column.index === 1 || data.column.index === 2) {
								data.cell.styles.textColor = [200, 0, 0];
							}
						}
						// Aplicar cor vermelha na coluna de saldo diário quando marcado
						// Aplicar cor baseada no saldo acumulado
						if (data.row.raw) {
							const rowData = data.row.raw as any;
							const indiceSaldoDiario = sections.saldoDiario ? 3 : -1;

							if (data.column.index === indiceSaldoDiario) {
								if (rowData._saldoNegativo) {
									// Saldo negativo: cor vermelha
									data.cell.styles.textColor = [200, 0, 0];
								} else if (rowData._saldoPositivo) {
									// Saldo positivo: cor verde
									data.cell.styles.textColor = [0, 128, 0];
								} else if (rowData._saldoVermelho) {
									// Fallback para compatibilidade: cor vermelha
									data.cell.styles.textColor = [200, 0, 0];
								}
							}
						}
					}
				});

				// Calcular posição Y após a tabela
				const lastPage = doc.getNumberOfPages();
				doc.setPage(lastPage);
				const finalY = (doc as { lastAutoTable?: { finalY: number } }).lastAutoTable?.finalY;

				if (finalY) {
					yPosition = finalY;
				} else {
					const linhasTabela = tableData.length + 1;
					yPosition = yPosAntesTabela + linhasTabela * 7 + 10;
				}
				yPosition += 10;
			}

			// Banco de Horas
			if (sections.bancoHoras) {
				if (yPosition > doc.internal.pageSize.getHeight() - 60) {
					doc.addPage();
					yPosition = 20;
				}

				const bancoHoras = await client.query(api.pontos.obterBancoHorasFuncionario, {
					funcionarioId
				});

				// Calcular total de dias do período selecionado
				const diasPeriodo = gerarDiasPeriodo(dataInicio, dataFim);
				const totalDiasPeriodo = diasPeriodo.length;

				// Calcular carga horária diária esperada baseada na configuração
				const [horaEntradaConfig, minutoEntradaConfig] = config.horarioEntrada
					.split(':')
					.map(Number);
				const [horaSaidaAlmocoConfig, minutoSaidaAlmocoConfig] = config.horarioSaidaAlmoco
					.split(':')
					.map(Number);
				const [horaRetornoAlmocoConfig, minutoRetornoAlmocoConfig] = config.horarioRetornoAlmoco
					.split(':')
					.map(Number);
				const [horaSaidaConfig, minutoSaidaConfig] = config.horarioSaida.split(':').map(Number);

				// Par 1: entrada -> saida_almoco
				const minutosPar1EsperadoConfig =
					horaSaidaAlmocoConfig * 60 +
					minutoSaidaAlmocoConfig -
					(horaEntradaConfig * 60 + minutoEntradaConfig);
				const minutosPar1EsperadoAjustadoConfig =
					minutosPar1EsperadoConfig < 0
						? minutosPar1EsperadoConfig + 24 * 60
						: minutosPar1EsperadoConfig;

				// Par 2: retorno_almoco -> saida
				const minutosPar2EsperadoConfig =
					horaSaidaConfig * 60 +
					minutoSaidaConfig -
					(horaRetornoAlmocoConfig * 60 + minutoRetornoAlmocoConfig);
				const minutosPar2EsperadoAjustadoConfig =
					minutosPar2EsperadoConfig < 0
						? minutosPar2EsperadoConfig + 24 * 60
						: minutosPar2EsperadoConfig;

				const cargaHorariaDiariaEsperadaMinutos =
					minutosPar1EsperadoAjustadoConfig + minutosPar2EsperadoAjustadoConfig;

				// Calcular saldos do período selecionado baseado nos saldos diários calculados
				let saldoPeriodoTrabalhadoMinutos = 0;
				let diasComSaldoPositivo = 0;
				let diasComSaldoNegativo = 0;
				let diasSemRegistros = 0;

				if (sections.registrosPonto && Object.keys(saldosDiariosPorData).length > 0) {
					// Somar todos os saldos diários do período
					for (const saldo of Object.values(saldosDiariosPorData)) {
						saldoPeriodoTrabalhadoMinutos += saldo.trabalhadoMinutos;

						// Calcular diferença diária corretamente: trabalhado - esperado
						const diferencaDiaria = saldo.trabalhadoMinutos - saldo.esperadoMinutos;

						if (diferencaDiaria > 0) {
							diasComSaldoPositivo++;
						} else if (diferencaDiaria < 0) {
							diasComSaldoNegativo++;
						}

						if (saldo.trabalhadoMinutos === 0 && saldo.esperadoMinutos > 0) {
							diasSemRegistros++;
						}
					}
				} else {
					// Fallback: calcular a partir dos registros se não tiver saldos diários
					const registrosPorDataPeriodo: Record<
						string,
						Array<{ tipo: string; hora: number; minuto: number }>
					> = {};

					for (const r of registrosFuncionario) {
						const dataKey = r.data;
						if (!registrosPorDataPeriodo[dataKey]) {
							registrosPorDataPeriodo[dataKey] = [];
						}
						registrosPorDataPeriodo[dataKey]!.push({
							tipo: r.tipo,
							hora: r.hora,
							minuto: r.minuto
						});
					}

					for (const regs of Object.values(registrosPorDataPeriodo)) {
						const saldoDiario = calcularSaldoDiario(regs);
						if (saldoDiario) {
							saldoPeriodoTrabalhadoMinutos += saldoDiario.saldoMinutos;
						}
					}
				}

				// Calcular saldo esperado do período: carga horária diária × número de dias
				// SEMPRE calcular diretamente, não somar saldos diários esperados (pode duplicar)
				const saldoPeriodoEsperadoMinutos = cargaHorariaDiariaEsperadaMinutos * totalDiasPeriodo;

				// Calcular diferença do período corretamente: trabalhado - esperado (para "Saldo do Período Exibido")
				const saldoPeriodoDiferencaMinutos =
					saldoPeriodoTrabalhadoMinutos - saldoPeriodoEsperadoMinutos;

				// Calcular diferença do período (trabalhado - esperado) para exibição na linha "Diferença do Período"
				// Negativo quando trabalhado < esperado (vermelho), positivo quando trabalhado > esperado (verde)
				const diferencaPeriodoTrabalhadoMenosEsperado =
					saldoPeriodoTrabalhadoMinutos - saldoPeriodoEsperadoMinutos;

				// Calcular médias diárias
				const mediaDiariaTrabalhadaHoras =
					totalDiasPeriodo > 0
						? Math.floor(saldoPeriodoTrabalhadoMinutos / 60 / totalDiasPeriodo)
						: 0;
				const mediaDiariaTrabalhadaMinutos =
					totalDiasPeriodo > 0
						? Math.floor((saldoPeriodoTrabalhadoMinutos / totalDiasPeriodo) % 60)
						: 0;

				// Calcular média esperada baseada na configuração padrão (não no período)
				const [horaEntrada, minutoEntrada] = config.horarioEntrada.split(':').map(Number);
				const [horaSaidaAlmoco, minutoSaidaAlmoco] = config.horarioSaidaAlmoco
					.split(':')
					.map(Number);
				const [horaRetornoAlmoco, minutoRetornoAlmoco] = config.horarioRetornoAlmoco
					.split(':')
					.map(Number);
				const [horaSaida, minutoSaida] = config.horarioSaida.split(':').map(Number);

				// Par 1: entrada -> saida_almoco
				const minutosPar1Esperado =
					horaSaidaAlmoco * 60 + minutoSaidaAlmoco - (horaEntrada * 60 + minutoEntrada);
				const minutosPar1EsperadoAjustado =
					minutosPar1Esperado < 0 ? minutosPar1Esperado + 24 * 60 : minutosPar1Esperado;

				// Par 2: retorno_almoco -> saida
				const minutosPar2Esperado =
					horaSaida * 60 + minutoSaida - (horaRetornoAlmoco * 60 + minutoRetornoAlmoco);
				const minutosPar2EsperadoAjustado =
					minutosPar2Esperado < 0 ? minutosPar2Esperado + 24 * 60 : minutosPar2Esperado;

				const totalEsperadoDiarioMinutos =
					minutosPar1EsperadoAjustado + minutosPar2EsperadoAjustado;
				const mediaDiariaEsperadaHoras = Math.floor(totalEsperadoDiarioMinutos / 60);
				const mediaDiariaEsperadaMinutos = totalEsperadoDiarioMinutos % 60;

				// Formatar valores
				// Saldo do Período Exibido: diferença (trabalhado - esperado)
				const horasPeriodoDiferenca = Math.floor(Math.abs(saldoPeriodoDiferencaMinutos) / 60);
				const minutosPeriodoDiferenca = Math.abs(saldoPeriodoDiferencaMinutos) % 60;
				const sinalPeriodoDiferenca = saldoPeriodoDiferencaMinutos >= 0 ? '+' : '-';
				const saldoPeriodoDiferencaFormatado = `${sinalPeriodoDiferenca}${horasPeriodoDiferenca}h ${minutosPeriodoDiferenca}min`;

				// Diferença do Período: trabalhado - esperado
				// Negativo quando trabalhado < esperado (vermelho), positivo quando trabalhado > esperado (verde)
				const horasDiferencaPeriodo = Math.floor(
					Math.abs(diferencaPeriodoTrabalhadoMenosEsperado) / 60
				);
				const minutosDiferencaPeriodo = Math.abs(diferencaPeriodoTrabalhadoMenosEsperado) % 60;
				const sinalDiferencaPeriodo = diferencaPeriodoTrabalhadoMenosEsperado >= 0 ? '+' : '-';
				const diferencaPeriodoFormatado = `${sinalDiferencaPeriodo}${horasDiferencaPeriodo}h ${minutosDiferencaPeriodo}min`;

				const horasPeriodoTrabalhado = Math.floor(saldoPeriodoTrabalhadoMinutos / 60);
				const minutosPeriodoTrabalhado = saldoPeriodoTrabalhadoMinutos % 60;
				const saldoPeriodoTrabalhadoFormatado = `+${horasPeriodoTrabalhado}h ${minutosPeriodoTrabalhado}min`;

				const horasPeriodoEsperado = Math.floor(saldoPeriodoEsperadoMinutos / 60);
				const minutosPeriodoEsperado = saldoPeriodoEsperadoMinutos % 60;
				const saldoPeriodoEsperadoFormatado = `+${horasPeriodoEsperado}h ${minutosPeriodoEsperado}min`;

				doc.setFontSize(12);
				doc.setFont('helvetica', 'bold');
				doc.setTextColor(41, 128, 185);
				doc.text('RESUMO DO BANCO DE HORAS', 15, yPosition);
				doc.setFont('helvetica', 'normal');
				doc.setTextColor(0, 0, 0);
				yPosition += 10;

				if (bancoHoras) {
					const saldoMinutos = bancoHoras.saldoAcumuladoMinutos;
					const horas = Math.floor(Math.abs(saldoMinutos) / 60);
					const minutos = Math.abs(saldoMinutos) % 60;
					const sinal = saldoMinutos >= 0 ? '+' : '-';
					const saldoFormatado = `${sinal}${horas}h ${minutos}min`;

					// Calcular saldo acumulado de períodos anteriores
					// Saldo Anterior = Saldo Total - Saldo do Período Atual
					const saldoAnteriorMinutos = saldoMinutos - saldoPeriodoDiferencaMinutos;
					const horasAnterior = Math.floor(Math.abs(saldoAnteriorMinutos) / 60);
					const minutosAnterior = Math.abs(saldoAnteriorMinutos) % 60;
					const sinalAnterior = saldoAnteriorMinutos >= 0 ? '+' : '-';
					const saldoAnteriorFormatado = `${sinalAnterior}${horasAnterior}h ${minutosAnterior}min`;

					// Calcular resultado final
					const resultadoFinalMinutos = saldoAnteriorMinutos + saldoPeriodoDiferencaMinutos;
					const horasResultadoFinal = Math.floor(Math.abs(resultadoFinalMinutos) / 60);
					const minutosResultadoFinal = Math.abs(resultadoFinalMinutos) % 60;
					const sinalResultadoFinal = resultadoFinalMinutos >= 0 ? '+' : '-';
					const resultadoFinalFormatado = `${sinalResultadoFinal}${horasResultadoFinal}h ${minutosResultadoFinal}min`;

					// Preparar dados da tabela com melhorias
					const bancoHorasData: Array<[string, string]> = [
						['Saldo Atual', saldoFormatado],
						['Saldo Banco Acumulado de Períodos Anteriores', saldoAnteriorFormatado],
						['Saldo do Período Exibido', saldoPeriodoDiferencaFormatado],
						['Resultado Final', resultadoFinalFormatado]
					];

					// Adicionar detalhamento
					bancoHorasData.push(['', '']); // Linha separadora
					bancoHorasData.push(['Saldo Trabalhado do Período', saldoPeriodoTrabalhadoFormatado]);
					bancoHorasData.push(['Saldo Esperado do Período', saldoPeriodoEsperadoFormatado]);
					bancoHorasData.push(['Diferença do Período', diferencaPeriodoFormatado]);

					// Adicionar estatísticas
					bancoHorasData.push(['', '']); // Linha separadora
					bancoHorasData.push([
						'Média Diária de Horas Trabalhadas',
						`+${mediaDiariaTrabalhadaHoras}h ${mediaDiariaTrabalhadaMinutos}min`
					]);
					bancoHorasData.push([
						'Média Diária Esperada',
						`+${mediaDiariaEsperadaHoras}h ${mediaDiariaEsperadaMinutos}min`
					]);

					// Adicionar contagens
					bancoHorasData.push(['', '']); // Linha separadora
					bancoHorasData.push(['Dias com Saldo Positivo', `${diasComSaldoPositivo} dias`]);
					bancoHorasData.push(['Dias com Saldo Negativo', `${diasComSaldoNegativo} dias`]);
					bancoHorasData.push(['Dias sem Registros', `${diasSemRegistros} dias`]);
					bancoHorasData.push(['Total de Dias do Período', `${totalDiasPeriodo} dias`]);

					// Criar tabela no mesmo estilo das outras seções
					autoTable(doc, {
						startY: yPosition,
						head: [['Campo', 'Valor']],
						body: bancoHorasData,
						theme: 'grid',
						headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
						styles: { fontSize: 9 },
						columnStyles: {
							0: { fontStyle: 'bold', cellWidth: 80 },
							1: { cellWidth: 'auto' }
						},
						didParseCell: function (data) {
							// Ignorar linhas separadoras vazias
							if (data.cell.text[0] === '' && data.column.index === 0) {
								data.cell.styles.fillColor = [240, 240, 240]; // Cor de fundo cinza claro
								return;
							}

							const campo = data.cell.text[0];
							const valor = data.cell.text[1] || '';

							// Aplicar cores baseado no valor
							if (data.column.index === 1 && valor) {
								// Aplicar cor no Saldo Atual (vermelho para negativo, azul para positivo)
								if (campo === 'Saldo Atual') {
									if (saldoMinutos < 0) {
										data.cell.styles.textColor = [200, 0, 0]; // Vermelho para negativo
									} else if (saldoMinutos > 0) {
										data.cell.styles.textColor = [0, 100, 200]; // Azul para positivo
									}
									data.cell.styles.fontStyle = 'bold';
								}

								// Verificar se o valor contém sinal negativo
								if (valor.includes('-') && !valor.includes('±')) {
									data.cell.styles.textColor = [200, 0, 0]; // Vermelho para negativo
									if (
										campo === 'Saldo do Período Exibido' ||
										campo === 'Diferença do Período' ||
										campo === 'Resultado Final'
									) {
										data.cell.styles.fontStyle = 'bold';
									}
								} else if (
									valor.includes('+') ||
									campo.includes('Trabalhado') ||
									campo.includes('Esperado')
								) {
									// Verde para valores positivos ou campos de trabalhado/esperado
									if (campo.includes('Diferença do Período')) {
										// Diferença do Período: trabalhado - esperado
										// Negativo quando trabalhado < esperado (vermelho), positivo quando trabalhado > esperado (verde)
										if (diferencaPeriodoTrabalhadoMenosEsperado < 0) {
											data.cell.styles.textColor = [200, 0, 0]; // Vermelho quando trabalhado < esperado
										} else if (diferencaPeriodoTrabalhadoMenosEsperado > 0) {
											data.cell.styles.textColor = [0, 128, 0]; // Verde quando trabalhado > esperado
										}
										data.cell.styles.fontStyle = 'bold';
									} else if (campo.includes('Trabalhado') || campo.includes('Esperado')) {
										data.cell.styles.textColor = [0, 128, 0]; // Verde para trabalhado/esperado
									}
								}

								// Destacar campos específicos
								if (campo === 'Dias com Saldo Negativo') {
									data.cell.styles.textColor = [200, 0, 0];
									data.cell.styles.fontStyle = 'bold';
								}
								if (campo === 'Dias com Saldo Positivo') {
									data.cell.styles.textColor = [0, 128, 0];
									data.cell.styles.fontStyle = 'bold';
								}
								if (campo === 'Resultado Final') {
									if (resultadoFinalMinutos < 0) {
										data.cell.styles.textColor = [200, 0, 0];
									} else if (resultadoFinalMinutos > 0) {
										data.cell.styles.textColor = [0, 128, 0];
									}
									data.cell.styles.fontStyle = 'bold';
								}
								if (campo === 'Saldo do Período Exibido') {
									if (saldoPeriodoDiferencaMinutos < 0) {
										data.cell.styles.textColor = [200, 0, 0];
									} else if (saldoPeriodoDiferencaMinutos > 0) {
										data.cell.styles.textColor = [0, 128, 0];
									}
									data.cell.styles.fontStyle = 'bold';
								}
							}
						}
					});

					// Calcular posição Y após a tabela
					const lastPage = doc.getNumberOfPages();
					doc.setPage(lastPage);
					const finalY = (doc as { lastAutoTable?: { finalY: number } }).lastAutoTable?.finalY;
					if (finalY) {
						yPosition = finalY + 10;
					} else {
						yPosition += bancoHorasData.length * 7 + 10;
					}
				} else {
					// Se não houver banco de horas, criar tabela vazia com mensagem
					autoTable(doc, {
						startY: yPosition,
						head: [['Campo', 'Valor']],
						body: [['Banco de horas', 'Não disponível']],
						theme: 'grid',
						headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
						styles: { fontSize: 9 }
					});

					const lastPage = doc.getNumberOfPages();
					doc.setPage(lastPage);
					const finalY = (doc as { lastAutoTable?: { finalY: number } }).lastAutoTable?.finalY;
					if (finalY) {
						yPosition = finalY + 10;
					} else {
						yPosition += 20;
					}
				}
			}

			// Alterações pelo Gestor
			if (sections.alteracoesGestor && homologacoes.length > 0) {
				if (yPosition > doc.internal.pageSize.getHeight() - 60) {
					doc.addPage();
					yPosition = 20;
				}

				doc.setFontSize(12);
				doc.setFont('helvetica', 'bold');
				doc.setTextColor(41, 128, 185);
				doc.text('ALTERAÇÕES PELO GESTOR', 15, yPosition);
				doc.setFont('helvetica', 'normal');
				doc.setTextColor(0, 0, 0);
				yPosition += 10;

				const homologacoesData = homologacoes
					.map((h) => {
						// Formatar data de criação usando função centralizada (DD/MM/AAAA)
						const dataFormatada = formatarDataDDMMAAAA(h.criadoEm);

						if (h.registroId && h.horaAnterior !== undefined) {
							return [
								dataFormatada,
								'Edição de Registro',
								h.horaAnterior !== undefined
									? `${formatarHoraPonto(h.horaAnterior, h.minutoAnterior || 0)} → ${formatarHoraPonto(h.horaNova || 0, h.minutoNova || 0)}`
									: '-',
								h.motivoDescricao || h.motivoTipo || '-',
								h.observacoes || '-'
							];
						} else if (h.tipoAjuste) {
							const tipoAjusteLabel =
								h.tipoAjuste === 'compensar'
									? 'Compensar'
									: h.tipoAjuste === 'abonar'
										? 'Abonar'
										: 'Descontar';
							return [
								dataFormatada,
								`Ajuste: ${tipoAjusteLabel}`,
								`${h.periodoDias || 0}d ${h.periodoHoras || 0}h ${h.periodoMinutos || 0}min`,
								h.motivoDescricao || h.motivoTipo || '-',
								h.observacoes || '-'
							];
						}
						return [];
					})
					.filter((row) => row.length > 0);

				if (homologacoesData.length > 0) {
					autoTable(doc, {
						startY: yPosition,
						head: [['Data', 'Tipo', 'Detalhes', 'Motivo', 'Observações']],
						body: homologacoesData,
						theme: 'grid',
						headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
						styles: { fontSize: 9 },
						columnStyles: {
							0: { cellWidth: 30 }, // Data
							1: { cellWidth: 40 }, // Tipo
							2: { cellWidth: 50, cellPadding: { top: 2, bottom: 2, left: 2, right: 2 } }, // Detalhes - maior largura
							3: { cellWidth: 40 }, // Motivo
							4: { cellWidth: 'auto' } // Observações
						},
						didParseCell: function (data) {
							// Permitir quebra de linha na coluna Detalhes (índice 2)
							if (data.column.index === 2) {
								data.cell.styles.overflow = 'linebreak';
								data.cell.styles.cellWidth = 50;
							}
						}
					});

					const lastPage = doc.getNumberOfPages();
					doc.setPage(lastPage);
					const finalY = (doc as { lastAutoTable?: { finalY: number } }).lastAutoTable?.finalY;
					if (finalY) {
						yPosition = finalY + 10;
					} else {
						yPosition += homologacoesData.length * 7 + 10;
					}
				}
			}

			// ============================================
			// SEÇÃO: DETALHAMENTO DE INCONSISTÊNCIAS
			// ============================================
			const todasInconsistencias = dias.flatMap((dia) =>
				dia.inconsistencias.map((inc) => ({
					...inc,
					data: dia.data,
					dataFormatada: dia.dataFormatada
				}))
			);

			if (todasInconsistencias.length > 0) {
				if (yPosition > doc.internal.pageSize.getHeight() - 60) {
					doc.addPage();
					yPosition = 20;
				}

				doc.setFontSize(12);
				doc.setFont('helvetica', 'bold');
				doc.setTextColor(41, 128, 185);
				doc.text('DETALHAMENTO DE INCONSISTÊNCIAS', 15, yPosition);
				doc.setFont('helvetica', 'normal');
				doc.setTextColor(0, 0, 0);
				yPosition += 10;

				const inconsistenciasData = todasInconsistencias.map((inc) => {
					const statusLabel =
						inc.status === 'resolvida'
							? 'Resolvida'
							: inc.status === 'ignorada'
								? 'Ignorada'
								: 'Pendente';
					const tipoLabel =
						inc.tipo === 'ponto_com_atestado'
							? 'Ponto com Atestado'
							: inc.tipo === 'ponto_com_licenca'
								? 'Ponto com Licença'
								: inc.tipo === 'ponto_com_ausencia'
									? 'Ponto com Ausência'
									: inc.tipo === 'registro_duplicado'
										? 'Registro Duplicado'
										: inc.tipo === 'sequencia_invalida'
											? 'Sequência Inválida'
											: inc.tipo === 'saldo_inconsistente'
												? 'Saldo Inconsistente'
												: inc.tipo;
					return [
						inc.dataFormatada,
						tipoLabel,
						inc.descricao,
						statusLabel,
						inc.resolvidoEm
							? `Resolvido em ${formatarDataDDMMAAAA(inc.resolvidoEm.toString())}`
							: '-'
					];
				});

				autoTable(doc, {
					startY: yPosition,
					head: [['Data', 'Tipo', 'Descrição', 'Status', 'Resolução']],
					body: inconsistenciasData,
					theme: 'grid',
					headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
					styles: { fontSize: 9 },
					columnStyles: {
						0: { cellWidth: 30 },
						1: { cellWidth: 40 },
						2: { cellWidth: 60, overflow: 'linebreak' },
						3: { cellWidth: 30 },
						4: { cellWidth: 30 }
					},
					didParseCell: function (data) {
						// Colorir status
						if (data.column.index === 3) {
							const status = data.cell.text[0] as string;
							if (status === 'Pendente') {
								data.cell.styles.textColor = [255, 140, 0]; // Laranja
								data.cell.styles.fontStyle = 'bold';
							} else if (status === 'Resolvida') {
								data.cell.styles.textColor = [0, 128, 0]; // Verde
							} else if (status === 'Ignorada') {
								data.cell.styles.textColor = [128, 128, 128]; // Cinza
							}
						}
					}
				});

				const lastPageInconsistencias = doc.getNumberOfPages();
				doc.setPage(lastPageInconsistencias);
				const finalYInconsistencias = (doc as { lastAutoTable?: { finalY: number } })
					.lastAutoTable?.finalY;
				if (finalYInconsistencias) {
					yPosition = finalYInconsistencias + 10;
				} else {
					yPosition += inconsistenciasData.length * 7 + 10;
				}
			}

			// ============================================
			// SEÇÃO: AJUSTES DE BANCO DE HORAS
			// ============================================
			const todosAjustes = dias.flatMap((dia) =>
				dia.ajustes.map((ajuste) => ({
					...ajuste,
					data: dia.data,
					dataFormatada: dia.dataFormatada
				}))
			);

			if (todosAjustes.length > 0) {
				if (yPosition > doc.internal.pageSize.getHeight() - 60) {
					doc.addPage();
					yPosition = 20;
				}

				doc.setFontSize(12);
				doc.setFont('helvetica', 'bold');
				doc.setTextColor(41, 128, 185);
				doc.text('AJUSTES DE BANCO DE HORAS', 15, yPosition);
				doc.setFont('helvetica', 'normal');
				doc.setTextColor(0, 0, 0);
				yPosition += 10;

				const ajustesData = todosAjustes.map((ajuste) => {
					const tipoLabel =
						ajuste.tipo === 'abonar'
							? 'Abonar'
							: ajuste.tipo === 'descontar'
								? 'Descontar'
								: 'Compensar';
					const horas = Math.floor(Math.abs(ajuste.valorMinutos) / 60);
					const minutos = Math.abs(ajuste.valorMinutos) % 60;
					const sinal = ajuste.valorMinutos >= 0 ? '+' : '-';
					const valorFormatado = `${sinal}${horas}h ${minutos}min`;
					return [
						ajuste.dataFormatada,
						tipoLabel,
						valorFormatado,
						ajuste.motivoDescricao || '-',
						ajuste.gestorId ? 'Gestor' : 'Automático'
					];
				});

				autoTable(doc, {
					startY: yPosition,
					head: [['Data', 'Tipo', 'Valor', 'Motivo', 'Autor']],
					body: ajustesData,
					theme: 'grid',
					headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
					styles: { fontSize: 9 },
					columnStyles: {
						0: { cellWidth: 30 },
						1: { cellWidth: 30 },
						2: { cellWidth: 30 },
						3: { cellWidth: 50, overflow: 'linebreak' },
						4: { cellWidth: 30 }
					},
					didParseCell: function (data) {
						// Colorir valores
						if (data.column.index === 2) {
							const valor = data.cell.text[0] as string;
							if (valor.startsWith('+')) {
								data.cell.styles.textColor = [0, 128, 0]; // Verde
								data.cell.styles.fontStyle = 'bold';
							} else if (valor.startsWith('-')) {
								data.cell.styles.textColor = [200, 0, 0]; // Vermelho
								data.cell.styles.fontStyle = 'bold';
							}
						}
					}
				});

				const lastPageAjustes = doc.getNumberOfPages();
				doc.setPage(lastPageAjustes);
				const finalYAjustes = (doc as { lastAutoTable?: { finalY: number } }).lastAutoTable?.finalY;
				if (finalYAjustes) {
					yPosition = finalYAjustes + 10;
				} else {
					yPosition += ajustesData.length * 7 + 10;
				}
			}

			// Dispensas de Registro
			if (sections.dispensasRegistro && dispensas.length > 0) {
				if (yPosition > doc.internal.pageSize.getHeight() - 60) {
					doc.addPage();
					yPosition = 20;
				}

				doc.setFontSize(12);
				doc.setFont('helvetica', 'bold');
				doc.setTextColor(41, 128, 185);
				doc.text('DISPENSAS DE REGISTRO', 15, yPosition);
				doc.setFont('helvetica', 'normal');
				doc.setTextColor(0, 0, 0);
				yPosition += 10;

				const dispensasData = dispensas.map((d) => {
					// Formatar data de início e fim usando função centralizada (DD/MM/AAAA)
					const dataInicioFormatada = formatarDataDDMMAAAA(d.dataInicio);
					const dataFimFormatada = formatarDataDDMMAAAA(d.dataFim);

					return [
						`${dataInicioFormatada} ${d.horaInicio.toString().padStart(2, '0')}:${d.minutoInicio.toString().padStart(2, '0')}`,
						`${dataFimFormatada} ${d.horaFim.toString().padStart(2, '0')}:${d.minutoFim.toString().padStart(2, '0')}`,
						d.motivo,
						d.isento ? 'Isento (sem expiração)' : 'Temporária'
					];
				});

				autoTable(doc, {
					startY: yPosition,
					head: [['Início', 'Fim', 'Motivo', 'Tipo']],
					body: dispensasData,
					theme: 'grid',
					headStyles: { fillColor: [41, 128, 185], fontStyle: 'bold' },
					styles: { fontSize: 9 }
				});

				const lastPage = doc.getNumberOfPages();
				doc.setPage(lastPage);
				const finalY = (doc as { lastAutoTable?: { finalY: number } }).lastAutoTable?.finalY;
				if (finalY) {
					yPosition = finalY + 10;
				} else {
					yPosition += dispensasData.length * 7 + 10;
				}
			}

			// Rodapé
			adicionarRodape(doc);

			// Salvar
			const nomeArquivo = `ficha-ponto-${funcionario.matricula || funcionario.nome}-${dataInicio}-${dataFim}.pdf`;
			doc.save(nomeArquivo);

			// Fechar modal após gerar PDF
			mostrarModalImpressao = false;
			funcionarioParaImprimir = '';
			toast.success('PDF gerado com sucesso!');
		} catch (error) {
			console.error('Erro ao gerar PDF:', error);
			const errorMessage = error instanceof Error ? error.message : String(error);
			
			// Mensagens de erro mais específicas
			if (errorMessage.includes('Configuração de ponto não encontrada')) {
				toast.error('Configuração de ponto não encontrada. Entre em contato com o administrador.');
			} else if (errorMessage.includes('Nenhum dado encontrado')) {
				toast.error('Nenhum dado encontrado para este funcionário no período selecionado.');
			} else if (errorMessage.includes('timeout') || errorMessage.includes('Timeout')) {
				toast.error('Tempo de geração excedido. Tente um período menor (máximo 90 dias).');
			} else {
				toast.error(`Erro ao gerar ficha de ponto: ${errorMessage}`);
			}
		} finally {
			carregando = false;
		}
	}
	*/

	function abrirModalDetalhes(registroId: Id<'registrosPonto'>) {
		if (!registroId) {
			console.error('Erro: registroId inválido');
			return;
		}
		registroDetalhesId = registroId;
		mostrarModalDetalhes = true;
	}

	function fecharModalDetalhes() {
		mostrarModalDetalhes = false;
		registroDetalhesId = '';
	}

	// Wrapper para a função importada imprimirDetalhesRegistro
	async function imprimirDetalhesRegistroWrapper(registroId: Id<'registrosPonto'>) {
		await imprimirDetalhesRegistro(
			client,
			registroId,
			logoGovPE,
			(message: string) => toast.error(message)
		);
	}

	// Função antiga comentada (foi movida para módulo)
	/*
	async function imprimirDetalhesRegistroOld(registroId: Id<'registrosPonto'>) {
		try {
			// Buscar dados completos do registro
			const registro = await client.query(api.pontos.obterRegistro, { registroId });

			if (!registro) {
				alert('Registro não encontrado');
				return;
			}

			const doc = new jsPDF();

			// Logo
			let yPosition = 20;
			try {
				const logoImg = new Image();
				logoImg.src = logoGovPE;
				await new Promise<void>((resolve, reject) => {
					logoImg.onload = () => resolve();
					logoImg.onerror = () => reject();
					setTimeout(() => reject(), 3000);
				});

				const logoWidth = 30;
				const aspectRatio = logoImg.height / logoImg.width;
				const logoHeight = logoWidth * aspectRatio;

				doc.addImage(logoImg, 'PNG', 15, 10, logoWidth, logoHeight);
				yPosition = Math.max(25, 10 + logoHeight / 2);
			} catch (err) {
				console.warn('Não foi possível carregar a logo:', err);
			}

			// Cabeçalho com estilo melhorado
			doc.setFontSize(18);
			doc.setTextColor(41, 128, 185);
			doc.setFont('helvetica', 'bold');
			doc.text('DETALHES DO REGISTRO DE PONTO', 105, yPosition, { align: 'center' });

			// Linha decorativa abaixo do título
			doc.setDrawColor(41, 128, 185);
			doc.setLineWidth(0.5);
			doc.line(15, yPosition + 3, 195, yPosition + 3);

			yPosition += 15;

			// Informações do Funcionário em tabela
			const funcionarioData: Array<[string, string]> = [];
			if (registro.funcionario) {
				if (registro.funcionario.matricula) {
					funcionarioData.push(['Matrícula', registro.funcionario.matricula]);
				}
				funcionarioData.push(['Nome', registro.funcionario.nome]);
				if (registro.funcionario.descricaoCargo) {
					funcionarioData.push(['Cargo/Função', registro.funcionario.descricaoCargo]);
				}
			}

			if (funcionarioData.length > 0) {
				doc.setFontSize(12);
				doc.setTextColor(41, 128, 185);
				doc.setFont('helvetica', 'bold');
				doc.text('DADOS DO FUNCIONÁRIO', 15, yPosition);
				yPosition += 8;

				autoTable(doc, {
					startY: yPosition,
					head: [['Campo', 'Valor']],
					body: funcionarioData,
					theme: 'striped',
					headStyles: {
						fillColor: [41, 128, 185],
						textColor: [255, 255, 255],
						fontStyle: 'bold',
						fontSize: 10
					},
					bodyStyles: {
						fontSize: 10,
						textColor: [0, 0, 0]
					},
					columnStyles: {
						0: { cellWidth: 50, fontStyle: 'bold' },
						1: { cellWidth: 140 }
					},
					margin: { left: 15, right: 15 },
					styles: { cellPadding: 4 }
				});

				type JsPDFWithAutoTable = jsPDF & {
					lastAutoTable?: { finalY: number };
				};
				const finalY = (doc as JsPDFWithAutoTable).lastAutoTable?.finalY ?? yPosition + 10;
				yPosition = finalY + 10;
			}

			// Informações do Registro em tabela
			const config = await client.query(api.configuracaoPonto.obterConfiguracao, {});
			const tipoLabel = config
				? getTipoRegistroLabel(registro.tipo, {
						nomeEntrada: config.nomeEntrada,
						nomeSaidaAlmoco: config.nomeSaidaAlmoco,
						nomeRetornoAlmoco: config.nomeRetornoAlmoco,
						nomeSaida: config.nomeSaida
					})
				: getTipoRegistroLabel(registro.tipo);

			const dataHora = `${registro.data} ${registro.hora.toString().padStart(2, '0')}:${registro.minuto.toString().padStart(2, '0')}:${registro.segundo.toString().padStart(2, '0')}`;

			const registroData: Array<[string, string]> = [
				['Tipo', tipoLabel],
				['Data e Hora', dataHora],
				['Status', registro.dentroDoPrazo ? 'Dentro do Prazo' : 'Fora do Prazo'],
				['Tolerância', `${registro.toleranciaMinutos} minutos`],
				['Sincronizado', registro.sincronizadoComServidor ? 'Sim (Servidor)' : 'Não (PC Local)']
			];

			if (registro.justificativa) {
				registroData.push(['Justificativa', registro.justificativa]);
			}

			// Verificar se precisa de nova página
			if (yPosition > 200) {
				doc.addPage();
				yPosition = 20;
			}

			doc.setFontSize(12);
			doc.setTextColor(41, 128, 185);
			doc.setFont('helvetica', 'bold');
			doc.text('DADOS DO REGISTRO', 15, yPosition);
			yPosition += 8;

			autoTable(doc, {
				startY: yPosition,
				head: [['Campo', 'Valor']],
				body: registroData,
				theme: 'striped',
				headStyles: {
					fillColor: [41, 128, 185],
					textColor: [255, 255, 255],
					fontStyle: 'bold',
					fontSize: 10
				},
				bodyStyles: {
					fontSize: 10,
					textColor: [0, 0, 0]
				},
				columnStyles: {
					0: { cellWidth: 50, fontStyle: 'bold' },
					1: { cellWidth: 140 }
				},
				margin: { left: 15, right: 15 },
				styles: { cellPadding: 4 },
				didParseCell: (data: any) => {
					if (data.section === 'body' && data.column.index === 1) {
						// Aplicar cor verde para "Dentro do Prazo" e vermelho para "Fora do Prazo"
						if (data.cell.text[0] === 'Dentro do Prazo') {
							data.cell.styles.textColor = [0, 128, 0];
							data.cell.styles.fontStyle = 'bold';
						} else if (data.cell.text[0] === 'Fora do Prazo') {
							data.cell.styles.textColor = [200, 0, 0];
							data.cell.styles.fontStyle = 'bold';
						}
					}
				}
			});

			type JsPDFWithAutoTable2 = jsPDF & {
				lastAutoTable?: { finalY: number };
			};
			const finalYRegistro = (doc as JsPDFWithAutoTable2).lastAutoTable?.finalY ?? yPosition + 10;
			yPosition = finalYRegistro + 10;

			// Localização em tabela
			if (registro.latitude && registro.longitude) {
				// Verificar se precisa de nova página
				if (yPosition > 200) {
					doc.addPage();
					yPosition = 20;
				}

				const localizacaoData: Array<[string, string]> = [
					['Latitude', registro.latitude.toFixed(6)],
					['Longitude', registro.longitude.toFixed(6)]
				];

				if (registro.precisao) {
					localizacaoData.push(['Precisão', `${registro.precisao.toFixed(2)} metros`]);
				}

				if (registro.endereco) {
					localizacaoData.push(['Endereço', registro.endereco]);
				}

				if (registro.cidade) {
					localizacaoData.push(['Cidade', registro.cidade]);
				}

				if (registro.estado) {
					localizacaoData.push(['Estado', registro.estado]);
				}

				if (registro.pais) {
					localizacaoData.push(['País', registro.pais]);
				}

				if (registro.timezone) {
					localizacaoData.push(['Fuso Horário', registro.timezone]);
				}

				doc.setFontSize(12);
				doc.setTextColor(41, 128, 185);
				doc.setFont('helvetica', 'bold');
				doc.text('LOCALIZAÇÃO', 15, yPosition);
				yPosition += 8;

				autoTable(doc, {
					startY: yPosition,
					head: [['Campo', 'Valor']],
					body: localizacaoData,
					theme: 'striped',
					headStyles: {
						fillColor: [41, 128, 185],
						textColor: [255, 255, 255],
						fontStyle: 'bold',
						fontSize: 10
					},
					bodyStyles: {
						fontSize: 10,
						textColor: [0, 0, 0]
					},
					columnStyles: {
						0: { cellWidth: 50, fontStyle: 'bold' },
						1: { cellWidth: 140 }
					},
					margin: { left: 15, right: 15 },
					styles: { cellPadding: 4 }
				});

				type JsPDFWithAutoTable3 = jsPDF & {
					lastAutoTable?: { finalY: number };
				};
				const finalYLocalizacao =
					(doc as JsPDFWithAutoTable3).lastAutoTable?.finalY ?? yPosition + 10;
				yPosition = finalYLocalizacao + 10;
			}

			// Validação de GPS e Anti-Spoofing
			if (registro.latitude && registro.longitude) {
				// Verificar se precisa de nova página
				if (yPosition > 200) {
					doc.addPage();
					yPosition = 20;
				}

				doc.setFontSize(12);
				doc.setTextColor(41, 128, 185);
				doc.setFont('helvetica', 'bold');
				doc.text('VALIDAÇÃO DE LOCALIZAÇÃO GPS', 15, yPosition);
				yPosition += 10;

				// Dados do GPS em tabela
				const gpsData: Array<[string, string]> = [];
				if (registro.precisao !== null && registro.precisao !== undefined) {
					gpsData.push(['Precisão', `${registro.precisao.toFixed(2)} metros`]);
				}

				if (gpsData.length > 0) {
					autoTable(doc, {
						startY: yPosition,
						head: [['Campo', 'Valor']],
						body: gpsData,
						theme: 'striped',
						headStyles: {
							fillColor: [41, 128, 185],
							textColor: [255, 255, 255],
							fontStyle: 'bold',
							fontSize: 10
						},
						bodyStyles: {
							fontSize: 10,
							textColor: [0, 0, 0]
						},
						columnStyles: {
							0: { cellWidth: 50, fontStyle: 'bold' },
							1: { cellWidth: 140 }
						},
						margin: { left: 15, right: 15 },
						styles: { cellPadding: 4 }
					});

					type JsPDFWithAutoTable4 = jsPDF & {
						lastAutoTable?: { finalY: number };
					};
					const finalYGps = (doc as JsPDFWithAutoTable4).lastAutoTable?.finalY ?? yPosition + 10;
					yPosition = finalYGps + 5;
				}

				// Confiabilidade em tabela
				const confiabilidadeData: Array<[string, string]> = [];
				if (registro.confiabilidadeGPS !== null && registro.confiabilidadeGPS !== undefined) {
					const confiabilidadePercent = (registro.confiabilidadeGPS * 100).toFixed(1);
					confiabilidadeData.push(['Confiabilidade GPS (Frontend)', `${confiabilidadePercent}%`]);
				}

				if (
					registro.scoreConfiancaBackend !== null &&
					registro.scoreConfiancaBackend !== undefined
				) {
					const scorePercent = (registro.scoreConfiancaBackend * 100).toFixed(1);
					confiabilidadeData.push(['Score de Confiança (Backend)', `${scorePercent}%`]);
				}

				if (confiabilidadeData.length > 0) {
					doc.setFontSize(11);
					doc.setTextColor(0, 0, 0);
					doc.setFont('helvetica', 'bold');
					doc.text('Confiabilidade:', 15, yPosition);
					yPosition += 8;

					autoTable(doc, {
						startY: yPosition,
						head: [['Campo', 'Valor']],
						body: confiabilidadeData,
						theme: 'striped',
						headStyles: {
							fillColor: [60, 60, 60],
							textColor: [255, 255, 255],
							fontStyle: 'bold',
							fontSize: 10
						},
						bodyStyles: {
							fontSize: 10
						},
						columnStyles: {
							0: { cellWidth: 80, fontStyle: 'bold' },
							1: { cellWidth: 110 }
						},
						margin: { left: 15, right: 15 },
						styles: { cellPadding: 4 },
						didParseCell: (data: any) => {
							if (data.section === 'body' && data.column.index === 1) {
								// Aplicar cores baseadas nos valores
								const valorTexto = data.cell.text[0];
								const valorNum = parseFloat(valorTexto.replace('%', ''));
								if (!isNaN(valorNum)) {
									if (valorNum >= 70) {
										data.cell.styles.textColor = [0, 128, 0];
										data.cell.styles.fontStyle = 'bold';
									} else if (valorNum >= 40) {
										data.cell.styles.textColor = [255, 165, 0];
										data.cell.styles.fontStyle = 'bold';
									} else {
										data.cell.styles.textColor = [255, 0, 0];
										data.cell.styles.fontStyle = 'bold';
									}
								}
							}
						}
					});

					type JsPDFWithAutoTable5 = jsPDF & {
						lastAutoTable?: { finalY: number };
					};
					const finalYConf = (doc as JsPDFWithAutoTable5).lastAutoTable?.finalY ?? yPosition + 10;
					yPosition = finalYConf + 5;
				}

				// Status de Validação em destaque
				if (registro.suspeitaSpoofing !== null && registro.suspeitaSpoofing !== undefined) {
					const statusData: Array<[string, string]> = [];

					if (registro.suspeitaSpoofing) {
						statusData.push(['Status', '⚠️ MARCAÇÃO SUSPEITA DETECTADA']);
						if (registro.motivoSuspeita) {
							statusData.push(['Motivo', registro.motivoSuspeita]);
						}
					} else {
						statusData.push(['Status', '✓ Localização validada com sucesso']);
					}

					if (statusData.length > 0) {
						doc.setFontSize(11);
						doc.setTextColor(0, 0, 0);
						doc.setFont('helvetica', 'bold');
						doc.text('Status de Validação:', 15, yPosition);
						yPosition += 8;

						autoTable(doc, {
							startY: yPosition,
							head: [['Campo', 'Valor']],
							body: statusData,
							theme: 'striped',
							headStyles: {
								fillColor: registro.suspeitaSpoofing ? [200, 0, 0] : [0, 128, 0],
								textColor: [255, 255, 255],
								fontStyle: 'bold',
								fontSize: 10
							},
							bodyStyles: {
								fontSize: 10
							},
							columnStyles: {
								0: { cellWidth: 50, fontStyle: 'bold' },
								1: { cellWidth: 140 }
							},
							margin: { left: 15, right: 15 },
							styles: { cellPadding: 4 },
							didParseCell: (data: any) => {
								if (data.section === 'body' && data.column.index === 1) {
									if (registro.suspeitaSpoofing) {
										data.cell.styles.textColor = [200, 0, 0];
										data.cell.styles.fontStyle = 'bold';
									} else {
										data.cell.styles.textColor = [0, 128, 0];
										data.cell.styles.fontStyle = 'bold';
									}
								}
							}
						});

						type JsPDFWithAutoTable6 = jsPDF & {
							lastAutoTable?: { finalY: number };
						};
						const finalYStatus =
							(doc as JsPDFWithAutoTable6).lastAutoTable?.finalY ?? yPosition + 10;
						yPosition = finalYStatus + 5;
					}
				}

				// Avisos de Validação em tabela
				if (registro.avisosValidacao && registro.avisosValidacao.length > 0) {
					const avisosData = registro.avisosValidacao.map((aviso: string) => ['', aviso]);

					doc.setFontSize(11);
					doc.setTextColor(0, 0, 0);
					doc.setFont('helvetica', 'bold');
					doc.text('Avisos de Validação:', 15, yPosition);
					yPosition += 8;

					autoTable(doc, {
						startY: yPosition,
						head: [['', 'Aviso']],
						body: avisosData,
						theme: 'striped',
						headStyles: {
							fillColor: [255, 165, 0],
							textColor: [255, 255, 255],
							fontStyle: 'bold',
							fontSize: 10
						},
						bodyStyles: {
							fontSize: 10,
							textColor: [0, 0, 0]
						},
						columnStyles: {
							0: { cellWidth: 10 },
							1: { cellWidth: 180 }
						},
						margin: { left: 15, right: 15 },
						styles: { cellPadding: 4 }
					});

					type JsPDFWithAutoTable7 = jsPDF & {
						lastAutoTable?: { finalY: number };
					};
					const finalYAvisos = (doc as JsPDFWithAutoTable7).lastAutoTable?.finalY ?? yPosition + 10;
					yPosition = finalYAvisos + 5;
				}

				// Análise de Propriedades GPS em tabela
				const propriedadesData: Array<[string, string]> = [];
				let propriedadesGPS = 0;
				let propriedadesTotais = 5;

				if (
					registro.altitude !== null &&
					registro.altitude !== undefined &&
					registro.altitude !== 0
				) {
					propriedadesData.push(['Altitude', '✓ Disponível']);
					propriedadesGPS++;
				} else {
					propriedadesData.push(['Altitude', '✗ Não disponível']);
				}

				if (
					registro.altitudeAccuracy !== null &&
					registro.altitudeAccuracy !== undefined &&
					registro.altitudeAccuracy > 0
				) {
					propriedadesData.push(['Precisão de Altitude', '✓ Disponível']);
					propriedadesGPS++;
				} else {
					propriedadesData.push(['Precisão de Altitude', '✗ Não disponível']);
				}

				if (
					registro.heading !== null &&
					registro.heading !== undefined &&
					!isNaN(registro.heading)
				) {
					propriedadesData.push(['Direção (Heading)', '✓ Disponível']);
					propriedadesGPS++;
				} else {
					propriedadesData.push(['Direção (Heading)', '✗ Não disponível']);
				}

				if (registro.speed !== null && registro.speed !== undefined && !isNaN(registro.speed)) {
					propriedadesData.push(['Velocidade', '✓ Disponível']);
					propriedadesGPS++;
				} else {
					propriedadesData.push(['Velocidade', '✗ Não disponível']);
				}

				if (
					registro.precisao !== null &&
					registro.precisao !== undefined &&
					registro.precisao < 20
				) {
					propriedadesData.push(['Precisão GPS', '✓ Alta precisão (< 20m)']);
					propriedadesGPS++;
				} else if (
					registro.precisao !== null &&
					registro.precisao !== undefined &&
					registro.precisao >= 20 &&
					registro.precisao < 100
				) {
					propriedadesData.push(['Precisão GPS', '⚠ Precisão média (20-100m)']);
					propriedadesGPS += 0.5;
				} else {
					propriedadesData.push(['Precisão GPS', '✗ Baixa precisão (> 100m)']);
				}

				// Indicador de qualidade GPS
				const qualidadeGPS = (propriedadesGPS / propriedadesTotais) * 100;
				const qualidadeTexto =
					qualidadeGPS >= 80
						? 'Alta qualidade (GPS real)'
						: qualidadeGPS >= 50
							? 'Qualidade média'
							: 'Baixa qualidade (possível spoofing)';
				const qualidadeCor =
					qualidadeGPS >= 80 ? [0, 128, 0] : qualidadeGPS >= 50 ? [255, 165, 0] : [255, 0, 0];
				propriedadesData.push(['Qualidade GPS', `${qualidadeTexto} (${qualidadeGPS.toFixed(0)}%)`]);

				doc.setFontSize(11);
				doc.setTextColor(0, 0, 0);
				doc.setFont('helvetica', 'bold');
				doc.text('Análise de Propriedades GPS:', 15, yPosition);
				yPosition += 8;

				autoTable(doc, {
					startY: yPosition,
					head: [['Propriedade', 'Status']],
					body: propriedadesData,
					theme: 'striped',
					headStyles: {
						fillColor: [60, 60, 60],
						textColor: [255, 255, 255],
						fontStyle: 'bold',
						fontSize: 10
					},
					bodyStyles: {
						fontSize: 10
					},
					columnStyles: {
						0: { cellWidth: 80, fontStyle: 'bold' },
						1: { cellWidth: 110 }
					},
					margin: { left: 15, right: 15 },
					styles: { cellPadding: 4 },
					didParseCell: (data: any) => {
						if (data.section === 'body' && data.column.index === 1) {
							const texto = data.cell.text[0];
							if (texto.includes('✓')) {
								data.cell.styles.textColor = [0, 128, 0];
								data.cell.styles.fontStyle = 'bold';
							} else if (texto.includes('✗')) {
								data.cell.styles.textColor = [200, 0, 0];
							} else if (texto.includes('⚠')) {
								data.cell.styles.textColor = [255, 165, 0];
								data.cell.styles.fontStyle = 'bold';
							}
							// Última linha (Qualidade GPS)
							if (data.row.index === propriedadesData.length - 1) {
								data.cell.styles.textColor = qualidadeCor;
								data.cell.styles.fontStyle = 'bold';
							}
						}
					}
				});

				type JsPDFWithAutoTable8 = jsPDF & {
					lastAutoTable?: { finalY: number };
				};
				const finalYPropriedades =
					(doc as JsPDFWithAutoTable8).lastAutoTable?.finalY ?? yPosition + 10;
				yPosition = finalYPropriedades + 10;
			}

			// Validação de Geofencing (Localização Permitida)
			if (registro.latitude && registro.longitude) {
				// Verificar se precisa de nova página
				if (yPosition > 200) {
					doc.addPage();
					yPosition = 20;
				}

				doc.setFontSize(12);
				doc.setTextColor(41, 128, 185);
				doc.setFont('helvetica', 'bold');
				doc.text('VALIDAÇÃO DE LOCALIZAÇÃO PERMITIDA', 15, yPosition);
				yPosition += 10;

				if (registro.enderecoMarcacaoEsperado || registro.dentroRaioPermitido !== undefined) {
					// Buscar dados do endereço esperado se houver ID
					let enderecoEsperadoNome = 'Não configurado';
					let enderecoEsperadoEndereco = 'Não configurado';
					let enderecoEsperadoLatitude: number | null = null;
					let enderecoEsperadoLongitude: number | null = null;

					if (registro.enderecoMarcacaoEsperado) {
						try {
							const enderecoEsperado = await client.query(api.enderecosMarcacao.obterEndereco, {
								enderecoId: registro.enderecoMarcacaoEsperado
							});
							if (enderecoEsperado) {
								enderecoEsperadoNome = enderecoEsperado.nome;
								enderecoEsperadoEndereco = `${enderecoEsperado.endereco}, ${enderecoEsperado.cidade}/${enderecoEsperado.estado}`;
								enderecoEsperadoLatitude = enderecoEsperado.latitude;
								enderecoEsperadoLongitude = enderecoEsperado.longitude;
							}
						} catch (error) {
							console.warn('Erro ao buscar endereço esperado:', error);
						}
					}

					const geofencingData: Array<[string, string]> = [
						['Endereço Esperado', enderecoEsperadoNome],
						['Localização', enderecoEsperadoEndereco]
					];

					if (enderecoEsperadoLatitude !== null && enderecoEsperadoLongitude !== null) {
						geofencingData.push([
							'Coordenadas Esperadas',
							`${enderecoEsperadoLatitude.toFixed(6)}, ${enderecoEsperadoLongitude.toFixed(6)}`
						]);
					}

					geofencingData.push([
						'Coordenadas do Registro',
						`${registro.latitude.toFixed(6)}, ${registro.longitude.toFixed(6)}`
					]);

					if (
						registro.distanciaEnderecoEsperado !== null &&
						registro.distanciaEnderecoEsperado !== undefined
					) {
						const distanciaKm = (registro.distanciaEnderecoEsperado / 1000).toFixed(2);
						const distanciaMetros = registro.distanciaEnderecoEsperado.toFixed(0);
						const distanciaTexto =
							registro.distanciaEnderecoEsperado >= 1000
								? `${distanciaKm} km (${distanciaMetros} metros)`
								: `${distanciaMetros} metros`;
						geofencingData.push(['Distância', distanciaTexto]);
					}

					if (registro.raioToleranciaUsado !== null && registro.raioToleranciaUsado !== undefined) {
						const raioKm = (registro.raioToleranciaUsado / 1000).toFixed(2);
						const raioMetros = registro.raioToleranciaUsado.toFixed(0);
						const raioTexto =
							registro.raioToleranciaUsado >= 1000
								? `${raioKm} km (${raioMetros} metros)`
								: `${raioMetros} metros`;
						geofencingData.push(['Raio Permitido', raioTexto]);
					} else {
						geofencingData.push(['Raio Permitido', 'Não configurado']);
					}

					// Status da validação
					let statusTexto = 'Não validado';
					if (registro.dentroRaioPermitido === true) {
						statusTexto = '✓ DENTRO DO RAIO PERMITIDO';
					} else if (registro.dentroRaioPermitido === false) {
						statusTexto = '⚠️ FORA DO RAIO PERMITIDO';
						if (
							registro.distanciaEnderecoEsperado !== null &&
							registro.distanciaEnderecoEsperado !== undefined &&
							registro.raioToleranciaUsado !== null &&
							registro.raioToleranciaUsado !== undefined
						) {
							const distanciaExcedente =
								registro.distanciaEnderecoEsperado - registro.raioToleranciaUsado;
							const distanciaExcedenteKm = (distanciaExcedente / 1000).toFixed(2);
							const distanciaExcedenteMetros = distanciaExcedente.toFixed(0);
							const excedenteTexto =
								distanciaExcedente >= 1000
									? `${distanciaExcedenteKm} km além do permitido`
									: `${distanciaExcedenteMetros} metros além do permitido`;
							geofencingData.push(['Distância Excedente', excedenteTexto]);
						}
					}
					geofencingData.push(['Status', statusTexto]);

					autoTable(doc, {
						startY: yPosition,
						head: [['Campo', 'Valor']],
						body: geofencingData,
						theme: 'striped',
						headStyles: {
							fillColor: [41, 128, 185],
							textColor: [255, 255, 255],
							fontStyle: 'bold',
							fontSize: 10
						},
						bodyStyles: {
							fontSize: 10
						},
						columnStyles: {
							0: { cellWidth: 60, fontStyle: 'bold' },
							1: { cellWidth: 130 }
						},
						margin: { left: 15, right: 15 },
						styles: { cellPadding: 4 },
						didParseCell: (data: any) => {
							if (data.section === 'body' && data.column.index === 1) {
								const texto = data.cell.text[0];
								if (texto.includes('✓ DENTRO')) {
									data.cell.styles.textColor = [0, 128, 0];
									data.cell.styles.fontStyle = 'bold';
								} else if (texto.includes('⚠️ FORA')) {
									data.cell.styles.textColor = [200, 0, 0];
									data.cell.styles.fontStyle = 'bold';
								}
							}
						}
					});

					type JsPDFWithAutoTable9 = jsPDF & {
						lastAutoTable?: { finalY: number };
					};
					const finalYGeofencing =
						(doc as JsPDFWithAutoTable9).lastAutoTable?.finalY ?? yPosition + 10;
					yPosition = finalYGeofencing + 5;

					// Observação se fora do raio
					if (registro.dentroRaioPermitido === false) {
						doc.setFontSize(9);
						doc.setTextColor(100, 100, 100);
						const observacaoLines = doc.splitTextToSize(
							'O registro foi realizado fora da área permitida de marcação de ponto. Verifique se o funcionário possui autorização para trabalho remoto ou deslocamento.',
							170
						);
						doc.text(observacaoLines, 20, yPosition);
						yPosition += observacaoLines.length * 4 + 5;
						doc.setFontSize(10);
						doc.setTextColor(0, 0, 0);
					}

					yPosition += 5;
				} else {
					doc.setFontSize(10);
					doc.setTextColor(100, 100, 100);
					doc.text(
						'Validação de localização permitida não configurada para este registro.',
						15,
						yPosition
					);
					yPosition += 8;
					doc.setTextColor(0, 0, 0);
				}
			}

			// Dados Técnicos
			// Verificar se precisa de nova página
			if (yPosition > 200) {
				doc.addPage();
				yPosition = 20;
			}

			doc.setFontSize(12);
			doc.setTextColor(41, 128, 185);
			doc.setFont('helvetica', 'bold');
			doc.text('DADOS TÉCNICOS', 15, yPosition);
			yPosition += 10;

			// Consolidar todos os dados técnicos em uma única tabela
			const dadosTecnicosData: Array<[string, string]> = [];

			// Informações de Rede
			if (registro.ipAddress || registro.ipPublico || registro.ipLocal) {
				if (registro.ipAddress) {
					dadosTecnicosData.push(['IP', registro.ipAddress]);
				}
				if (registro.ipPublico) {
					dadosTecnicosData.push(['IP Público', registro.ipPublico]);
				}
				if (registro.ipLocal) {
					dadosTecnicosData.push(['IP Local', registro.ipLocal]);
				}
			}

			// Informações do Navegador
			if (registro.browser || registro.userAgent) {
				if (registro.browser) {
					dadosTecnicosData.push([
						'Navegador',
						`${registro.browser}${registro.browserVersion ? ` ${registro.browserVersion}` : ''}`
					]);
				}
				if (registro.engine) {
					dadosTecnicosData.push(['Engine', registro.engine]);
				}
				if (registro.userAgent) {
					dadosTecnicosData.push(['User Agent', registro.userAgent]);
				}
			}

			// Informações do Sistema
			if (registro.sistemaOperacional || registro.arquitetura) {
				if (registro.sistemaOperacional) {
					dadosTecnicosData.push([
						'Sistema Operacional',
						`${registro.sistemaOperacional}${registro.osVersion ? ` ${registro.osVersion}` : ''}`
					]);
				}
				if (registro.arquitetura) {
					dadosTecnicosData.push(['Arquitetura', registro.arquitetura]);
				}
				if (registro.plataforma) {
					dadosTecnicosData.push(['Plataforma', registro.plataforma]);
				}
			}

			// Informações do Dispositivo
			if (registro.deviceType || registro.screenResolution) {
				if (registro.deviceType) {
					dadosTecnicosData.push(['Tipo de Dispositivo', registro.deviceType]);
				}
				if (registro.deviceModel) {
					dadosTecnicosData.push(['Modelo', registro.deviceModel]);
				}
				if (registro.screenResolution) {
					dadosTecnicosData.push(['Resolução', registro.screenResolution]);
				}
				if (registro.coresTela) {
					dadosTecnicosData.push(['Cores da Tela', registro.coresTela]);
				}
				if (registro.isMobile || registro.isTablet || registro.isDesktop) {
					const tipoDispositivo = registro.isMobile
						? 'Mobile'
						: registro.isTablet
							? 'Tablet'
							: 'Desktop';
					dadosTecnicosData.push(['Categoria', tipoDispositivo]);
				}
				if (registro.idioma) {
					dadosTecnicosData.push(['Idioma', registro.idioma]);
				}
				if (registro.connectionType) {
					dadosTecnicosData.push(['Tipo de Conexão', registro.connectionType]);
				}
				if (registro.memoryInfo) {
					dadosTecnicosData.push(['Memória', registro.memoryInfo]);
				}
			}

			if (dadosTecnicosData.length > 0) {
				autoTable(doc, {
					startY: yPosition,
					head: [['Campo', 'Valor']],
					body: dadosTecnicosData,
					theme: 'striped',
					headStyles: {
						fillColor: [60, 60, 60],
						textColor: [255, 255, 255],
						fontStyle: 'bold',
						fontSize: 10
					},
					bodyStyles: {
						fontSize: 9,
						textColor: [0, 0, 0]
					},
					columnStyles: {
						0: { cellWidth: 60, fontStyle: 'bold' },
						1: { cellWidth: 130 }
					},
					margin: { left: 15, right: 15 },
					styles: { cellPadding: 3 }
				});

				type JsPDFWithAutoTable10 = jsPDF & {
					lastAutoTable?: { finalY: number };
				};
				const finalYTecnicos =
					(doc as JsPDFWithAutoTable10).lastAutoTable?.finalY ?? yPosition + 10;
				yPosition = finalYTecnicos + 10;
			}

			// Imagem capturada (se disponível)
			if (registro.imagemUrl) {
				yPosition += 10;
				// Verificar se precisa de nova página
				if (yPosition > 200) {
					doc.addPage();
					yPosition = 20;
				}

				doc.setFont('helvetica', 'bold');
				doc.text('FOTO CAPTURADA', 105, yPosition, { align: 'center' });
				doc.setFont('helvetica', 'normal');
				yPosition += 10;

				try {
					// Carregar imagem usando fetch para evitar problemas de CORS
					const response = await fetch(registro.imagemUrl);
					if (!response.ok) {
						throw new Error('Erro ao carregar imagem');
					}

					const blob = await response.blob();
					const reader = new FileReader();

					// Converter blob para base64
					const base64 = await new Promise<string>((resolve, reject) => {
						reader.onloadend = () => {
							if (typeof reader.result === 'string') {
								resolve(reader.result);
							} else {
								reject(new Error('Erro ao converter imagem'));
							}
						};
						reader.onerror = () => reject(new Error('Erro ao ler imagem'));
						reader.readAsDataURL(blob);
					});

					// Criar elemento de imagem para obter dimensões
					const img = new Image();
					await new Promise<void>((resolve, reject) => {
						img.onload = () => resolve();
						img.onerror = () => reject(new Error('Erro ao processar imagem'));
						img.src = base64;
						setTimeout(() => reject(new Error('Timeout ao processar imagem')), 10000);
					});

					// Calcular dimensões para caber na página (largura máxima 80mm, manter proporção)
					const maxWidth = 80;
					const maxHeight = 60;
					let imgWidth = img.width;
					let imgHeight = img.height;
					const aspectRatio = imgWidth / imgHeight;

					if (imgWidth > maxWidth || imgHeight > maxHeight) {
						if (aspectRatio > 1) {
							// Imagem horizontal
							imgWidth = maxWidth;
							imgHeight = maxWidth / aspectRatio;
						} else {
							// Imagem vertical
							imgHeight = maxHeight;
							imgWidth = maxHeight * aspectRatio;
						}
					}

					// Centralizar imagem
					const xPosition = (doc.internal.pageSize.getWidth() - imgWidth) / 2;

					// Verificar se cabe na página atual
					if (yPosition + imgHeight > doc.internal.pageSize.getHeight() - 20) {
						doc.addPage();
						yPosition = 20;
					}

					// Adicionar imagem ao PDF usando base64
					doc.addImage(base64, 'JPEG', xPosition, yPosition, imgWidth, imgHeight);
					yPosition += imgHeight + 10;
				} catch (error) {
					console.warn('Erro ao adicionar imagem ao PDF:', error);
					doc.setFontSize(10);
					doc.text('Foto não disponível para impressão', 105, yPosition, { align: 'center' });
					yPosition += 6;
				}
			}

			// Rodapé melhorado
			const pageCount = doc.getNumberOfPages();
			for (let i = 1; i <= pageCount; i++) {
				doc.setPage(i);

				// Linha decorativa no rodapé
				doc.setDrawColor(200, 200, 200);
				doc.setLineWidth(0.3);
				doc.line(
					15,
					doc.internal.pageSize.getHeight() - 20,
					195,
					doc.internal.pageSize.getHeight() - 20
				);

				// Texto do rodapé
				doc.setFontSize(8);
				doc.setTextColor(100, 100, 100);
				doc.setFont('helvetica', 'normal');
				const dataGeracao = new Date().toLocaleDateString('pt-BR', {
					day: '2-digit',
					month: '2-digit',
					year: 'numeric',
					hour: '2-digit',
					minute: '2-digit'
				});
				doc.text(
					`SGSE - Sistema de Gerenciamento de Secretaria de Esportes`,
					15,
					doc.internal.pageSize.getHeight() - 12,
					{ align: 'left' }
				);
				doc.text(
					`Gerado em: ${dataGeracao} | Página ${i} de ${pageCount}`,
					195,
					doc.internal.pageSize.getHeight() - 12,
					{ align: 'right' }
				);
			}

			// Salvar
			const nomeArquivo = `detalhes-ponto-${registro.data}-${registro.hora.toString().padStart(2, '0')}${registro.minuto.toString().padStart(2, '0')}.pdf`;
			doc.save(nomeArquivo);
		} catch (error) {
			console.error('Erro ao gerar PDF detalhado:', error);
			alert('Erro ao gerar relatório detalhado. Tente novamente.');
		}
	}
	*/
</script>

<div class="container mx-auto max-w-7xl px-4 py-6">
	<!-- Header -->
	<section
		class="border-base-300 from-primary/10 via-base-100 to-secondary/10 relative mb-8 overflow-hidden rounded-2xl border bg-gradient-to-br p-8 shadow-lg"
	>
		<div class="bg-primary/20 absolute top-10 -left-10 h-40 w-40 rounded-full blur-3xl"></div>
		<div class="bg-secondary/20 absolute right-0 -bottom-16 h-56 w-56 rounded-full blur-3xl"></div>
		<div class="relative z-10 flex flex-col gap-6 lg:flex-row lg:items-center lg:justify-between">
			<div class="flex items-center gap-4">
				<div
					class="bg-primary/20 border-primary/30 rounded-2xl border p-4 shadow-lg backdrop-blur-sm"
				>
					<Clock class="text-primary h-10 w-10" strokeWidth={2.5} />
				</div>
				<div class="max-w-3xl space-y-2">
					<h1 class="text-base-content text-4xl leading-tight font-black sm:text-5xl">
						Registro de Pontos
					</h1>
					<p class="text-base-content/70 text-base leading-relaxed sm:text-lg">
						Gerencie e visualize os registros de ponto dos funcionários com informações detalhadas e
						relatórios
					</p>
				</div>
			</div>
			{#if estatisticas}
				<div
					class="border-base-200/60 bg-base-100/70 grid grid-cols-2 gap-4 rounded-2xl border p-6 shadow-lg backdrop-blur sm:max-w-sm"
				>
					<div>
						<p class="text-base-content/60 text-sm font-semibold">Total de Registros</p>
						<p class="text-base-content mt-2 text-2xl font-bold">{estatisticas.totalRegistros}</p>
					</div>
					<div class="text-right">
						<p class="text-base-content/60 text-sm font-semibold">Funcionários</p>
						<p class="text-base-content mt-2 text-xl font-bold">{estatisticas.totalFuncionarios}</p>
					</div>
					<div
						class="via-base-300 col-span-2 h-px bg-gradient-to-r from-transparent to-transparent"
					></div>
					<div class="text-base-content/70 col-span-2 flex items-center justify-between text-sm">
						<span>
							{estatisticas.totalRegistros > 0
								? ((estatisticas.dentroDoPrazo / estatisticas.totalRegistros) * 100).toFixed(1)
								: 0}% dentro do prazo
						</span>
						<span class="badge badge-primary badge-sm">Ativo</span>
					</div>
				</div>
			{/if}
		</div>
	</section>

	<!-- Cards de Estatísticas -->
	{#if estatisticas}
		<div class="mb-8 grid grid-cols-1 gap-6 md:grid-cols-2 lg:grid-cols-4">
			<!-- Total de Registros -->
			<div
				class="card transform border border-blue-500/20 bg-gradient-to-br from-blue-500/10 to-blue-600/20 shadow-xl transition-all duration-300 hover:-translate-y-1 hover:shadow-2xl"
			>
				<div class="card-body">
					<div class="flex items-center justify-between">
						<div class="flex-1">
							<p class="text-base-content/70 mb-1 text-sm font-semibold">Total de Registros</p>
							<p class="text-base-content text-3xl font-bold">{estatisticas.totalRegistros}</p>
						</div>
						<div class="rounded-xl bg-blue-500/20 p-3">
							<BarChart3 class="h-8 w-8 text-blue-600" strokeWidth={2.5} />
						</div>
					</div>
				</div>
			</div>

			<!-- Dentro do Prazo -->
			<div
				class="card transform border border-green-500/20 bg-gradient-to-br from-green-500/10 to-green-600/20 shadow-xl transition-all duration-300 hover:-translate-y-1 hover:shadow-2xl"
			>
				<div class="card-body">
					<div class="flex items-center justify-between">
						<div class="flex-1">
							<p class="text-base-content/70 mb-1 text-sm font-semibold">Dentro do Prazo</p>
							<p class="text-3xl font-bold text-green-600">{estatisticas.dentroDoPrazo}</p>
							<p class="text-base-content/60 mt-1 text-xs">
								{estatisticas.totalRegistros > 0
									? ((estatisticas.dentroDoPrazo / estatisticas.totalRegistros) * 100).toFixed(1)
									: 0}% do total
							</p>
						</div>
						<div class="rounded-xl bg-green-500/20 p-3">
							<CheckCircle2 class="h-8 w-8 text-green-600" strokeWidth={2.5} />
						</div>
					</div>
				</div>
			</div>

			<!-- Fora do Prazo -->
			<div
				class="card transform border border-red-500/20 bg-gradient-to-br from-red-500/10 to-red-600/20 shadow-xl transition-all duration-300 hover:-translate-y-1 hover:shadow-2xl"
			>
				<div class="card-body">
					<div class="flex items-center justify-between">
						<div class="flex-1">
							<p class="text-base-content/70 mb-1 text-sm font-semibold">Fora do Prazo</p>
							<p class="text-3xl font-bold text-red-600">{estatisticas.foraDoPrazo}</p>
							<p class="text-base-content/60 mt-1 text-xs">
								{estatisticas.totalRegistros > 0
									? ((estatisticas.foraDoPrazo / estatisticas.totalRegistros) * 100).toFixed(1)
									: 0}% do total
							</p>
						</div>
						<div class="rounded-xl bg-red-500/20 p-3">
							<XCircle class="h-8 w-8 text-red-600" strokeWidth={2.5} />
						</div>
					</div>
				</div>
			</div>

			<!-- Funcionários -->
			<div
				class="card transform border border-purple-500/20 bg-gradient-to-br from-purple-500/10 to-purple-600/20 shadow-xl transition-all duration-300 hover:-translate-y-1 hover:shadow-2xl"
			>
				<div class="card-body">
					<div class="flex items-center justify-between">
						<div class="flex-1">
							<p class="text-base-content/70 mb-1 text-sm font-semibold">Funcionários</p>
							<p class="text-3xl font-bold text-purple-600">{estatisticas.totalFuncionarios}</p>
							<p class="text-base-content/60 mt-1 text-xs">
								{estatisticas.funcionariosDentroPrazo} dentro, {estatisticas.funcionariosForaPrazo} fora
							</p>
						</div>
						<div class="rounded-xl bg-purple-500/20 p-3">
							<Users class="h-8 w-8 text-purple-600" strokeWidth={2.5} />
						</div>
					</div>
				</div>
			</div>
		</div>
	{/if}

	<!-- Gráfico de Estatísticas -->
	<div class="card bg-base-100/90 border-base-300 mb-8 border shadow-xl backdrop-blur-sm">
		<div class="card-body">
			<div class="mb-6 flex items-center justify-between">
				<h2 class="card-title text-2xl">
					<div class="bg-primary/10 rounded-lg p-2">
						<BarChart3 class="text-primary h-6 w-6" strokeWidth={2.5} />
					</div>
					<span>Visão Geral das Estatísticas</span>
				</h2>
			</div>
			<div class="bg-base-200/50 border-base-300 relative h-80 w-full rounded-xl border p-4">
				{#if estatisticasQuery === undefined || estatisticasQuery?.isLoading}
					<div class="bg-base-200/30 absolute inset-0 flex items-center justify-center rounded-xl">
						<div class="flex flex-col items-center gap-4">
							<span class="loading loading-spinner loading-lg text-primary"></span>
							<span class="text-base-content/70 font-medium">Carregando estatísticas...</span>
						</div>
					</div>
				{:else if estatisticasQuery?.error}
					<div class="bg-base-200/30 absolute inset-0 flex items-center justify-center rounded-xl">
						<div class="alert alert-error shadow-lg">
							<XCircle class="h-6 w-6" />
							<div>
								<h3 class="font-bold">Erro ao carregar estatísticas</h3>
								<div class="mt-1 text-sm">
									{estatisticasQuery.error?.message ||
										String(estatisticasQuery.error) ||
										'Erro desconhecido'}
								</div>
							</div>
						</div>
					</div>
				{:else if !estatisticas || !chartData}
					<div class="bg-base-200/30 absolute inset-0 flex items-center justify-center rounded-xl">
						<div class="text-center">
							<FileText class="text-base-content/30 mx-auto mb-2 h-12 w-12" />
							<p class="text-base-content/70">Nenhuma estatística disponível</p>
						</div>
					</div>
				{:else}
					<canvas bind:this={chartCanvas} class="h-full w-full"></canvas>
				{/if}
			</div>
		</div>
	</div>

	<!-- Seção Integrada: Filtros e Registros -->
	<div class="card bg-base-100 border-base-300 border shadow-xl">
		<div class="card-body p-6">
			<!-- Cabeçalho Unificado -->
			<div class="mb-6 flex flex-col gap-4 lg:flex-row lg:items-center lg:justify-between">
				<div class="flex items-center gap-3">
					<div class="from-primary/20 to-primary/10 rounded-xl bg-gradient-to-br p-3 shadow-sm">
						<Clock class="text-primary h-6 w-6" strokeWidth={2.5} />
					</div>
					<div>
						<h2 class="text-base-content text-2xl font-bold">Registros de Ponto</h2>
						<p class="text-base-content/60 mt-1 text-sm">
							Gerencie e visualize os registros de ponto dos funcionários
						</p>
					</div>
				</div>
				<div class="flex flex-wrap items-center gap-2">
					<button type="button" class="btn btn-outline btn-sm gap-2" onclick={limparFiltros}>
						<Filter class="h-4 w-4" />
						Limpar Filtros
					</button>
					<button
						type="button"
						class="btn btn-primary btn-sm gap-2"
						onclick={exportarCSV}
						disabled={registrosFiltrados.length === 0}
					>
						<Download class="h-4 w-4" />
						Exportar CSV
					</button>
				</div>
			</div>

			<!-- Filtros Compactos e Modernos -->
			<div class="bg-base-200/50 border-base-300 mb-6 rounded-xl border p-4">
				<div class="mb-3 flex items-center gap-2">
					<Filter class="text-base-content/60 h-4 w-4" strokeWidth={2} />
					<span class="text-base-content/70 text-sm font-semibold">Filtros de Busca</span>
				</div>
				<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-5">
					<div class="form-control">
						<label class="label py-1" for="data-inicio-text">
							<span class="label-text text-base-content/70 text-xs font-medium">Data Início</span>
						</label>
						<div class="relative">
							<input
								id="data-inicio-text"
								type="text"
								inputmode="numeric"
								placeholder="dd/mm/aaaa"
								value={dataInicioExibicao}
								class="input input-bordered input-sm focus:input-primary pr-10"
								oninput={(e) => {
									const target = e.target as HTMLInputElement;
									const valorComMascara = maskDate(target.value);
									dataInicioExibicao = valorComMascara;

									// Converter para formato backend quando completo
									if (validateDate(valorComMascara)) {
										dataInicioInterno = formatarDataParaBackendWrapper(valorComMascara);
										// Atualizar o input date oculto
										const dateInput = document.getElementById(
											'data-inicio-date'
										) as HTMLInputElement;
										if (dateInput) {
											dateInput.value = dataInicioInterno;
										}
									}
								}}
								onblur={() => {
									// Validar e corrigir ao sair do campo
									if (dataInicioExibicao && !validateDate(dataInicioExibicao)) {
										toast.error('Data de início inválida. Use o formato dd/mm/aaaa');
										dataInicioExibicao = formatarDataParaExibicao(dataInicioInterno);
									} else if (dataInicioExibicao && validateDate(dataInicioExibicao)) {
										dataInicioInterno = formatarDataParaBackendWrapper(dataInicioExibicao);
									}
								}}
							/>
							<input
								id="data-inicio-date"
								type="date"
								value={dataInicioInterno}
								class="sr-only"
								onchange={(e) => {
									const target = e.target as HTMLInputElement;
									if (target.value) {
										dataInicioInterno = target.value;
										dataInicioExibicao = formatarDataParaExibicao(target.value);
									}
								}}
							/>
							<button
								type="button"
								class="btn btn-ghost btn-xs btn-circle absolute top-1/2 right-2 z-20 h-6 w-6 -translate-y-1/2 p-0"
								onclick={() => {
									const dateInput = document.getElementById('data-inicio-date') as HTMLInputElement;
									if (dateInput?.showPicker) {
										dateInput.showPicker();
									} else {
										// Fallback: clicar no input date
										dateInput?.click();
									}
								}}
								title="Abrir calendário"
							>
								<Calendar class="text-base-content/50 h-3.5 w-3.5" />
							</button>
						</div>
					</div>

					<div class="form-control">
						<label class="label py-1" for="data-fim-text">
							<span class="label-text text-base-content/70 text-xs font-medium">Data Fim</span>
						</label>
						<div class="relative">
							<input
								id="data-fim-text"
								type="text"
								inputmode="numeric"
								placeholder="dd/mm/aaaa"
								value={dataFimExibicao}
								class="input input-bordered input-sm focus:input-primary pr-10"
								oninput={(e) => {
									const target = e.target as HTMLInputElement;
									const valorComMascara = maskDate(target.value);
									dataFimExibicao = valorComMascara;

									// Converter para formato backend quando completo
									if (validateDate(valorComMascara)) {
										dataFimInterno = formatarDataParaBackendWrapper(valorComMascara);
										// Atualizar o input date oculto
										const dateInput = document.getElementById('data-fim-date') as HTMLInputElement;
										if (dateInput) {
											dateInput.value = dataFimInterno;
										}
									}
								}}
								onblur={() => {
									// Validar e corrigir ao sair do campo
									if (dataFimExibicao && !validateDate(dataFimExibicao)) {
										toast.error('Data de fim inválida. Use o formato dd/mm/aaaa');
										dataFimExibicao = formatarDataParaExibicao(dataFimInterno);
									} else if (dataFimExibicao && validateDate(dataFimExibicao)) {
										dataFimInterno = formatarDataParaBackendWrapper(dataFimExibicao);
									}
								}}
							/>
							<input
								id="data-fim-date"
								type="date"
								value={dataFimInterno}
								class="sr-only"
								onchange={(e) => {
									const target = e.target as HTMLInputElement;
									if (target.value) {
										dataFimInterno = target.value;
										dataFimExibicao = formatarDataParaExibicao(target.value);
									}
								}}
							/>
							<button
								type="button"
								class="btn btn-ghost btn-xs btn-circle absolute top-1/2 right-2 z-20 h-6 w-6 -translate-y-1/2 p-0"
								onclick={() => {
									const dateInput = document.getElementById('data-fim-date') as HTMLInputElement;
									if (dateInput?.showPicker) {
										dateInput.showPicker();
									} else {
										// Fallback: clicar no input date
										dateInput?.click();
									}
								}}
								title="Abrir calendário"
							>
								<Calendar class="text-base-content/50 h-3.5 w-3.5" />
							</button>
						</div>
					</div>

					<div class="form-control">
						<label class="label py-1" for="funcionario">
							<span class="label-text text-base-content/70 text-xs font-medium">Funcionário</span>
						</label>
						<select
							id="funcionario"
							bind:value={funcionarioIdFiltro}
							class="select select-bordered select-sm focus:select-primary"
						>
							<option value="">Todos os funcionários</option>
							{#each funcionarios as funcionario}
								<option value={funcionario._id}>{funcionario.nome}</option>
							{/each}
						</select>
					</div>

					<div class="form-control">
						<label class="label py-1" for="status">
							<span class="label-text text-base-content/70 text-xs font-medium">Status</span>
						</label>
						<select
							id="status"
							bind:value={statusFiltro}
							class="select select-bordered select-sm focus:select-primary"
						>
							<option value="todos">Todos</option>
							<option value="dentro">Dentro do Prazo</option>
							<option value="fora">Fora do Prazo</option>
						</select>
					</div>

					<div class="form-control">
						<label class="label py-1" for="localizacao">
							<span class="label-text text-base-content/70 text-xs font-medium">Localização</span>
						</label>
						<select
							id="localizacao"
							bind:value={localizacaoFiltro}
							class="select select-bordered select-sm focus:select-primary"
						>
							<option value="todos">Todas</option>
							<option value="dentro">Dentro do Raio</option>
							<option value="fora">Fora do Raio</option>
						</select>
					</div>
				</div>

				<!-- Badges de Filtros Ativos -->
				{#if funcionarioIdFiltro || dataInicio || dataFim || statusFiltro !== 'todos' || localizacaoFiltro !== 'todos'}
					<div class="border-base-300 mt-4 flex flex-wrap items-center gap-2 border-t pt-3">
						<span class="text-base-content/60 text-xs font-medium">Filtros ativos:</span>
						{#if funcionarioIdFiltro && funcionarioSelecionadoNome}
							<div class="badge badge-primary badge-sm gap-1.5">
								<Users class="h-3 w-3" />
								{funcionarioSelecionadoNome}
							</div>
						{/if}
						{#if dataInicio}
							<div class="badge badge-info badge-sm gap-1.5">
								<Clock class="h-3 w-3" />
								De: {formatarDataDDMMAAAA(dataInicio)}
							</div>
						{/if}
						{#if dataFim}
							<div class="badge badge-info badge-sm gap-1.5">
								<Clock class="h-3 w-3" />
								Até: {formatarDataDDMMAAAA(dataFim)}
							</div>
						{/if}
						{#if statusFiltro !== 'todos'}
							<div class="badge badge-warning badge-sm">
								Status: {statusFiltro === 'dentro' ? 'Dentro do Prazo' : 'Fora do Prazo'}
							</div>
						{/if}
						{#if localizacaoFiltro !== 'todos'}
							<div class="badge badge-secondary badge-sm">
								Local: {localizacaoFiltro === 'dentro' ? 'Dentro do Raio' : 'Fora do Raio'}
							</div>
						{/if}
						<div class="badge badge-ghost badge-sm">
							{registrosFiltrados.length} registro(s)
						</div>
					</div>
				{/if}
			</div>

			{#if registrosQuery === undefined || registrosQuery?.isLoading}
				<div
					class="bg-base-200/50 border-base-300 flex flex-col items-center justify-center rounded-xl border py-16"
				>
					<span class="loading loading-spinner loading-lg text-primary mb-4"></span>
					<span class="text-base-content/70 font-medium">Carregando registros...</span>
					<span class="text-base-content/50 mt-2 text-sm">Aguarde um momento</span>
				</div>
			{:else if registrosQuery?.error}
				<div class="alert alert-error border-error/50 border-2 shadow-lg">
					<XCircle class="h-6 w-6" />
					<div>
						<h3 class="font-bold">Erro ao carregar registros</h3>
						<div class="mt-1 text-sm">
							{registrosQuery.error?.message || String(registrosQuery.error) || 'Erro desconhecido'}
						</div>
					</div>
				</div>
			{:else if !registrosQuery?.data}
				<div class="alert alert-warning border-warning/50 border-2 shadow-lg">
					<Clock class="h-6 w-6" />
					<span class="font-medium">Aguardando dados da consulta...</span>
				</div>
			{:else if registros.length === 0}
				<div
					class="alert alert-info border-info/50 from-info/10 to-info/5 rounded-xl border-2 bg-gradient-to-r shadow-lg"
				>
					<FileText class="text-info h-6 w-6" />
					<div class="flex-1">
						<h3 class="text-base-content font-bold">Nenhum registro encontrado</h3>
						<div class="mt-2 text-sm opacity-80">
							<p>
								Período: <span class="font-semibold"
									>{formatarDataDDMMAAAA(dataInicio)} até {formatarDataDDMMAAAA(dataFim)}</span
								>
							</p>
							{#if funcionarioIdFiltro && funcionarioSelecionadoNome}
								<p class="mt-1">
									Funcionário: <span class="font-semibold">{funcionarioSelecionadoNome}</span>
								</p>
							{/if}
							<p class="text-base-content/60 mt-2">
								Tente ajustar os filtros para encontrar registros.
							</p>
						</div>
					</div>
				</div>
			{:else if registrosAgrupados.length === 0}
				<div
					class="alert alert-warning border-warning/50 from-warning/10 to-warning/5 rounded-xl border-2 bg-gradient-to-r shadow-lg"
				>
					<FileText class="text-warning h-6 w-6" />
					<div class="flex-1">
						<h3 class="font-bold">Registros encontrados, mas não foi possível agrupá-los</h3>
						<div class="mt-2 text-sm opacity-80">
							Total de registros: <span class="font-semibold">{registros.length}</span>
						</div>
					</div>
				</div>
			{:else}
				<div class="space-y-4">
					{#each registrosAgrupados as grupo}
						<div
							class="card bg-base-100 border-base-300 border shadow-md transition-all duration-200 hover:shadow-lg"
						>
							<div class="card-body p-5">
								<!-- Cabeçalho Modernizado -->
								<div
									class="border-base-300 mb-4 flex flex-col gap-4 border-b pb-4 lg:flex-row lg:items-center lg:justify-between"
								>
									<div class="flex items-start gap-3">
										<div class="bg-primary/10 rounded-lg p-2.5">
											<Users class="text-primary h-5 w-5" strokeWidth={2.5} />
										</div>
										<div class="flex-1">
											<h3 class="text-base-content text-lg font-bold">
												{grupo.funcionario?.nome || 'Funcionário não encontrado'}
											</h3>
											{#if grupo.funcionario?.matricula}
												<p class="text-base-content/60 mt-1 text-sm">
													Matrícula: <span class="font-semibold">{grupo.funcionario.matricula}</span
													>
												</p>
											{/if}
											{#if grupo.funcionario?.descricaoCargo}
												<p class="text-base-content/50 mt-1 text-xs">
													{grupo.funcionario.descricaoCargo}
												</p>
											{/if}
										</div>
									</div>

									<div class="flex flex-wrap items-center gap-3">
										<!-- Banco de Horas -->
										{#key grupo.funcionarioId}
											{@const bancoHorasQuery = useQuery(api.pontos.obterBancoHorasFuncionario, {
												funcionarioId: grupo.funcionarioId
											})}
											{@const bancoHoras = bancoHorasQuery?.data}
											{@const saldoAcumulado = bancoHoras?.saldoAcumuladoMinutos ?? 0}
											{@const saldoPositivo = saldoAcumulado >= 0}

											{#if bancoHoras}
												<div
													class="rounded-lg border p-3 shadow-sm transition-all hover:shadow-md {saldoPositivo
														? 'border-success/30 bg-success/5'
														: 'border-error/30 bg-error/5'}"
												>
													<div class="flex items-center gap-2.5">
														<div
															class="rounded-lg p-1.5 {saldoPositivo
																? 'bg-success/20'
																: 'bg-error/20'}"
														>
															{#if saldoPositivo}
																<TrendingUp class="text-success h-4 w-4" strokeWidth={2.5} />
															{:else}
																<TrendingDown class="text-error h-4 w-4" strokeWidth={2.5} />
															{/if}
														</div>
														<div>
															<p class="text-base-content/60 text-xs font-medium">Banco de Horas</p>
															<p
																class="text-base font-bold {saldoPositivo
																	? 'text-success'
																	: 'text-error'}"
															>
																{formatarSaldoHoras(saldoAcumulado)}
															</p>
														</div>
													</div>
												</div>
											{/if}
										{/key}

									<button
										class="btn btn-primary btn-sm gap-2"
										onclick={(e) => {
											e.preventDefault();
											abrirModalImpressao(grupo.funcionarioId);
										}}
									>
										<Printer class="h-4 w-4" />
										Imprimir Ficha
									</button>
									</div>
								</div>

								<div
									class="border-base-300 bg-base-100/50 max-h-[600px] overflow-x-auto overflow-y-auto rounded-xl border shadow-inner"
								>
									<table class="table-zebra table w-full">
										<thead
											class="from-base-300/95 to-base-200/95 sticky top-0 z-10 bg-gradient-to-r shadow-md backdrop-blur-sm"
										>
											<tr>
												<th
													class="text-base-content border-base-400 border-b text-sm font-bold whitespace-nowrap"
													>Data</th
												>
												<th
													class="text-base-content border-base-400 border-b text-sm font-bold whitespace-nowrap"
													>Tipo</th
												>
												<th
													class="text-base-content border-base-400 border-b text-sm font-bold whitespace-nowrap"
													>Horário</th
												>
												<th
													class="text-base-content border-base-400 border-b text-sm font-bold whitespace-nowrap"
													>Saldo Parcial</th
												>
												<th
													class="text-base-content border-base-400 border-b text-sm font-bold whitespace-nowrap"
													>Saldo Diário</th
												>
												<th
													class="text-base-content border-base-400 border-b text-sm font-bold whitespace-nowrap"
													>Localização</th
												>
												<th
													class="text-base-content border-base-400 border-b text-sm font-bold whitespace-nowrap"
													>Status</th
												>
												<th
													class="text-base-content border-base-400 border-b text-sm font-bold whitespace-nowrap"
													>Ações</th
												>
											</tr>
										</thead>
										<tbody>
											{#each Object.values(grupo.registrosPorData) as grupoData, dataIndex}
												{@const totalRegistros = grupoData.registros.length}
												{@const dataFormatada = formatarDataDDMMAAAA(grupoData.data)}
												{@const saldosParciais = calcularSaldosParciais(grupoData.registros)}
												{@const isUltimoDia =
													dataIndex === Object.values(grupo.registrosPorData).length - 1}
												{#each grupoData.registros as registro, index}
													{@const saldoParcial = saldosParciais.get(index)}
													<tr
														class="hover:bg-base-200/50 transition-colors {dataIndex % 2 === 0
															? 'bg-base-100/40'
															: 'bg-base-50/60'} {!isUltimoDia && index === totalRegistros - 1
															? 'border-base-300 border-b-4'
															: ''}"
													>
														<td class="text-sm font-semibold whitespace-nowrap">{dataFormatada}</td>
														<td class="whitespace-nowrap">
															<span class="badge badge-outline badge-sm text-xs font-medium">
																{config
																	? getTipoRegistroLabel(registro.tipo, {
																			nomeEntrada: config.nomeEntrada,
																			nomeSaidaAlmoco: config.nomeSaidaAlmoco,
																			nomeRetornoAlmoco: config.nomeRetornoAlmoco,
																			nomeSaida: config.nomeSaida
																		})
																	: getTipoRegistroLabel(registro.tipo)}
															</span>
														</td>
														<td class="font-mono text-sm font-medium whitespace-nowrap"
															>{formatarHoraPonto(registro.hora, registro.minuto)}</td
														>
														<td class="whitespace-nowrap">
															{#if saldoParcial}
																<span
																	class="badge badge-info badge-sm text-xs font-semibold shadow-sm"
																>
																	Par {saldoParcial.parNumero}: +{saldoParcial.horas}h {saldoParcial.minutos}min
																</span>
															{:else}
																<span class="text-base-content/30 text-sm">-</span>
															{/if}
														</td>
														{#if index === 0}
															<td class="whitespace-nowrap" rowspan={totalRegistros}>
																{#if grupoData.saldoDiarioComparativo}
																	<SaldoDiarioComparativoBadge
																		saldo={grupoData.saldoDiarioComparativo}
																		size="md"
																	/>
																{:else if grupoData.saldoDiario}
																	<SaldoDiarioBadge saldo={grupoData.saldoDiario} size="md" />
																{:else}
																	<span class="badge badge-ghost badge-lg">-</span>
																{/if}
															</td>
														{/if}
														<td class="whitespace-nowrap">
															<LocalizacaoIcon dentroRaioPermitido={registro.dentroRaioPermitido} />
														</td>
														<td class="whitespace-nowrap">
															<span
																class="badge badge-sm font-semibold {registro.dentroDoPrazo
																	? 'badge-success shadow-sm'
																	: 'badge-error shadow-sm'}"
															>
																{registro.dentroDoPrazo ? '✓ Dentro do Prazo' : '✗ Fora do Prazo'}
															</span>
														</td>
														<td class="whitespace-nowrap">
															<button
																class="btn btn-sm btn-outline btn-primary hover:btn-primary gap-2 text-xs transition-all hover:shadow-md"
																onclick={() => abrirModalDetalhes(registro._id)}
																title="Ver Detalhes"
															>
																<FileText class="h-3 w-3" />
																<span class="text-xs">Detalhes</span>
															</button>
														</td>
													</tr>
												{/each}
											{/each}
										</tbody>
									</table>
								</div>
							</div>
						</div>
					{/each}
				</div>
			{/if}
		</div>
	</div>
</div>

{#if mostrarModalImpressao && funcionarioParaImprimir}
	<PrintPontoModal
		funcionarioId={funcionarioParaImprimir}
		onClose={() => {
			mostrarModalImpressao = false;
			funcionarioParaImprimir = '';
		}}
		onGenerate={gerarPDFComSelecaoWrapper}
	/>
{/if}

<!-- Modal de Detalhes do Registro -->
{#if mostrarModalDetalhes && registroDetalhesId}
	{@const registroDetalhesQuery = useQuery(
		api.pontos.obterRegistro,
		registroDetalhesId ? { registroId: registroDetalhesId } : 'skip'
	)}
	{@const registroDetalhes = registroDetalhesQuery?.data}

	<dialog
		class="modal modal-open"
		onclick={(e) => e.target === e.currentTarget && fecharModalDetalhes()}
	>
		<div
			class="modal-box flex max-h-[90vh] max-w-4xl flex-col overflow-hidden"
			onclick={(e) => e.stopPropagation()}
		>
			<div
				class="border-base-300 mb-4 flex flex-shrink-0 items-center justify-between border-b pb-4"
			>
				<h3 class="text-xl font-bold">Detalhes do Registro de Ponto</h3>
				<button class="btn btn-sm btn-circle btn-ghost" onclick={fecharModalDetalhes}>
					<X class="h-4 w-4" />
				</button>
			</div>

			<div class="flex-1 overflow-y-auto pr-2">
				{#if registroDetalhesQuery === undefined || registroDetalhesQuery?.isLoading}
					<div class="flex items-center justify-center py-8">
						<span class="loading loading-spinner loading-lg text-primary"></span>
						<span class="ml-4">Carregando detalhes...</span>
					</div>
				{:else if registroDetalhesQuery?.error}
					<div class="alert alert-error">
						<XCircle class="h-6 w-6" />
						<div>
							<h3 class="font-bold">Erro ao carregar detalhes</h3>
							<div class="mt-1 text-sm">
								{registroDetalhesQuery.error?.message ||
									String(registroDetalhesQuery.error) ||
									'Erro desconhecido'}
							</div>
						</div>
					</div>
				{:else if !registroDetalhes}
					<div class="alert alert-warning">
						<FileText class="h-6 w-6" />
						<span>Registro não encontrado</span>
					</div>
				{:else}
					<!-- Informações Básicas -->
					<div class="card bg-base-200 mb-4">
						<div class="card-body">
							<h4 class="mb-2 font-bold">Informações do Registro</h4>
							{#if registroDetalhes.funcionario}
								<p><strong>Funcionário:</strong> {registroDetalhes.funcionario.nome}</p>
								{#if registroDetalhes.funcionario.matricula}
									<p><strong>Matrícula:</strong> {registroDetalhes.funcionario.matricula}</p>
								{/if}
							{/if}
							<p><strong>Data:</strong> {formatarDataDDMMAAAA(registroDetalhes.data)}</p>
							<p>
								<strong>Horário:</strong>
								{formatarHoraPonto(registroDetalhes.hora, registroDetalhes.minuto)}
							</p>
							<p>
								<strong>Status:</strong>
								<span
									class="badge {registroDetalhes.dentroDoPrazo ? 'badge-success' : 'badge-error'}"
									>{registroDetalhes.dentroDoPrazo ? 'Dentro do Prazo' : 'Fora do Prazo'}</span
								>
							</p>
						</div>
					</div>

					<!-- Localização GPS -->
					{#if registroDetalhes.latitude !== undefined && registroDetalhes.longitude !== undefined}
						<div class="card bg-base-200 mb-4">
							<div class="card-body">
								<h4 class="mb-2 font-bold">Localização GPS</h4>
								<p><strong>Latitude:</strong> {registroDetalhes.latitude.toFixed(6)}</p>
								<p><strong>Longitude:</strong> {registroDetalhes.longitude.toFixed(6)}</p>
								{#if registroDetalhes.precisao !== undefined}
									<p><strong>Precisão:</strong> {registroDetalhes.precisao.toFixed(2)}m</p>
								{/if}
								{#if registroDetalhes.endereco || registroDetalhes.cidade}
									<p>
										<strong>Endereço:</strong>
										{registroDetalhes.endereco || ''}
										{registroDetalhes.cidade ? `, ${registroDetalhes.cidade}` : ''}
										{registroDetalhes.estado ? ` - ${registroDetalhes.estado}` : ''}
									</p>
								{/if}
								{#if registroDetalhes.confiabilidadeGPS !== undefined}
									<p>
										<strong>Confiabilidade GPS:</strong>
										{(registroDetalhes.confiabilidadeGPS * 100).toFixed(1)}%
									</p>
								{/if}
								{#if registroDetalhes.scoreConfiancaBackend !== undefined}
									<p>
										<strong>Score de Confiança:</strong>
										{(registroDetalhes.scoreConfiancaBackend * 100).toFixed(1)}%
									</p>
								{/if}
							</div>
						</div>
					{/if}

					<!-- Dados de Sensores (Acelerômetro) -->
					{#if registroDetalhes.acelerometroX !== undefined || registroDetalhes.sensorDisponivel !== undefined}
						<div class="card bg-base-200 mb-4">
							<div class="card-body">
								<h4 class="mb-2 font-bold">Dados de Sensores</h4>
								{#if registroDetalhes.sensorDisponivel === false && registroDetalhes.isDesktop !== true}
									<p class="text-warning">
										<strong>Sensor:</strong> Não disponível neste dispositivo
									</p>
								{:else if registroDetalhes.permissaoSensorNegada === true}
									<p class="text-error"><strong>Sensor:</strong> Permissão negada</p>
								{:else if registroDetalhes.acelerometroX !== undefined}
									<p><strong>Sensor:</strong> Disponível</p>
									<p>
										<strong>Acelerômetro X:</strong>
										{registroDetalhes.acelerometroX.toFixed(3)} m/s²
									</p>
									{#if registroDetalhes.acelerometroY !== undefined}
										<p>
											<strong>Acelerômetro Y:</strong>
											{registroDetalhes.acelerometroY.toFixed(3)} m/s²
										</p>
									{/if}
									{#if registroDetalhes.acelerometroZ !== undefined}
										<p>
											<strong>Acelerômetro Z:</strong>
											{registroDetalhes.acelerometroZ.toFixed(3)} m/s²
										</p>
									{/if}
									{#if registroDetalhes.magnitudeMovimento !== undefined}
										<p>
											<strong>Magnitude:</strong>
											{registroDetalhes.magnitudeMovimento.toFixed(3)} m/s²
										</p>
									{/if}
									{#if registroDetalhes.movimentoDetectado !== undefined}
										<p>
											<strong>Movimento Detectado:</strong>
											<span
												class="badge {registroDetalhes.movimentoDetectado
													? 'badge-success'
													: 'badge-warning'}"
												>{registroDetalhes.movimentoDetectado ? 'Sim' : 'Não'}</span
											>
										</p>
									{/if}
									{#if registroDetalhes.variacaoAcelerometro !== undefined}
										<p>
											<strong>Variação:</strong>
											{registroDetalhes.variacaoAcelerometro.toFixed(6)}
										</p>
									{/if}
								{:else if registroDetalhes.isDesktop === true}
									<p class="text-info">
										<strong>Sensor:</strong> Não disponível em desktop (normal)
									</p>
								{/if}
							</div>
						</div>
					{/if}
				{/if}
			</div>

			<div class="border-base-300 mt-4 flex flex-shrink-0 justify-end gap-2 border-t pt-4">
				{#if registroDetalhes}
					<button
						class="btn btn-primary gap-2"
						onclick={() => imprimirDetalhesRegistroWrapper(registroDetalhesId)}
					>
						<Printer class="h-4 w-4" />
						Imprimir PDF
					</button>
				{/if}
				<button class="btn btn-outline" onclick={fecharModalDetalhes}>Fechar</button>
			</div>
		</div>
		<form method="dialog" class="modal-backdrop" onclick={fecharModalDetalhes}></form>
	</dialog>
{/if}
