import { useState, useEffect, useRef, useCallback } from 'react'; import { invoke } from '@tauri-apps/api/core'; export type OllamaStatus = 'checking' | 'not_installed' | 'installed_stopped' | 'running'; interface UseOllamaCheckReturn { status: OllamaStatus; isChecking: boolean; check: () => Promise; startPolling: () => void; stopPolling: () => void; reset: () => void; elapsedTime: number; } const INITIAL_INTERVAL = 2000; // 2 segundos const MAX_INTERVAL = 30000; // 30 segundos const BACKOFF_MULTIPLIER = 1.5; const TOTAL_TIMEOUT = 300000; // 5 minutos export function useOllamaCheck(): UseOllamaCheckReturn { const [status, setStatus] = useState('not_installed'); const [isChecking, setIsChecking] = useState(false); const [elapsedTime, setElapsedTime] = useState(0); const intervalRef = useRef(null); const timeoutRef = useRef(null); const startTimeRef = useRef(null); const currentIntervalRef = useRef(INITIAL_INTERVAL); const attemptCountRef = useRef(0); const isPollingActiveRef = useRef(false); const elapsedTimerRef = useRef(null); const performCheck = useCallback(async (): Promise => { try { const result = await invoke<{ installed: boolean; running: boolean; status: string; }>('check_ollama_full'); const newStatus = result.status as OllamaStatus; setStatus(newStatus); // Se Ollama está rodando, polling deve parar if (newStatus === 'running' && result.installed && result.running) { return true; // Sucesso, parar polling } return false; // Continua polling } catch (error) { console.error('Failed to check ollama:', error); // Se falhar, tentar verificação robusta como fallback try { const isInstalledAndRunning = await invoke('verify_ollama_integrity_command'); if (isInstalledAndRunning) { setStatus('running'); return true; // Sucesso, parar polling } // Se verify_ollama_integrity retornou false, pode ser instalado mas não rodando // ou não instalado. Verificar separadamente para determinar status exato const installed = await invoke('check_ollama_installed'); if (!installed) { setStatus('not_installed'); return false; // Continua polling (não instalado ainda) } const running = await invoke('check_ollama_running'); if (!running) { setStatus('installed_stopped'); return false; // Continua polling (instalado mas não rodando) } setStatus('running'); return true; // Sucesso, parar polling } catch (fallbackError) { console.error('Fallback check also failed:', fallbackError); setStatus('not_installed'); return false; // Continua polling } } }, []); const startPolling = useCallback(() => { if (isPollingActiveRef.current) { return; // Já está rodando } isPollingActiveRef.current = true; setIsChecking(true); startTimeRef.current = Date.now(); attemptCountRef.current = 0; currentIntervalRef.current = INITIAL_INTERVAL; // Atualizar tempo decorrido a cada segundo elapsedTimerRef.current = setInterval(() => { if (startTimeRef.current) { const elapsed = Math.floor((Date.now() - startTimeRef.current) / 1000); setElapsedTime(elapsed); // Verificar timeout total if (elapsed * 1000 >= TOTAL_TIMEOUT) { setIsChecking(false); isPollingActiveRef.current = false; if (elapsedTimerRef.current) { clearInterval(elapsedTimerRef.current); elapsedTimerRef.current = null; } if (intervalRef.current) { clearInterval(intervalRef.current); intervalRef.current = null; } return; } } }, 1000); // Primeira verificação imediata performCheck().then((shouldStop) => { if (shouldStop) { stopPolling(); return; } // Iniciar polling com exponential backoff const scheduleNext = () => { if (!isPollingActiveRef.current) { return; } attemptCountRef.current += 1; // Calcular próximo intervalo com exponential backoff const nextInterval = Math.min( INITIAL_INTERVAL * Math.pow(BACKOFF_MULTIPLIER, attemptCountRef.current - 1), MAX_INTERVAL ); currentIntervalRef.current = nextInterval; intervalRef.current = setTimeout(async () => { if (!isPollingActiveRef.current) { return; } const shouldStop = await performCheck(); if (shouldStop) { stopPolling(); } else { scheduleNext(); } }, nextInterval); }; scheduleNext(); }); // Timeout total timeoutRef.current = setTimeout(() => { if (isPollingActiveRef.current) { console.warn('Ollama check polling timeout após 5 minutos'); stopPolling(); } }, TOTAL_TIMEOUT); }, [performCheck]); const stopPolling = useCallback(() => { isPollingActiveRef.current = false; setIsChecking(false); if (intervalRef.current) { clearTimeout(intervalRef.current); intervalRef.current = null; } if (timeoutRef.current) { clearTimeout(timeoutRef.current); timeoutRef.current = null; } if (elapsedTimerRef.current) { clearInterval(elapsedTimerRef.current); elapsedTimerRef.current = null; } }, []); const check = useCallback(async () => { // Forçar verificação manual await performCheck(); }, [performCheck]); const reset = useCallback(() => { stopPolling(); currentIntervalRef.current = INITIAL_INTERVAL; attemptCountRef.current = 0; setElapsedTime(0); startTimeRef.current = null; startPolling(); }, [stopPolling, startPolling]); // Limpar polling ao desmontar useEffect(() => { return () => { stopPolling(); }; }, [stopPolling]); // Parar polling automaticamente quando status for 'running' useEffect(() => { if (status === 'running' && isPollingActiveRef.current) { stopPolling(); } }, [status, stopPolling]); return { status, isChecking, check, startPolling, stopPolling, reset, elapsedTime, }; }