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

import { assert } from 'chai';
import { Proto } from '@signalapp/mock-server';

import * as durations from '../../util/durations';
import type { App, Bootstrap } from './fixtures';
import { initStorage, debug } from './fixtures';

const IdentifierType = Proto.ManifestRecord.Identifier.Type;

describe('storage service', function needsName() {
  this.timeout(durations.MINUTE);

  let bootstrap: Bootstrap;
  let app: App;

  beforeEach(async () => {
    ({ bootstrap, app } = await initStorage());
  });

  afterEach(async () => {
    await app.close();
    await bootstrap.teardown();
  });

  it('should drop gv1 record if there is a matching gv2 record', async () => {
    const { phone } = bootstrap;

    debug('adding both records');
    {
      const state = await phone.expectStorageState('consistency check');

      const groupV1Id = Buffer.from('Wi9258rCEp7AdSdp+jCMlQ==', 'base64');
      const masterKey = Buffer.from(
        '2+rdvzFGCOJI8POHcPNZHrYQWS/JXmT63R5OXKxhrPk=',
        'base64'
      );

      const updatedState = await phone.setStorageState(
        state
          .addRecord({
            type: IdentifierType.GROUPV1,
            record: {
              groupV1: {
                id: groupV1Id,
              },
            },
          })
          .addRecord({
            type: IdentifierType.GROUPV2,
            record: {
              groupV2: {
                masterKey,
              },
            },
          })
      );

      debug('sending fetch storage');
      await phone.sendFetchStorage({
        timestamp: bootstrap.getTimestamp(),
      });

      debug('waiting for next storage state');
      const nextState = await phone.waitForStorageState({
        after: updatedState,
      });

      assert.isFalse(
        nextState.hasRecord(({ type }) => {
          return type === IdentifierType.GROUPV1;
        }),
        'should not have gv1 record'
      );

      assert.isTrue(
        nextState.hasRecord(({ type, record }) => {
          if (type !== IdentifierType.GROUPV2) {
            return false;
          }

          if (!record.groupV2?.masterKey) {
            return false;
          }
          return Buffer.from(masterKey).equals(record.groupV2.masterKey);
        }),
        'should have gv2 record'
      );
    }
  });

  it('should drop duplicate account record', async () => {
    const { phone } = bootstrap;

    debug('duplicating account record');
    {
      const state = await phone.expectStorageState('consistency check');

      const oldAccount = state.findRecord(({ type }) => {
        return type === IdentifierType.ACCOUNT;
      });
      if (oldAccount === undefined) {
        throw new Error('should have initial account record');
      }

      const updatedState = await phone.setStorageState(
        state.addRecord({
          type: IdentifierType.ACCOUNT,
          record: oldAccount.record,
        })
      );

      debug('sending fetch storage');
      await phone.sendFetchStorage({
        timestamp: bootstrap.getTimestamp(),
      });

      debug('waiting for next storage state');
      const nextState = await phone.waitForStorageState({
        after: updatedState,
      });

      assert.isFalse(
        nextState.hasRecord(({ type, key }) => {
          return type === IdentifierType.ACCOUNT && key.equals(oldAccount.key);
        }),
        'should not have old account record'
      );

      assert.isTrue(
        nextState.hasRecord(({ type }) => {
          return type === IdentifierType.ACCOUNT;
        }),
        'should have new account record'
      );
    }
  });
});
