import React from 'react';
import { render, waitFor } from '@testing-library/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { I18nextProvider } from 'react-i18next';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import '@testing-library/jest-dom';
import { useTicketRealtime } from '@/features/tickets/hooks/useTicketRealtime';
import { ticketRepliesQueryKey } from '@/features/tickets/hooks/useTicketReplies';
import { realtimeEvents } from '@/shared/realtime/realtimeChannels';
import i18n from '@/locales/i18n';
import { LocaleProvider } from '@/app/providers/LocaleProvider';

const listenHandlers: Record<string, (payload: unknown) => void> = {};

const mockChannel = {
  listen: vi.fn((event: string, handler: (payload: unknown) => void) => {
    listenHandlers[event] = handler;
  }),
  stopListening: vi.fn(),
};

const mockEcho = {
  private: vi.fn(() => mockChannel),
  leave: vi.fn(),
};

vi.mock('@/shared/realtime/echoClient', () => ({
  getEcho: () => mockEcho,
  isRealtimeEnabled: () => true,
  leaveChannel: vi.fn(),
}));

vi.mock('react-hot-toast', () => ({
  default: vi.fn(),
}));

function TestHarness({ ticketId }: { ticketId: string }) {
  useTicketRealtime(ticketId);
  return null;
}

describe('useTicketRealtime', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    Object.keys(listenHandlers).forEach((key) => delete listenHandlers[key]);
    void i18n.changeLanguage('en');
  });

  it('invalidates replies query when reply event is received', async () => {
    const queryClient = new QueryClient();
    const invalidateSpy = vi.spyOn(queryClient, 'invalidateQueries');

    render(
      <QueryClientProvider client={queryClient}>
        <I18nextProvider i18n={i18n}>
          <LocaleProvider>
            <TestHarness ticketId="ticket-1" />
          </LocaleProvider>
        </I18nextProvider>
      </QueryClientProvider>,
    );

    await waitFor(() => {
      expect(mockEcho.private).toHaveBeenCalledWith('ticket.ticket-1');
    });

    listenHandlers[realtimeEvents.replyAdded]?.({ id: 'reply-1', ticket_id: 'ticket-1' });

    await waitFor(() => {
      expect(invalidateSpy).toHaveBeenCalledWith({
        queryKey: ticketRepliesQueryKey('ticket-1'),
      });
    });
  });

  it('cleans up listeners on unmount', async () => {
    const queryClient = new QueryClient();
    const { unmount } = render(
      <QueryClientProvider client={queryClient}>
        <I18nextProvider i18n={i18n}>
          <LocaleProvider>
            <TestHarness ticketId="ticket-1" />
          </LocaleProvider>
        </I18nextProvider>
      </QueryClientProvider>,
    );

    await waitFor(() => {
      expect(mockChannel.listen).toHaveBeenCalled();
    });

    unmount();

    expect(mockChannel.stopListening).toHaveBeenCalledWith(realtimeEvents.replyAdded);
  });
});
