import { useMutation, useQuery, useQueryClient, type MutateOptions } from '@tanstack/react-query';
import { api } from '@/lib/api';
import { isCommandCurrent, requireCommandGeneration, requireSelectedStore } from '@/lib/stores/store-context';
import { useAuthStore } from '@/stores/auth';
import type { Message, MessageList, PostMessageBody } from '@alqove/api-client';

export const MESSAGE_KEYS = {
  thread: (orderId: string, storeId?: string | null) => storeId !== undefined ? ['messages', 'store', storeId, orderId] as const : ['messages', orderId] as const,
};

export function useMessages(orderId: string, opts?: { enabled?: boolean; storeId?: string | null }) {
  const storeId = opts?.storeId;
  return useQuery({
    queryKey: MESSAGE_KEYS.thread(orderId, storeId),
    queryFn: () => storeId !== undefined ? api.messages.forStore(requireSelectedStore(storeId)).list(orderId) : api.messages.list(orderId),
    staleTime: 15_000,
    refetchInterval: 30_000,
    refetchOnWindowFocus: true,
    enabled: storeId !== null && (opts?.enabled ?? Boolean(orderId)),
  });
}

export function usePostMessage(orderId: string, storeId?: string | null) {
  const qc = useQueryClient();
  const generation = useAuthStore(state => state.commandGeneration);
  type Command = { body: PostMessageBody; generation: number; storeId: typeof storeId; orderId: string };
  const mutation = useMutation({
    mutationFn: ({ body, generation, storeId, orderId }: Command) => {
      requireCommandGeneration(generation);
      return storeId !== undefined ? api.messages.forStore(requireSelectedStore(storeId, 'store.admin', generation)).post(orderId, body) : api.messages.post(orderId, body);
    },
    onMutate: async ({ body, generation, storeId, orderId }: Command) => {
      requireCommandGeneration(generation);
      if (storeId !== undefined) requireSelectedStore(storeId, 'store.admin', generation);
      await qc.cancelQueries({ queryKey: MESSAGE_KEYS.thread(orderId, storeId) });
      requireCommandGeneration(generation);
      if (storeId !== undefined) requireSelectedStore(storeId, 'store.admin', generation);
      const previous = qc.getQueryData<MessageList>(MESSAGE_KEYS.thread(orderId, storeId));

      const optimistic: Message = {
        id: `optimistic-${Date.now()}`,
        thread_id: 'pending',
        author_user_id: null,
        author_role: storeId !== undefined ? 'seller' : 'buyer',
        body: body.body,
        attachments: [],
        created_at: new Date().toISOString(),
        deleted_at: null,
        deleted_by_user_id: null,
        deleted_by_admin: false,
      };

      qc.setQueryData<MessageList>(MESSAGE_KEYS.thread(orderId, storeId), {
        data: [...(previous?.data ?? []), optimistic],
        meta: { total: (previous?.meta.total ?? 0) + 1, has_more: false },
      });

      return { previous };
    },
    onError: (_err, { storeId, orderId, generation }, context) => {
      if (isCommandCurrent(generation) && context?.previous) {
        qc.setQueryData(MESSAGE_KEYS.thread(orderId, storeId), context.previous);
      }
    },
    onSettled: (_data, _error, { storeId, orderId, generation }, context) => {
      if (!context) return; // Rejected before optimism/dispatch: nothing to settle.
      const current = isCommandCurrent(generation);
      const refetchType = current ? 'active' : 'none';
      qc.invalidateQueries({ queryKey: MESSAGE_KEYS.thread(orderId, storeId), refetchType });
      if (current) qc.invalidateQueries({ queryKey: ['notifications', 'unread-count'] });
      qc.invalidateQueries({ queryKey: storeId !== undefined ? ['store', storeId, 'threads'] : ['me', 'threads'], refetchType });
    },
  });
  type Options = MutateOptions<Awaited<ReturnType<typeof api.messages.post>>, Error, PostMessageBody, { previous: MessageList | undefined }>;
  // Observer callbacks affect the visible composer; settlement above belongs to
  // the originating command even if React Query replaces options on a rerender.
  const guardOptions = (options?: Options): Parameters<typeof mutation.mutate>[1] => ({
    onSuccess: (data, command, context, mutationContext) => {
      if (isCommandCurrent(command.generation)) options?.onSuccess?.(data, command.body, context, mutationContext);
    },
    onError: (error, command, context, mutationContext) => {
      if (isCommandCurrent(command.generation)) options?.onError?.(error, command.body, context, mutationContext);
    },
    onSettled: (data, error, command, context, mutationContext) => {
      if (isCommandCurrent(command.generation)) options?.onSettled?.(data, error, command.body, context, mutationContext);
    },
  });
  return {
    ...mutation,
    mutate: (body: PostMessageBody, options?: Options) =>
      mutation.mutate({ body, generation, storeId, orderId }, guardOptions(options)),
    mutateAsync: (body: PostMessageBody, options?: Options) =>
      mutation.mutateAsync({ body, generation, storeId, orderId }, guardOptions(options)),
  };
}

export function useDeleteMessage(orderId: string, storeId?: string | null) {
  const qc = useQueryClient();
  const generation = useAuthStore(state => state.commandGeneration);
  type Command = { messageId: string; generation: number; storeId: typeof storeId; orderId: string };
  const mutation = useMutation({
    mutationFn: ({ messageId, generation, storeId }: Command) => {
      requireCommandGeneration(generation);
      return storeId !== undefined ? api.messages.forStore(requireSelectedStore(storeId, 'store.admin', generation)).delete(messageId) : api.messages.delete(messageId);
    },
    onSuccess: (_data, { storeId, orderId, generation }) => {
      const refetchType = isCommandCurrent(generation) ? 'active' : 'none';
      qc.invalidateQueries({ queryKey: MESSAGE_KEYS.thread(orderId, storeId), refetchType });
      qc.invalidateQueries({ queryKey: storeId !== undefined ? ['store', storeId, 'threads'] : ['me', 'threads'], refetchType });
    },
  });
  type Options = MutateOptions<Awaited<ReturnType<typeof api.messages.delete>>, Error, string>;
  const guardOptions = (options?: Options): Parameters<typeof mutation.mutate>[1] => ({
    onSuccess: (data, command, context, mutationContext) => {
      if (isCommandCurrent(command.generation)) options?.onSuccess?.(data, command.messageId, context, mutationContext);
    },
    onError: (error, command, context, mutationContext) => {
      if (isCommandCurrent(command.generation)) options?.onError?.(error, command.messageId, context, mutationContext);
    },
    onSettled: (data, error, command, context, mutationContext) => {
      if (isCommandCurrent(command.generation)) options?.onSettled?.(data, error, command.messageId, context, mutationContext);
    },
  });
  return {
    ...mutation,
    mutate: (messageId: string, options?: Options) => mutation.mutate({ messageId, generation, storeId, orderId }, guardOptions(options)),
    mutateAsync: (messageId: string, options?: Options) => mutation.mutateAsync({ messageId, generation, storeId, orderId }, guardOptions(options)),
  };
}
