import { defaultIfEmpty, firstValueFrom } from 'rxjs';

import { type Message } from '../../shared-kernel';
import { dispatchIncomingMessage } from './messages-incoming.handlers';

function createMessage(overrides: Partial<Message> = {}): Message {
  return {
    id: 'message-1',
    roomId: 'room-1',
    senderId: 'user-1',
    senderName: 'User 1',
    content: 'hello',
    timestamp: 1,
    reactions: [],
    isDeleted: false,
    ...overrides
  };
}

function createContext(overrides: Record<string, unknown> = {}) {
  return {
    db: {
      getMessages: vi.fn()
    },
    webrtc: {
      sendToPeer: vi.fn()
    },
    attachments: {},
    debugging: {},
    currentUser: null,
    currentRoom: null,
    savedRooms: [],
    ...overrides
  } as const;
}

describe('dispatchIncomingMessage room-scoped sync', () => {
  it('requests sync for event room even when another room is viewed', async () => {
    const getMessages = vi.fn(async (roomId: string) => roomId === 'room-b'
      ? [createMessage({ roomId: 'room-b', timestamp: 10, editedAt: 10 })]
      : [createMessage({ roomId: 'room-a', timestamp: 100, editedAt: 100 })]);
    const sendToPeer = vi.fn();
    const context = createContext({
      db: { getMessages },
      webrtc: { sendToPeer },
      currentRoom: { id: 'room-a' },
      savedRooms: [{ id: 'room-b' }]
    });

    await firstValueFrom(
      dispatchIncomingMessage(
        {
          type: 'chat-sync-summary',
          roomId: 'room-b',
          fromPeerId: 'peer-1',
          count: 2,
          lastUpdated: 20
        } as never,
        context as never
      ).pipe(defaultIfEmpty(null))
    );

    expect(getMessages).toHaveBeenCalledWith('room-b', expect.any(Number), 0);
    expect(sendToPeer).toHaveBeenCalledWith('peer-1', {
      type: 'chat-sync-request',
      roomId: 'room-b'
    });
  });

  it('sends full sync for requested room even when another room is viewed', async () => {
    const roomBMessageOne = createMessage({ id: 'message-b1', roomId: 'room-b', timestamp: 5 });
    const roomBMessageTwo = createMessage({ id: 'message-b2', roomId: 'room-b', timestamp: 15 });
    const roomBMessages = [roomBMessageOne, roomBMessageTwo];
    const getMessages = vi.fn(async (roomId: string) => roomId === 'room-b'
      ? roomBMessages
      : [createMessage({ id: 'message-a1', roomId: 'room-a', timestamp: 200 })]);
    const sendToPeer = vi.fn();
    const context = createContext({
      db: { getMessages },
      webrtc: { sendToPeer },
      currentRoom: { id: 'room-a' },
      savedRooms: [{ id: 'room-b' }]
    });

    await firstValueFrom(
      dispatchIncomingMessage(
        {
          type: 'chat-sync-request',
          roomId: 'room-b',
          fromPeerId: 'peer-2'
        } as never,
        context as never
      ).pipe(defaultIfEmpty(null))
    );

    expect(getMessages).toHaveBeenCalledWith('room-b', expect.any(Number), 0);
    expect(sendToPeer).toHaveBeenCalledWith('peer-2', {
      type: 'chat-sync-batch',
      roomId: 'room-b',
      messages: roomBMessages
    });
  });

  it('ignores chat messages for rooms that are not saved or currently viewed', async () => {
    const saveMessage = vi.fn(async () => undefined);
    const rememberMessageRoom = vi.fn();
    const context = createContext({
      db: { saveMessage },
      attachments: { rememberMessageRoom },
      currentRoom: { id: 'room-a' },
      savedRooms: [{ id: 'room-a' }]
    });

    const action = await firstValueFrom(
      dispatchIncomingMessage(
        {
          type: 'chat-message',
          message: createMessage({ roomId: 'room-b' })
        } as never,
        context as never
      ).pipe(defaultIfEmpty(null))
    );

    expect(action).toBeNull();
    expect(saveMessage).not.toHaveBeenCalled();
    expect(rememberMessageRoom).not.toHaveBeenCalled();
  });
});
