import { useCallback, useMemo } from 'react';
import type { Identifier } from '../../types/identifiers';


type ApiClient = {
  post: (path: string, body?: unknown) => Promise<{ data: unknown }>;
  delete: (path: string) => Promise<{ data: unknown }>;
};

interface CorrespondentOption {
  id?: string;
  name?: string;
  [key: string]: unknown;
}

interface UseDocumentCorrespondentActionsArgs {
  apiClient: ApiClient;
  correspondents: CorrespondentOption[];
  handleCorrespondentCreate: (payload: { name: string }) => Promise<CorrespondentOption | null>;
  notifyApiError: (error: unknown, fallback: string) => void;
  setStatusMessage: (message: string, variant?: string) => void;
  updateDocumentCaches?: (
    id: Identifier,
    updater: (doc: { correspondents?: CorrespondentOption[] } | null) => { correspondents?: CorrespondentOption[] } | null,
  ) => void;
}

const useDocumentCorrespondentActions = ({
  apiClient,
  correspondents,
  handleCorrespondentCreate,
  notifyApiError,
  setStatusMessage,
  updateDocumentCaches,
}: 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,
        correspondent,
      }: { documentId: Identifier; correspondentId: Identifier; correspondent?: CorrespondentOption | null },
      { notify = true }: { notify?: boolean } = {},
    ) => {
      if (documentId == null || correspondentId == null) {
        throw new Error('Missing document or correspondent.');
      }
      try {
        await apiClient.post(`/documents/${documentId}/correspondents`, {
          assignments: [{ correspondent_id: correspondentId }],
          replace: false,
        });
        if (updateDocumentCaches) {
          const resolved = correspondent
            || correspondents.find((entry) => entry?.id === correspondentId)
            || null;
          updateDocumentCaches(documentId, (doc) => {
            if (!doc) {
              return doc;
            }
            const current = Array.isArray(doc.correspondents) ? doc.correspondents : [];
            if (current.some((entry) => entry?.id === correspondentId)) {
              return doc;
            }
            const nextEntry = resolved?.name
              ? { id: resolved.id ?? correspondentId, name: resolved.name }
              : { id: correspondentId };
            return { ...doc, correspondents: [...current, nextEntry] };
          });
        }
        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, correspondents, notifyApiError, setStatusMessage, updateDocumentCaches],
  );

  const handleCorrespondentRemove = useCallback(
    async (
      { documentId, correspondentId }: { documentId: Identifier; correspondentId: Identifier },
      { notify = true }: { notify?: boolean } = {},
    ) => {
      if (documentId == null || correspondentId == null) {
        throw new Error('Missing document or correspondent.');
      }
      try {
        await apiClient.delete(`/documents/${documentId}/correspondents/${correspondentId}`);
        if (updateDocumentCaches) {
          updateDocumentCaches(documentId, (doc) => {
            if (!doc || !Array.isArray(doc.correspondents)) {
              return doc;
            }
            const filtered = doc.correspondents.filter((entry) => entry?.id !== correspondentId);
            return filtered.length === doc.correspondents.length ? doc : { ...doc, correspondents: filtered };
          });
        }
        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, notifyApiError, setStatusMessage, updateDocumentCaches],
  );

  const normalizeOption = (
    option: CorrespondentOption | string | null,
  ): CorrespondentOption | null => {
    if (!option) {
      return null;
    }
    if (typeof option === 'string') {
      const trimmed = option.trim();
      if (trimmed) {
        return { id: null, name: trimmed };
      }
      return null;
    }
    return option;
  };

  const handleCorrespondentAdd = useCallback(
    async ({ document, name, input = null, option = null }: { document?: { id?: string }; 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,
          correspondent: target.name ? target : { ...target, name: trimmed },
        });
        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;
