import { getCryptoLib } from './cryptoUtils';

export interface Hmac {
  digest(key: Buffer, data: Buffer): Promise<Buffer>;
}

type NodeCryptoCreateHmac = typeof import('crypto').createHmac;

export class NodeCryptoHmacSha256 implements Hmac {
  createHmac: NodeCryptoCreateHmac;

  constructor(createHmac: NodeCryptoCreateHmac) {
    this.createHmac = createHmac;
  }

  async digest(key: Buffer, data: Buffer): Promise<Buffer> {
    const result = this.createHmac('sha256', key)
      .update(data)
      .digest();
    return Promise.resolve(result);
  }
}

export class WebCryptoHmacSha256 implements Hmac {
  subtleCrypto: SubtleCrypto;

  constructor(subtleCrypto: SubtleCrypto) {
    this.subtleCrypto = subtleCrypto;
  }

  async digest(key: Buffer, data: Buffer): Promise<Buffer> {
    const cryptoKey = await this.subtleCrypto.importKey(
      'raw',
      key,
      { name: 'HMAC', hash: 'SHA-256' },
      true,
      ['sign']
    );
    const sig = await this.subtleCrypto.sign(
      // The `hash` is only specified for non-compliant browsers like Edge.
      { name: 'HMAC', hash: 'SHA-256' },
      cryptoKey,
      data
    );
    return Buffer.from(sig);
  }
}

export async function createHmacSha256(): Promise<Hmac> {
  const cryptoLib = await getCryptoLib();
  if (cryptoLib.name === 'subtleCrypto') {
    return new WebCryptoHmacSha256(cryptoLib.lib);
  } else {
    return new NodeCryptoHmacSha256(cryptoLib.lib.createHmac);
  }
}
