import { useEffect, useState, useCallback, useRef } from 'react'; import { useTranslation } from 'react-i18next'; import { useNotificationStore } from '@/stores'; import { usageApi } from '@/services/api/usage'; import { loadModelPrices, saveModelPrices, type ModelPrice } from '@/utils/usage'; export interface UsagePayload { total_requests?: number; success_count?: number; failure_count?: number; total_tokens?: number; apis?: Record; [key: string]: unknown; } export interface UseUsageDataReturn { usage: UsagePayload | null; loading: boolean; error: string; modelPrices: Record; setModelPrices: (prices: Record) => void; loadUsage: () => Promise; handleExport: () => Promise; handleImport: () => void; handleImportChange: (event: React.ChangeEvent) => Promise; importInputRef: React.RefObject; exporting: boolean; importing: boolean; } export function useUsageData(): UseUsageDataReturn { const { t } = useTranslation(); const { showNotification } = useNotificationStore(); const [usage, setUsage] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); const [modelPrices, setModelPrices] = useState>({}); const [exporting, setExporting] = useState(false); const [importing, setImporting] = useState(false); const importInputRef = useRef(null); const loadUsage = useCallback(async () => { setLoading(true); setError(''); try { const data = await usageApi.getUsage(); const payload = data?.usage ?? data; setUsage(payload); } catch (err: unknown) { const message = err instanceof Error ? err.message : t('usage_stats.loading_error'); setError(message); } finally { setLoading(false); } }, [t]); useEffect(() => { loadUsage(); setModelPrices(loadModelPrices()); }, [loadUsage]); const handleExport = async () => { setExporting(true); try { const data = await usageApi.exportUsage(); const exportedAt = typeof data?.exported_at === 'string' ? new Date(data.exported_at) : new Date(); const safeTimestamp = Number.isNaN(exportedAt.getTime()) ? new Date().toISOString() : exportedAt.toISOString(); const filename = `usage-export-${safeTimestamp.replace(/[:.]/g, '-')}.json`; const blob = new Blob([JSON.stringify(data ?? {}, null, 2)], { type: 'application/json' }); const url = window.URL.createObjectURL(blob); const link = document.createElement('a'); link.href = url; link.download = filename; link.click(); window.URL.revokeObjectURL(url); showNotification(t('usage_stats.export_success'), 'success'); } catch (err: unknown) { const message = err instanceof Error ? err.message : ''; showNotification( `${t('notification.download_failed')}${message ? `: ${message}` : ''}`, 'error' ); } finally { setExporting(false); } }; const handleImport = () => { importInputRef.current?.click(); }; const handleImportChange = async (event: React.ChangeEvent) => { const file = event.target.files?.[0]; event.target.value = ''; if (!file) return; setImporting(true); try { const text = await file.text(); let payload: unknown; try { payload = JSON.parse(text); } catch { showNotification(t('usage_stats.import_invalid'), 'error'); return; } const result = await usageApi.importUsage(payload); showNotification( t('usage_stats.import_success', { added: result?.added ?? 0, skipped: result?.skipped ?? 0, total: result?.total_requests ?? 0, failed: result?.failed_requests ?? 0 }), 'success' ); await loadUsage(); } catch (err: unknown) { const message = err instanceof Error ? err.message : ''; showNotification( `${t('notification.upload_failed')}${message ? `: ${message}` : ''}`, 'error' ); } finally { setImporting(false); } }; const handleSetModelPrices = useCallback((prices: Record) => { setModelPrices(prices); saveModelPrices(prices); }, []); return { usage, loading, error, modelPrices, setModelPrices: handleSetModelPrices, loadUsage, handleExport, handleImport, handleImportChange, importInputRef, exporting, importing }; }