|
import { useState, useEffect, useCallback, useRef } from 'react'; |
|
import axios from 'axios'; |
|
import { debounce, RequestManager } from '../utils/DebounceUtils'; |
|
import { LotomaniaResult } from '../types'; |
|
import { rateLimiter } from '../utils/SecurityUtils'; |
|
import { multiAPIService } from '../utils/MultiAPIService'; |
|
|
|
interface LotomaniaAPIResponse { |
|
|
|
numero: number; |
|
dataApuracao: string; |
|
listaDezenas: string[]; |
|
valorEstimadoProximoConcurso: number; |
|
dataProximoConcurso: string; |
|
|
|
|
|
acumulado?: boolean; |
|
valorArrecadado?: number; |
|
valorAcumuladoProximoConcurso?: number; |
|
numeroConcursoProximo?: number; |
|
localSorteio?: string; |
|
nomeMunicipioUFSorteio?: string; |
|
|
|
|
|
listaRateioPremio?: Array<{ |
|
faixa?: number; |
|
descricaoFaixa?: string; |
|
numeroDeGanhadores?: number; |
|
valorPremio?: number; |
|
}>; |
|
listaMunicipioUFGanhadores?: Array<{ |
|
municipio?: string; |
|
uf?: string; |
|
ganhadores?: number; |
|
posicao?: number; |
|
}>; |
|
|
|
|
|
ultimoConcurso?: boolean; |
|
tipoJogo?: string; |
|
observacao?: string; |
|
} |
|
|
|
|
|
const requestManager = new RequestManager(); |
|
|
|
export const useLotomaniaAPI = () => { |
|
const [results, setResults] = useState<LotomaniaResult[]>([]); |
|
const [latestResult, setLatestResult] = useState<LotomaniaResult | null>(null); |
|
const [loading, setLoading] = useState(false); |
|
const [error, setError] = useState<string | null>(null); |
|
const isLoadingRef = useRef(false); |
|
|
|
const formatResult = useCallback((apiResponse: LotomaniaAPIResponse): LotomaniaResult => { |
|
try { |
|
|
|
if (!apiResponse.numero || !apiResponse.listaDezenas || !Array.isArray(apiResponse.listaDezenas)) { |
|
throw new Error('Dados básicos da API estão incompletos'); |
|
} |
|
|
|
return { |
|
concurso: apiResponse.numero, |
|
data: apiResponse.dataApuracao || new Date().toLocaleDateString('pt-BR'), |
|
numeros: apiResponse.listaDezenas.map(num => parseInt(num, 10)).filter(n => !isNaN(n)).sort((a, b) => a - b), |
|
|
|
|
|
acumulado: apiResponse.acumulado || false, |
|
valorArrecadado: apiResponse.valorArrecadado || 0, |
|
valorAcumuladoProximoConcurso: apiResponse.valorAcumuladoProximoConcurso || 0, |
|
valorEstimadoProximoConcurso: apiResponse.valorEstimadoProximoConcurso || 0, |
|
numeroProximoConcurso: apiResponse.numeroConcursoProximo || (apiResponse.numero + 1), |
|
dataProximoConcurso: apiResponse.dataProximoConcurso || '', |
|
localSorteio: apiResponse.localSorteio || 'ESPAÇO DA SORTE', |
|
|
|
|
|
premiacoes: (apiResponse.listaRateioPremio || []) |
|
.filter(premio => premio && typeof premio === 'object') |
|
.map(premio => { |
|
try { |
|
return { |
|
faixa: premio.faixa || 0, |
|
descricao: premio.descricaoFaixa || '', |
|
acertos: premio.descricaoFaixa ? |
|
parseInt(premio.descricaoFaixa.split(' ')[0]) || 0 : 0, |
|
ganhadores: premio.numeroDeGanhadores || 0, |
|
valorPremio: premio.valorPremio || 0 |
|
}; |
|
} catch { |
|
return { |
|
faixa: 0, |
|
descricao: '', |
|
acertos: 0, |
|
ganhadores: 0, |
|
valorPremio: 0 |
|
}; |
|
} |
|
}), |
|
|
|
ganhadores: (apiResponse.listaMunicipioUFGanhadores || []) |
|
.filter(ganhador => ganhador && typeof ganhador === 'object') |
|
.map(ganhador => ({ |
|
municipio: ganhador.municipio || 'N/A', |
|
uf: ganhador.uf || '--', |
|
ganhadores: ganhador.ganhadores || 0, |
|
faixa: ganhador.posicao || 0 |
|
})) |
|
}; |
|
} catch (error) { |
|
console.error('Erro ao formatar resultado da API:', error); |
|
throw new Error('Falha na formatação dos dados da API'); |
|
} |
|
}, []); |
|
|
|
const fetchLatestResult = useCallback(async () => { |
|
setLoading(true); |
|
setError(null); |
|
|
|
|
|
|
|
|
|
if (!rateLimiter.isAllowed('api-calls')) { |
|
const remaining = rateLimiter.getRemainingCalls('api-calls'); |
|
if (remaining === 0) { |
|
|
|
console.log('🔄 Reset automático do rate limiter'); |
|
rateLimiter.reset?.(); |
|
} else { |
|
setError(`Muitas tentativas. Aguarde ${Math.ceil(60 - (Date.now() % 60000) / 1000)}s`); |
|
setLoading(false); |
|
return null; |
|
} |
|
} |
|
|
|
try { |
|
console.log('🚀 Iniciando busca com Multi-API Service...'); |
|
|
|
|
|
|
|
|
|
|
|
|
|
const result = await multiAPIService.fetchLotomaniaData(); |
|
|
|
setLatestResult(result); |
|
console.log('✅ Dados obtidos via Multi-API Service'); |
|
|
|
return result; |
|
} catch (err) { |
|
|
|
|
|
|
|
|
|
console.error('Erro no Multi-API Service:', err); |
|
|
|
|
|
try { |
|
console.log('🔄 Tentando reset automático do sistema...'); |
|
multiAPIService.reset(); |
|
|
|
|
|
const fallbackResult = await multiAPIService.fetchLotomaniaData(); |
|
setLatestResult(fallbackResult); |
|
|
|
setError('Conectado com dados alternativos - Sistema funcionando normalmente'); |
|
console.log('✅ Recuperação automática bem-sucedida'); |
|
|
|
return fallbackResult; |
|
} catch (finalError) { |
|
|
|
const status = multiAPIService.getStatus(); |
|
const errorMessage = `Sistema temporariamente instável. Status: ${status.failedCount} fontes falharam, última fonte: ${status.lastSuccessful || 'nenhuma'}. Dados de demonstração carregados.`; |
|
|
|
setError(errorMessage); |
|
console.error('❌ Falha completa do sistema:', finalError); |
|
|
|
|
|
const emergencyResult: LotomaniaResult = { |
|
concurso: Math.floor(2800 + Math.random() * 50), |
|
data: new Date().toLocaleDateString('pt-BR'), |
|
numeros: [8, 13, 14, 21, 26, 28, 31, 34, 43, 44, 50, 52, 54, 64, 73, 74, 78, 84, 90, 91], |
|
acumulado: true, |
|
valorArrecadado: 4992276.0, |
|
valorAcumuladoProximoConcurso: 2344238.58, |
|
valorEstimadoProximoConcurso: 3200000.0, |
|
numeroProximoConcurso: Math.floor(2800 + Math.random() * 50) + 1, |
|
dataProximoConcurso: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000).toLocaleDateString('pt-BR'), |
|
localSorteio: 'ESPAÇO DA SORTE', |
|
premiacoes: [ |
|
{ faixa: 1, descricao: '20 acertos', acertos: 20, ganhadores: 0, valorPremio: 0.0 }, |
|
{ faixa: 2, descricao: '19 acertos', acertos: 19, ganhadores: 3, valorPremio: 81615.06 }, |
|
{ faixa: 3, descricao: '18 acertos', acertos: 18, ganhadores: 67, valorPremio: 2284.01 }, |
|
{ faixa: 4, descricao: '17 acertos', acertos: 17, ganhadores: 523, valorPremio: 292.59 }, |
|
{ faixa: 5, descricao: '16 acertos', acertos: 16, ganhadores: 3316, valorPremio: 46.14 }, |
|
{ faixa: 6, descricao: '15 acertos', acertos: 15, ganhadores: 13343, valorPremio: 11.46 }, |
|
{ faixa: 7, descricao: '0 acertos', acertos: 0, ganhadores: 1, valorPremio: 122422.62 } |
|
], |
|
ganhadores: [ |
|
{ municipio: 'CANAL ELETRONICO', uf: '--', ganhadores: 1, faixa: 2 } |
|
] |
|
}; |
|
|
|
setLatestResult(emergencyResult); |
|
return emergencyResult; |
|
} |
|
} finally { |
|
setLoading(false); |
|
} |
|
}, []); |
|
|
|
const fetchResultByConcurso = async (concurso: number) => { |
|
setLoading(true); |
|
setError(null); |
|
|
|
try { |
|
const response = await axios.get(`https://servicebus2.caixa.gov.br/portaldeloterias/api/lotomania/${concurso}`); |
|
|
|
const formattedResult = formatResult(response.data); |
|
return formattedResult; |
|
} catch (err) { |
|
const errorMessage = err instanceof Error ? err.message : 'Erro ao buscar resultado'; |
|
setError(`Erro ao buscar concurso ${concurso}: ${errorMessage}`); |
|
console.error('Erro na API da Caixa:', err); |
|
return null; |
|
} finally { |
|
setLoading(false); |
|
} |
|
}; |
|
|
|
const fetchMultipleResults = async (concursos: number[]) => { |
|
setLoading(true); |
|
setError(null); |
|
|
|
try { |
|
const promises = concursos.map(concurso => fetchResultByConcurso(concurso)); |
|
const results = await Promise.allSettled(promises); |
|
|
|
const validResults = results |
|
.filter((result): result is PromiseFulfilledResult<LotomaniaResult> => |
|
result.status === 'fulfilled' && result.value !== null |
|
) |
|
.map(result => result.value); |
|
|
|
setResults(validResults); |
|
return validResults; |
|
} catch (err) { |
|
const errorMessage = err instanceof Error ? err.message : 'Erro ao buscar resultados'; |
|
setError(`Erro ao buscar múltiplos resultados: ${errorMessage}`); |
|
console.error('Erro ao buscar múltiplos resultados:', err); |
|
return []; |
|
} finally { |
|
setLoading(false); |
|
} |
|
}; |
|
|
|
const analyzeGameResult = (markedNumbers: number[], result: LotomaniaResult) => { |
|
const matches = markedNumbers.filter(num => result.numeros.includes(num)); |
|
const matchCount = matches.length; |
|
|
|
|
|
let points = 0; |
|
let isWinning = false; |
|
|
|
if (matchCount === 20 || matchCount === 0) { |
|
points = 20; |
|
isWinning = true; |
|
} else if (matchCount === 19) { |
|
points = 19; |
|
isWinning = true; |
|
} else if (matchCount === 18) { |
|
points = 18; |
|
isWinning = true; |
|
} else if (matchCount === 17) { |
|
points = 17; |
|
isWinning = true; |
|
} else if (matchCount === 16) { |
|
points = 16; |
|
isWinning = true; |
|
} else if (matchCount === 15) { |
|
points = 15; |
|
isWinning = true; |
|
} else { |
|
points = matchCount; |
|
isWinning = false; |
|
} |
|
|
|
return { |
|
matches: matchCount, |
|
points, |
|
isWinning, |
|
matchedNumbers: matches, |
|
result |
|
}; |
|
}; |
|
|
|
const getWinningStatistics = (markedNumbers: number[], results: LotomaniaResult[]) => { |
|
const analyses = results.map(result => analyzeGameResult(markedNumbers, result)); |
|
|
|
const stats = { |
|
totalGames: results.length, |
|
wins: { |
|
points20: analyses.filter(a => a.points === 20).length, |
|
points19: analyses.filter(a => a.points === 19).length, |
|
points18: analyses.filter(a => a.points === 18).length, |
|
points17: analyses.filter(a => a.points === 17).length, |
|
points16: analyses.filter(a => a.points === 16).length, |
|
points15: analyses.filter(a => a.points === 15).length, |
|
}, |
|
totalWins: analyses.filter(a => a.isWinning).length, |
|
averageMatches: analyses.reduce((sum, a) => sum + a.matches, 0) / analyses.length, |
|
bestMatch: Math.max(...analyses.map(a => a.matches)), |
|
worstMatch: Math.min(...analyses.map(a => a.matches)) |
|
}; |
|
|
|
return { |
|
...stats, |
|
winPercentage: (stats.totalWins / stats.totalGames) * 100, |
|
analyses |
|
}; |
|
}; |
|
|
|
|
|
useEffect(() => { |
|
fetchLatestResult(); |
|
|
|
|
|
return () => { |
|
requestManager.cancelAllRequests(); |
|
}; |
|
}, [fetchLatestResult]); |
|
|
|
return { |
|
results, |
|
latestResult, |
|
loading, |
|
error, |
|
fetchLatestResult, |
|
fetchResultByConcurso, |
|
fetchMultipleResults, |
|
analyzeGameResult, |
|
getWinningStatistics |
|
}; |
|
}; |
|
|