import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import { useShoppingListSync } from '../../src/lib/useShoppingListSync';

vi.mock('@/services/shopping-lists', () => ({
  getShoppingListSyncSocketUrl: vi.fn(() => 'ws://localhost/sync'),
}));

class MockWebSocket {
  static OPEN = 1;
  static CONNECTING = 0;
  url: string;
  readyState = 1; // OPEN
  onopen: any = null;
  onmessage: any = null;
  onerror: any = null;
  onclose: any = null;
  send = vi.fn();
  close = vi.fn();

  constructor(url: string) {
    this.url = url;
  }
}

describe('useShoppingListSync', () => {
  let originalWebSocket: any;
  let createdSockets: MockWebSocket[] = [];

  beforeEach(() => {
    vi.clearAllMocks();
    createdSockets = [];
    originalWebSocket = global.WebSocket;
    const MockClass = class extends MockWebSocket {
      constructor(url: string) {
        super(url);
        createdSockets.push(this);
      }
    };
    (MockClass as any).OPEN = 1;
    (MockClass as any).CONNECTING = 0;
    global.WebSocket = MockClass as any;
  });

  afterEach(() => {
    global.WebSocket = originalWebSocket;
  });

  it('initializes and connects to the correct socket URL', () => {
    const onSync = vi.fn();
    renderHook(() => useShoppingListSync('hh1', 'list1', onSync));
    
    expect(createdSockets).toHaveLength(1);
    expect(createdSockets[0].url).toBe('ws://localhost/sync');
  });

  it('handles incoming item_updated messages correctly', () => {
    const onSync = vi.fn();
    renderHook(() => useShoppingListSync('hh1', 'list1', onSync));
    
    const ws = createdSockets[0];
    act(() => { if (ws.onopen) ws.onopen(); });

    act(() => {
      if (ws.onmessage) ws.onmessage({ data: JSON.stringify({ type: 'ITEM_UPDATED', itemId: 'item1', updates: { checked: true } }) });
    });
    
    expect(onSync).toHaveBeenCalledWith({ type: 'ITEM_UPDATED', itemId: 'item1', updates: { checked: true } });
  });

  it('broadcasts toggle item messages when connected', () => {
    const onSync = vi.fn();
    const { result } = renderHook(() => useShoppingListSync('hh1', 'list1', onSync));
    
    const ws = createdSockets[0];
    act(() => { if (ws.onopen) ws.onopen(); });

    act(() => {
      result.current.toggleItemCheck('item1', true);
    });

    expect(ws.send).toHaveBeenCalledWith(JSON.stringify({
      type: 'TOGGLE_ITEM',
      itemId: 'item1',
      checked: true,
    }));
  });

  it('handles disconnect and reconnect backoff', () => {
    vi.useFakeTimers();
    const onSync = vi.fn();
    const { result } = renderHook(() => useShoppingListSync('hh1', 'list1', onSync));
    
    let ws = createdSockets[0];
    act(() => { if (ws.onopen) ws.onopen(); });
    expect(result.current.isConnected).toBe(true);

    act(() => { if (ws.onclose) ws.onclose({ reason: 'test' }); });
    expect(result.current.isConnected).toBe(false);

    // After 1000ms it should attempt reconnect
    act(() => { vi.advanceTimersByTime(1000); });
    expect(createdSockets).toHaveLength(2);

    vi.useRealTimers();
  });
});
