'use strict'

const os = require('os')
const forge = require('node-forge')

// from self-cert module
function selfCert (opts) {
  const options = opts || {}
  const log = opts.logger || require('abstract-logging')
  const now = new Date()

  if (!options.attrs) options.attrs = {}
  if (!options.expires) {
    options.expires = new Date(
      now.getFullYear() + 5, now.getMonth() + 1, now.getDate()
    )
  }

  log.debug('generating key pair')
  const keys = forge.pki.rsa.generateKeyPair(options.bits || 2048)
  log.debug('key pair generated')

  log.debug('generating self-signed certificate')
  const cert = forge.pki.createCertificate()
  cert.publicKey = keys.publicKey
  cert.serialNumber = '01'
  cert.validity.notBefore = now
  cert.validity.notAfter = options.expires

  const attrs = [
    { name: 'commonName', value: options.attrs.commonName || os.hostname() },
    { name: 'countryName', value: options.attrs.countryName || 'US' },
    { name: 'stateOrProvinceName', value: options.attrs.stateName || 'Georgia' },
    { name: 'localityName', value: options.attrs.locality || 'Atlanta' },
    { name: 'organizationName', value: options.attrs.orgName || 'None' },
    { shortName: 'OU', value: options.attrs.shortName || 'example' }
  ]
  cert.setSubject(attrs)
  cert.setIssuer(attrs)

  cert.setExtensions([
    { name: 'basicConstraints', cA: true },
    {
      name: 'keyUsage',
      keyCertSign: true,
      digitalSignature: true,
      nonRepudiation: true,
      keyEncipherment: true,
      dataEncipherment: true
    },
    {
      name: 'extKeyUsage',
      serverAuth: true,
      clientAuth: true,
      codeSigning: true,
      emailProtection: true,
      timeStamping: true
    },
    {
      name: 'nsCertType',
      client: true,
      server: true,
      email: true,
      objsign: true,
      sslCA: true,
      emailCA: true,
      objCA: true
    },
    { name: 'subjectKeyIdentifier' },
    {
      name: 'subjectAltName',
      altNames: [{ type: 6 /* URI */, value: 'DNS: ' + attrs[0].value }].concat((function () {
        const interfaces = os.networkInterfaces()

        // fix citgm: skip invalid ips (aix72-ppc64)
        const ips = Object.values(interfaces).flat()
          .filter(i => !!forge.util.bytesFromIP(i.address))
          .map(i => ({ type: 7 /* IP */, ip: i.address }))

        return ips
      }()))
    }
  ])

  cert.sign(keys.privateKey)
  log.debug('certificate generated')
  return {
    privateKey: forge.pki.privateKeyToPem(keys.privateKey),
    publicKey: forge.pki.publicKeyToPem(keys.publicKey),
    certificate: forge.pki.certificateToPem(cert)
  }
}

async function buildCertificate () {
  // "global" is used in here because "t.context" is only supported by "t.beforeEach" and "t.afterEach"
  // For the test case which execute this code which will be using `t.before` and it can reduce the
  // number of times executing it.
  if (!global.context || !global.context.cert || !global.context.key) {
    const certs = selfCert({
      expires: new Date(Date.now() + 86400000)
    })
    global.context = {
      cert: certs.certificate,
      key: certs.privateKey
    }
  }
}

module.exports = { buildCertificate }
