import { useCallback, useMemo } from 'react'; import { isPlainObject, isStringValue } from '../../utils/typeGuards'; type ApiClient = { post: (path: string, body?: unknown) => Promise<{ data: unknown }>; delete: (path: string) => Promise<{ data: unknown }>; }; interface CorrespondentOption { id?: string | number; name?: string; [key: string]: unknown; } interface UseDocumentCorrespondentActionsArgs { apiClient: ApiClient; correspondents: CorrespondentOption[]; handleCorrespondentCreate: (payload: { name: string }) => Promise; refreshCurrentFolder: () => Promise; notifyApiError: (error: unknown, fallback: string) => void; setStatusMessage: (message: string, variant?: string) => void; } const useDocumentCorrespondentActions = ({ apiClient, correspondents, handleCorrespondentCreate, refreshCurrentFolder, notifyApiError, setStatusMessage, }: UseDocumentCorrespondentActionsArgs) => { const correspondentLookupByName = useMemo(() => { const map = new Map(); correspondents.forEach((correspondent) => { if (correspondent?.name) { map.set(correspondent.name.toLowerCase(), correspondent); } }); return map; }, [correspondents]); const handleDocumentCorrespondentAttach = useCallback( async ( { documentId, correspondentId }: { documentId?: string | number | null; correspondentId?: string | number | null }, { notify = true, refresh = true }: { notify?: boolean; refresh?: boolean } = {}, ) => { if (!documentId || !correspondentId) { throw new Error('Missing document or correspondent.'); } try { await apiClient.post(`/documents/${documentId}/correspondents`, { assignments: [{ correspondent_id: correspondentId }], replace: false, }); if (refresh) { await refreshCurrentFolder(); } if (notify) { setStatusMessage('Correspondent assigned.', 'success'); } return true; } catch (error) { const message = error.response?.data?.error || 'Failed to assign correspondent.'; notifyApiError(error, message); throw new Error(message); } }, [apiClient, refreshCurrentFolder, notifyApiError, setStatusMessage], ); const handleCorrespondentRemove = useCallback( async ( { documentId, correspondentId }: { documentId?: string | number | null; correspondentId?: string | number | null }, { notify = true, refresh = true }: { notify?: boolean; refresh?: boolean } = {}, ) => { if (!documentId || !correspondentId) { throw new Error('Missing document or correspondent.'); } try { await apiClient.delete(`/documents/${documentId}/correspondents/${correspondentId}`); if (refresh) { await refreshCurrentFolder(); } if (notify) { setStatusMessage('Correspondent removed.', 'success'); } return true; } catch (error) { const message = error.response?.data?.error || 'Failed to remove correspondent.'; notifyApiError(error, message); throw new Error(message); } }, [apiClient, refreshCurrentFolder, notifyApiError, setStatusMessage], ); const normalizeOption = ( option: CorrespondentOption | string | null, ): CorrespondentOption | null => { if (!option) { return null; } if (isPlainObject(option) && 'id' in option) { return option as CorrespondentOption; } if (isStringValue(option)) { const trimmed = option.trim(); if (trimmed) { return { id: null, name: trimmed }; } } return null; }; const handleCorrespondentAdd = useCallback( async ({ document, name, input = null, option = null }: { document?: { id?: string | number }; name?: string; input?: HTMLInputElement | null; option?: CorrespondentOption | string | null }) => { if (!document?.id) { throw new Error('Missing document for correspondent assignment.'); } const trimmed = name?.trim?.() || ''; if (!trimmed) { setStatusMessage('Correspondent name is required.', 'error'); return; } let target = correspondentLookupByName.get(trimmed.toLowerCase()) || normalizeOption(option); if (!target) { try { target = await handleCorrespondentCreate({ name: trimmed }); } catch { return; } } if (!target?.id) { setStatusMessage('Unable to resolve correspondent.', 'error'); return; } try { await handleDocumentCorrespondentAttach({ documentId: document.id, correspondentId: target.id, }); if (input) { input.value = ''; } } catch (error) { setStatusMessage('Failed to assign correspondent.', 'error'); console.error('[documents] assign correspondent failed', error); } }, [ correspondentLookupByName, handleCorrespondentCreate, handleDocumentCorrespondentAttach, setStatusMessage, ], ); return { correspondentLookupByName, handleDocumentCorrespondentAttach, handleCorrespondentRemove, handleCorrespondentAdd, }; }; export default useDocumentCorrespondentActions;