import { useState, useEffect, useMemo, useCallback } from 'react'; import { LotomaniaGame } from '../types'; import { LotomaniaAlgorithm } from '../utils/lotomaniaAlgorithm'; import { LRUCache } from '../utils/LRUCache'; interface CacheEntry { games: LotomaniaGame[]; timestamp: number; verticalGames: LotomaniaGame[]; horizontalGames: LotomaniaGame[]; } // Cache LRU para evitar memory leaks const gameCache = new LRUCache(5); // Máximo 5 entradas const CACHE_DURATION = 5 * 60 * 1000; // 5 minutos // Cleanup automático do cache a cada 2 minutos setInterval(() => { gameCache.cleanup(CACHE_DURATION); }, 2 * 60 * 1000); export const useOptimizedAlgorithm = () => { const [algorithm] = useState(() => new LotomaniaAlgorithm()); const [allGames, setAllGames] = useState([]); const [isLoading, setIsLoading] = useState(true); const [error, setError] = useState(null); // Função otimizada para gerar jogos com cache const generateGamesOptimized = useCallback(async (): Promise => { const cacheKey = 'lotomania-games-v1'; const cached = gameCache.get(cacheKey); // Verificar cache if (cached && (Date.now() - cached.timestamp) < CACHE_DURATION) { return cached.games; } try { setIsLoading(true); setError(null); // Usar Worker para cálculos pesados (se disponível) const games = await new Promise((resolve) => { // Simular processamento assíncrono para evitar bloquear UI setTimeout(() => { try { const result = algorithm.generateAllGames(); resolve(result); } catch (err) { setError('Erro ao gerar jogos: ' + (err as Error).message); resolve([]); } }, 10); }); // Armazenar no cache const verticalGames = games.filter(g => g.type === 'vertical'); const horizontalGames = games.filter(g => g.type === 'horizontal'); gameCache.set(cacheKey, { games, timestamp: Date.now(), verticalGames, horizontalGames }); return games; } finally { setIsLoading(false); } }, [algorithm]); // Carregar jogos na inicialização useEffect(() => { generateGamesOptimized().then(setAllGames); }, [generateGamesOptimized]); // Memoizar jogos separados const verticalGames = useMemo(() => { return allGames.filter(game => game.type === 'vertical'); }, [allGames]); const horizontalGames = useMemo(() => { return allGames.filter(game => game.type === 'horizontal'); }, [allGames]); // Memoizar estatísticas const gameStatistics = useMemo(() => { if (allGames.length === 0) return null; try { return algorithm.getGameStatistics(); } catch (err) { console.error('Erro ao calcular estatísticas:', err); return null; } }, [allGames, algorithm]); // Função otimizada para buscar jogo por ID const getGameById = useCallback((id: number): LotomaniaGame | undefined => { return allGames.find(game => game.id === id); }, [allGames]); // Função otimizada para buscar jogos por tipo const getGamesByType = useCallback((type: 'vertical' | 'horizontal'): LotomaniaGame[] => { return type === 'vertical' ? verticalGames : horizontalGames; }, [verticalGames, horizontalGames]); // Função para buscar próximo/anterior jogo do mesmo tipo const getAdjacentGame = useCallback(( currentId: number, type: 'vertical' | 'horizontal', direction: 'next' | 'previous' ): LotomaniaGame | undefined => { const games = getGamesByType(type); const currentIndex = games.findIndex(game => game.id === currentId); if (currentIndex === -1) return undefined; const targetIndex = direction === 'next' ? currentIndex + 1 : currentIndex - 1; return games[targetIndex]; }, [getGamesByType]); // Função otimizada para converter jogos em números const getNumbersFromGame = useCallback((game: LotomaniaGame): number[] => { try { return algorithm.getNumbersFromGame(game); } catch (err) { console.error('Erro ao obter números do jogo:', err); return []; } }, [algorithm]); // Função para limpar cache (útil para testes) const clearCache = useCallback(() => { gameCache.clear(); }, []); // Função para verificar integridade dos dados const validateGameData = useCallback((): boolean => { if (allGames.length === 0) return false; // Verificar se todos os jogos têm IDs únicos const ids = new Set(allGames.map(g => g.id)); if (ids.size !== allGames.length) { console.warn('IDs de jogos duplicados detectados'); return false; } // Verificar se jogos verticais têm colunas marcadas const invalidVertical = verticalGames.some(g => !g.markedColumns || g.markedColumns.length === 0); if (invalidVertical) { console.warn('Jogos verticais com colunas inválidas detectados'); return false; } // Verificar se jogos horizontais têm linhas marcadas const invalidHorizontal = horizontalGames.some(g => !g.markedRows || g.markedRows.length === 0); if (invalidHorizontal) { console.warn('Jogos horizontais com linhas inválidas detectados'); return false; } return true; }, [allGames, verticalGames, horizontalGames]); // Performance monitoring const performanceMetrics = useMemo(() => { const startTime = performance.now(); const isValid = validateGameData(); const endTime = performance.now(); return { isValid, validationTime: endTime - startTime, totalGames: allGames.length, verticalCount: verticalGames.length, horizontalCount: horizontalGames.length, memoryUsage: { estimated: allGames.length * 200 // bytes aproximados por jogo } }; }, [allGames, verticalGames, horizontalGames, validateGameData]); return { // Dados allGames, verticalGames, horizontalGames, gameStatistics, // Estado isLoading, error, // Funções otimizadas getGameById, getGamesByType, getAdjacentGame, getNumbersFromGame, generateGamesOptimized, clearCache, validateGameData, // Métricas performanceMetrics, // Algoritmo (para funcionalidades avançadas) algorithm }; };