import { AbstractSelfAccountKeyring } from '../../../keyring/account_base';
import { AbstractCryptoConfig } from '../../base';
import { AccountRoomKeyConfigDumpArgs, AccountRoomKeyConfigLoadArgs } from './args';
import { AESCipherSIVDeterministic } from '../../../base/cipher/aes';
import { AbstractRoomKeyring } from '../../../keyring/room_base';
import { AccountMasterDeriveKeyV1 } from '../../common';
import { makeInfoByteArray } from '../../../utility';

/**
 * Account key encryption: AES-SIV
        KEY: 32B from account keyring -> 64B HKDF by context
        NONCE: none

    Used to encrypt various keys of the account. (for public key crypto)
    NOTE: No LRU cache on derive_key(), root keys are only loaded once and they are unique so there's no benefit.
 */
export class AccountRoomKeyConfigV1 extends AbstractCryptoConfig<
  AccountRoomKeyConfigLoadArgs,
  AccountRoomKeyConfigDumpArgs
> {
  constructor(never_dump: boolean = false) {
    super(1, never_dump);
  }

  public load(args: AccountRoomKeyConfigLoadArgs): Promise<Uint8Array> {
    return this.derive_key(args.self_kr, args.key_version, args.room_kr).then((derive_key) => {
      return AESCipherSIVDeterministic.decrypt(
        args.cipher.slice(this.version.byteLength),
        derive_key
      );
    });
  }

  public dump(args: AccountRoomKeyConfigDumpArgs): Promise<Uint8Array> {
    return this.derive_key(args.self_kr, args.key_version, args.room_kr).then((derive_key) => {
      return AESCipherSIVDeterministic.encrypt(
        args.room_kr.get_key(args.key_version),
        derive_key,
        undefined,
        this.version
      );
    });
  }

  public derive_key(
    self_kr: AbstractSelfAccountKeyring,
    key_version: string,
    room_kr: AbstractRoomKeyring
  ) {
    return AccountMasterDeriveKeyV1.derive_key(
      self_kr,
      makeInfoByteArray(['account-room-key-', this.version, '-', key_version, '-', room_kr.id]),
      false
    );
  }
}
