import { useCallback, useMemo } from 'react';

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<CorrespondentOption | null | undefined>;
  refreshCurrentFolder: () => Promise<void>;
  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<string, CorrespondentOption>();
    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 (typeof option === 'object' && 'id' in option) {
      return option as CorrespondentOption;
    }
    if (typeof option === 'string') {
      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;
