import { useMutation, useQueryClient } from '@tanstack/react-query';
import { ticketKeys } from '../keys';
import { ticketsApi } from '../apis';
import type { PostMessageResult, TicketDetail } from '../types';
import { useTicketViewer } from './useTicketViewer';

interface PostMessageVars {
  body: string;
  /** Client-generated id; the reconcile key so the optimistic bubble is never double-rendered (§3.5). */
  clientMessageId: string;
}

/**
 * The optimistic message send — the interaction that must feel instant, and a failure must never lose the
 * typed text (phase §3.3/§5 invariant).
 *
 * `onMutate` is idempotent on `clientMessageId`: a **fresh** send appends a pending bubble; a **retry** (the
 * same id already sitting in the cache with `sendStatus: 'failed'`) flips it back to `sending` in place
 * instead of appending a duplicate — so retry and first-send are the same call. `onError` does **not** roll
 * the thread back — it flips the bubble to `sendStatus: 'failed'` and leaves it in the thread with its typed
 * body, so the failed-bubble UI (retry / discard-and-retype) always has something to act on. `onSuccess`
 * replaces the bubble **by `clientMessageId`** with the server message. `onSettled` invalidates the thread,
 * the inbox lists (last-activity/unread move), and the chrome unread-total badge.
 */
export function usePostMessage(ticketId: number) {
  const queryClient = useQueryClient();
  const { role } = useTicketViewer();
  const key = ticketKeys.detail(ticketId);

  return useMutation<PostMessageResult, unknown, PostMessageVars>({
    mutationFn: ({ body, clientMessageId }) => ticketsApi.postMessage(ticketId, { body, clientMessageId }),

    onMutate: async ({ body, clientMessageId }) => {
      await queryClient.cancelQueries({ queryKey: key });
      const current = queryClient.getQueryData<TicketDetail>(key);
      if (!current) return;
      const alreadyPending = current.messages.some((m) => m.clientMessageId === clientMessageId);
      queryClient.setQueryData<TicketDetail>(key, {
        ...current,
        messages: alreadyPending
          ? current.messages.map((m) =>
              m.clientMessageId === clientMessageId ? { ...m, body, sendStatus: 'sending' as const } : m,
            )
          : [
              ...current.messages,
              {
                id: null,
                clientMessageId,
                ticketId,
                body,
                authorRole: role,
                createdAt: new Date().toISOString(),
                isMine: true,
                sendStatus: 'sending' as const,
              },
            ],
      });
    },

    onError: (_err, { clientMessageId }) => {
      const current = queryClient.getQueryData<TicketDetail>(key);
      if (!current) return;
      queryClient.setQueryData<TicketDetail>(key, {
        ...current,
        messages: current.messages.map((m) =>
          m.clientMessageId === clientMessageId ? { ...m, sendStatus: 'failed' as const } : m,
        ),
      });
    },

    onSuccess: (result, { clientMessageId }) => {
      const current = queryClient.getQueryData<TicketDetail>(key);
      if (current) {
        queryClient.setQueryData<TicketDetail>(key, {
          ...current,
          messages: current.messages.map((m) =>
            m.clientMessageId === clientMessageId
              ? { ...m, id: result.messageId, createdAt: result.sentAt, sendStatus: 'sent' }
              : m,
          ),
        });
      }
    },

    onSettled: () => {
      queryClient.invalidateQueries({ queryKey: key });
      queryClient.invalidateQueries({ queryKey: ticketKeys.lists() });
      queryClient.invalidateQueries({ queryKey: ticketKeys.unreadTotal() });
    },
  });
}
