/*
 * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
 *
 * SPDX-License-Identifier: AGPL-3.0-only
 */
import { RealtimeNote } from './realtime-note';
import { mockAwareness } from './test-utils/mock-awareness';
import { mockConnection } from './test-utils/mock-connection';
import { mockWebsocketDoc } from './test-utils/mock-websocket-doc';
import * as websocketAwarenessModule from './websocket-awareness';
import { WebsocketAwareness } from './websocket-awareness';
import * as websocketDocModule from './websocket-doc';
import { WebsocketDoc } from './websocket-doc';

describe('realtime note', () => {
  let mockedDoc: WebsocketDoc;
  let mockedAwareness: WebsocketAwareness;

  beforeEach(() => {
    jest.resetAllMocks();
    jest.resetModules();
    mockedDoc = mockWebsocketDoc();
    mockedAwareness = mockAwareness();
    jest
      .spyOn(websocketDocModule, 'WebsocketDoc')
      .mockImplementation(() => mockedDoc);
    jest
      .spyOn(websocketAwarenessModule, 'WebsocketAwareness')
      .mockImplementation(() => mockedAwareness);
  });

  afterAll(() => {
    jest.resetAllMocks();
    jest.resetModules();
  });

  it('can connect and disconnect clients', () => {
    const sut = new RealtimeNote('mock-note', 'nothing');
    const client1 = mockConnection(true);
    sut.addClient(client1);
    expect(sut.getConnections()).toStrictEqual([client1]);
    expect(sut.hasConnections()).toBeTruthy();
    sut.removeClient(client1);
    expect(sut.getConnections()).toStrictEqual([]);
    expect(sut.hasConnections()).toBeFalsy();
  });

  it('creates a y-doc and y-awareness', () => {
    const sut = new RealtimeNote('mock-note', 'nothing');
    expect(sut.getYDoc()).toBe(mockedDoc);
    expect(sut.getAwareness()).toBe(mockedAwareness);
  });

  it('destroys y-doc and y-awareness on self-destruction', () => {
    const sut = new RealtimeNote('mock-note', 'nothing');
    const docDestroy = jest.spyOn(mockedDoc, 'destroy');
    const awarenessDestroy = jest.spyOn(mockedAwareness, 'destroy');
    sut.destroy();
    expect(docDestroy).toBeCalled();
    expect(awarenessDestroy).toBeCalled();
  });

  it('emits destroy event on destruction', async () => {
    const sut = new RealtimeNote('mock-note', 'nothing');
    const destroyPromise = new Promise<void>((resolve) => {
      sut.once('destroy', () => {
        resolve();
      });
    });
    sut.destroy();
    await expect(destroyPromise).resolves.not.toThrow();
  });

  it("doesn't destroy a destroyed note", () => {
    const sut = new RealtimeNote('mock-note', 'nothing');
    sut.destroy();
    expect(() => sut.destroy()).toThrow();
  });
});
