import { AESCipherSIVDeterministic } from '../../base/cipher/aes';
import { AbstractSelfAccountKeyring } from '../../keyring/account_base';
import { PeerAccountKeyring } from '../../keyring/account_peer';
import { AbstractRoomKeyring } from '../../keyring/room_base';
import { AbstractCryptoConfig } from '../base';
import { AccountPairDeriveKeyV1 } from '../common';
import { RoomKeyConfigDumpArgs, RoomKeyConfigLoadArgs } from './args';
import { makeInfoByteArray } from '../../utility';

/**
 * Room key encryption: AES-SIV
        KEY: 32B from curve25519 scalar_mult -> 64B HKDF by context
        NONCE: none

    Encryption of the keys required for room access. These are always distributed by the owner with P2P encryption
    for other peer accounts.
    NOTE: No LRU cache on derive_key(), room keys are only loaded once and they are unique so there's no benefit.
 */
export class RoomKeyConfigV1 extends AbstractCryptoConfig<
  RoomKeyConfigLoadArgs,
  RoomKeyConfigDumpArgs
> {
  constructor(never_dump: boolean = false) {
    super(1, never_dump);
  }

  public load(args: RoomKeyConfigLoadArgs): Promise<Uint8Array> {
    let cipher = args.cipher.slice(this.version.byteLength);

    return this.derive_key(args.self_kr, args.peer_kr, args.room_kr, args.key_version).then(
      (derive_key) => {
        return AESCipherSIVDeterministic.decrypt(cipher, derive_key);
      }
    );
  }

  public dump(args: RoomKeyConfigDumpArgs): Promise<Uint8Array> {
    return this.derive_key(args.self_kr, args.peer_kr, args.room_kr, args.key_version).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,
    peer_kr: PeerAccountKeyring,
    room_kr: AbstractRoomKeyring,
    key_version: string
  ): Promise<Uint8Array> {
    return AccountPairDeriveKeyV1.derive_key(
      self_kr,
      peer_kr,
      makeInfoByteArray(['room-key-', this.version, '-', room_kr.id, '-', key_version]),
      false
    );
  }
}
