// Copyright 2024 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import { assert } from 'chai';
import { Proto, StorageState } from '@signalapp/mock-server';
import * as durations from '../../util/durations/index.std.ts';
import type { App } from './fixtures.node.ts';
import {
  Bootstrap,
  debug,
  getCallLinkRecordPredicate,
} from './fixtures.node.ts';
import { createCallLink } from '../helpers.node.ts';
import { uuidToBytes } from '../../util/uuidToBytes.std.ts';
import { MY_STORY_ID } from '../../types/Stories.std.ts';

const IdentifierType = Proto.ManifestRecord.Identifier.Type;

describe('storage service', function (this: Mocha.Suite) {
  this.timeout(durations.MINUTE);

  let bootstrap: Bootstrap;
  let app: App;

  beforeEach(async () => {
    bootstrap = new Bootstrap({ contactCount: 0 });
    await bootstrap.init();

    const { phone } = bootstrap;

    let state = StorageState.getEmpty();

    state = state.updateAccount({
      profileKey: phone.profileKey.serialize(),
      givenName: phone.profileName,
    });

    // Add my story
    state = state.addRecord({
      type: IdentifierType.STORY_DISTRIBUTION_LIST,
      record: {
        storyDistributionList: {
          allowsReplies: true,
          identifier: uuidToBytes(MY_STORY_ID),
          isBlockList: true,
          name: MY_STORY_ID,
          deletedAtTimestamp: null,
          recipientServiceIdsBinary: null,
        },
      },
    });

    await phone.setStorageState(state);

    app = await bootstrap.link();
  });

  afterEach(async function (this: Mocha.Context) {
    if (!bootstrap) {
      return;
    }

    await bootstrap.maybeSaveLogs(this.currentTest, app);
    if (app) {
      await app.close();
    }
    await bootstrap.teardown();
  });

  it('should save created call links and restore on relink', async () => {
    const { phone } = bootstrap;

    let state;

    const window = await app.getWindow();
    state = await phone.expectStorageState('initial state');

    debug('Creating call link');
    const roomId = await createCallLink(window, { name: 'Fun link' });
    assert.exists(roomId, 'Call link roomId should exist');

    debug('Waiting for storage update');
    state = await phone.waitForStorageState({ after: state });

    const record = state.findRecord(getCallLinkRecordPredicate(roomId));
    assert.ok(record, 'Saves call link record with matching roomId');
    const deletedAt = record.record.callLink.deletedAtTimestampMs ?? 0;
    assert.notOk(deletedAt, 'deletedAt falsey');

    debug('Creating link then deleting it');
    const roomIdDelete = await createCallLink(window, {
      name: 'Link to delete',
    });
    assert.exists(roomIdDelete, 'Call link roomId should exist');

    debug('Waiting for storage update');
    state = await phone.waitForStorageState({ after: state });

    const recordToDelete = state.findRecord(
      getCallLinkRecordPredicate(roomIdDelete)
    );
    assert.ok(recordToDelete, 'Saves call link record with matching roomId');

    const deletedAtBeforeDelete =
      recordToDelete.record.callLink.deletedAtTimestampMs ?? 0;
    assert.notOk(deletedAtBeforeDelete, 'deletedAt falsey');

    debug('Deleting call link');
    const callLinkItem = window.getByText('Link to delete');
    await callLinkItem.click();
    const callLinkDetails = window.locator(
      '.CallsTab__ConversationCallDetails'
    );
    await callLinkDetails.waitFor();
    const deleteButton = window.getByRole('button', {
      name: 'Delete link',
    });
    await deleteButton.click();
    const confirmModal = window.getByTestId(
      'ConfirmationDialog.CallLinkDetails__DeleteLinkModal'
    );
    await confirmModal.waitFor();
    const deleteConfirm = window.locator('.module-Button').getByText('Delete');
    await deleteConfirm.click();

    debug('Waiting for storage update');
    state = await phone.waitForStorageState({ after: state });

    const recordAfterDelete = state.findRecord(
      getCallLinkRecordPredicate(roomIdDelete)
    );
    assert.ok(recordAfterDelete, 'Call link record still present');
    const deletedAtAfterDelete =
      recordAfterDelete.record.callLink.deletedAtTimestampMs ?? 0;
    assert.ok(deletedAtAfterDelete, 'deletedAt present');
  });
});
