import { useState, useRef, useCallback } from 'react'; import { useAuthStore } from '@/features/auth/store/authStore'; import { apiClient } from '@/services/api/client'; import { parseApiError } from '@/utils/apiErrorHandler'; import { useToast } from '@/hooks/useToast'; const MAX_RETRY = 3; export function useAccountSettings() { const { logout } = useAuthStore(); const toast = useToast(); const [isChangingPassword, setIsChangingPassword] = useState(false); const [isDeletingAccount, setIsDeletingAccount] = useState(false); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [currentPassword, setCurrentPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [passwordError, setPasswordError] = useState(''); const [deletePassword, setDeletePassword] = useState(''); const [deleteReason, setDeleteReason] = useState(''); const [deleteConfirmText, setDeleteConfirmText] = useState(''); const [deleteValidationError, setDeleteValidationError] = useState(null); const [mutationError, setMutationError] = useState(null); const [retryCount, setRetryCount] = useState(0); const lastMutationRef = useRef<(() => Promise) | null>(null); const handleChangePassword = useCallback( async (e: React.FormEvent) => { e.preventDefault(); setPasswordError(''); if (newPassword !== confirmPassword) { setPasswordError('New passwords do not match'); return; } if (newPassword.length < 12) { setPasswordError('Password must be at least 12 characters long'); return; } const performMutation = async () => { await apiClient.put('/users/me/password', { current_password: currentPassword, new_password: newPassword, }); toast.success('Password changed successfully'); setCurrentPassword(''); setNewPassword(''); setConfirmPassword(''); setMutationError(null); setRetryCount(0); lastMutationRef.current = null; }; lastMutationRef.current = performMutation; setIsChangingPassword(true); try { await performMutation(); } catch (error: unknown) { const apiError = parseApiError(error); setPasswordError(apiError.message); setMutationError(new Error(apiError.message)); } finally { setIsChangingPassword(false); } }, [ currentPassword, newPassword, confirmPassword, toast, ], ); const handleDeleteAccount = useCallback(async () => { setDeleteValidationError(null); setMutationError(null); if (deleteConfirmText !== 'DELETE') { setDeleteValidationError('Please type DELETE to confirm'); return; } try { setIsDeletingAccount(true); await apiClient.delete('/users/me', { data: { password: deletePassword, reason: deleteReason, confirm_text: deleteConfirmText, }, }); toast.success('Account deletion requested. You will be logged out.'); setTimeout(() => { logout(); window.location.href = '/login'; }, 2000); } catch (error: unknown) { const apiError = parseApiError(error); setMutationError(new Error(apiError.message)); } finally { setIsDeletingAccount(false); setIsDeleteDialogOpen(false); } }, [deletePassword, deleteReason, deleteConfirmText, toast, logout]); const handleExportData = useCallback(async () => { const performMutation = async () => { const response = await apiClient.get('/users/me/export', { responseType: 'blob', }); const url = window.URL.createObjectURL(new Blob([response.data])); const link = document.createElement('a'); link.href = url; link.setAttribute( 'download', `veza-data-export-${new Date().toISOString()}.json`, ); document.body.appendChild(link); link.click(); link.remove(); window.URL.revokeObjectURL(url); toast.success('Data export started'); setMutationError(null); setRetryCount(0); lastMutationRef.current = null; }; lastMutationRef.current = performMutation; try { await performMutation(); } catch (error: unknown) { const apiError = parseApiError(error); setMutationError(new Error(apiError.message)); } }, [toast]); const handleRetry = useCallback(async () => { if (!lastMutationRef.current || retryCount >= MAX_RETRY) return; setRetryCount((prev) => prev + 1); try { await lastMutationRef.current(); } catch { // Error handled by mutation } }, [retryCount]); const dismissError = useCallback(() => { setMutationError(null); setRetryCount(0); lastMutationRef.current = null; }, []); return { mutationError, retryCount, maxRetry: MAX_RETRY, handleRetry, dismissError, isChangingPassword, currentPassword, setCurrentPassword, newPassword, setNewPassword, confirmPassword, setConfirmPassword, passwordError, handleChangePassword, isDeleteDialogOpen, setIsDeleteDialogOpen, isDeletingAccount, deletePassword, setDeletePassword, deleteReason, setDeleteReason, deleteConfirmText, setDeleteConfirmText, deleteValidationError, setDeleteValidationError, handleDeleteAccount, handleExportData, }; }