import Ripemd160Polyfill from 'ripemd160-min'
import { isNodeCryptoAvailable } from './cryptoUtils'

type NodeCryptoCreateHash = typeof import('crypto').createHash

export interface Ripemd160Digest {
  digest(data: Buffer): Buffer
}

export class Ripemd160PolyfillDigest implements Ripemd160Digest {
  digest(data: Buffer): Buffer {
    const instance = new Ripemd160Polyfill()
    instance.update(data)
    const hash = instance.digest()
    if (Array.isArray(hash)) {
      return Buffer.from(hash)
    } else {
      return Buffer.from(hash.buffer)
    }
  }
}

export class NodeCryptoRipemd160Digest implements Ripemd160Digest {
  nodeCryptoCreateHash: NodeCryptoCreateHash

  constructor(nodeCryptoCreateHash: NodeCryptoCreateHash) {
    this.nodeCryptoCreateHash = nodeCryptoCreateHash
  }

  digest(data: Buffer): Buffer {
    try {
      return this.nodeCryptoCreateHash('rmd160').update(data).digest()
    } catch (error) {
      try {
        return this.nodeCryptoCreateHash('ripemd160').update(data).digest()
      } catch (_err) {
        console.log(error)
        console.log('Node.js `crypto.createHash` exists but failing to digest for ripemd160, falling back to js implementation')
        const polyfill = new Ripemd160PolyfillDigest()
        return polyfill.digest(data)
      }
    }
  }
}

export function createHashRipemd160() {
  const nodeCryptoCreateHash = isNodeCryptoAvailable(nodeCrypto => {
    if (typeof nodeCrypto.createHash === 'function') {
      return nodeCrypto.createHash
    }
    return false
  })
  if (nodeCryptoCreateHash) {
    return new NodeCryptoRipemd160Digest(nodeCryptoCreateHash)
  } else {
    return new Ripemd160PolyfillDigest()
  }
}

export function hashRipemd160(data: Buffer) {
  const hash = createHashRipemd160()
  return hash.digest(data)
}
