import { test } from '@japa/runner'
import {
  Certificate,
  CertificateKind,
  IP12Certificate,
  IPemCertificate,
  Buffer as CoreBuffer,
} from '../../../src/index.js'
import { fileToBuffer } from '../../../src/lib/Buffer.js'

const { bufferToBase64 } = CoreBuffer
test.group('fromPem()', () => {
  const data = 'test-cert'
  const key = 'test-key'

  test('sets the kind', ({ assert }) => {
    const result = Certificate.fromPem(data, key)
    assert.equal(result.kind, CertificateKind)
  })

  test('sets the key (id)', ({ assert }) => {
    const result = Certificate.fromPem(data, key)
    assert.typeOf(result.key, 'string')
    assert.isNotEmpty(result.key)
  })

  test('sets the default name', ({ assert }) => {
    const result = Certificate.fromPem(data, key)
    assert.equal(result.name, 'New PEM certificate')
  })

  test('sets the certificate data', ({ assert }) => {
    const result = Certificate.fromPem(data, key)
    assert.equal(result.cert.data, data)
  })

  test('sets the certificate key', ({ assert }) => {
    const result = Certificate.fromPem(data, key)
    assert.equal(result.certKey!.data, key)
  })

  test('sets the type', ({ assert }) => {
    const result = Certificate.fromPem(data, key)
    assert.equal(result.type, 'pem')
  })

  test('sets the passed name', ({ assert }) => {
    const result = Certificate.fromPem(data, key, 'test')
    assert.equal(result.name, 'test')
  })

  test('sets the passed passphrase on the key', ({ assert }) => {
    const result = Certificate.fromPem(data, key, '', 'test-pass')
    assert.equal(result.certKey!.passphrase, 'test-pass')
  })

  test('creates a certificate from a buffer', ({ assert }) => {
    const contents = Buffer.from('test value')
    const result = Certificate.fromPem(contents, key, '', 'test-pass')
    assert.typeOf(result.cert.data, 'Uint8Array')
  })

  test('creates a certificate key from a buffer', ({ assert }) => {
    const contents = Buffer.from('test value')
    const result = Certificate.fromPem(data, contents)
    assert.typeOf(result.certKey!.data, 'Uint8Array')
  })
})

test.group('fromP12()', () => {
  const data = 'test-cert'

  test('sets the kind', ({ assert }) => {
    const result = Certificate.fromP12(data)
    assert.equal(result.kind, CertificateKind)
  })

  test('sets the key (id)', ({ assert }) => {
    const result = Certificate.fromP12(data)
    assert.typeOf(result.key, 'string')
    assert.isNotEmpty(result.key)
  })

  test('sets the default name', ({ assert }) => {
    const result = Certificate.fromP12(data)
    assert.equal(result.name, 'New P12 certificate')
  })

  test('sets the certificate data', ({ assert }) => {
    const result = Certificate.fromP12(data)
    assert.equal(result.cert.data, data)
  })

  test('sets the type', ({ assert }) => {
    const result = Certificate.fromP12(data)
    assert.equal(result.type, 'p12')
  })

  test('sets the passed name', ({ assert }) => {
    const result = Certificate.fromP12(data, 'test')
    assert.equal(result.name, 'test')
  })

  test('sets the passed passphrase on the key', ({ assert }) => {
    const result = Certificate.fromP12(data, '', 'test-pass')
    assert.equal(result.cert.passphrase, 'test-pass')
  })

  test('creates a certificate from a buffer', ({ assert }) => {
    const contents = Buffer.from('test value')
    const result = Certificate.fromP12(contents)
    assert.typeOf(result.cert.data, 'Uint8Array')
  })
})

test.group('constructor()', () => {
  const cert = 'test-cert'
  const key = 'test-key'

  const base: IP12Certificate = {
    cert: { data: cert },
    key: '123',
    kind: 'Core#Certificate',
    name: 'name',
    type: 'p12',
    created: 123456,
  }

  test('sets the kind', ({ assert }) => {
    const result = new Certificate(base)
    assert.equal(result.kind, CertificateKind)
  })

  test('sets the key (id)', ({ assert }) => {
    const result = new Certificate(base)
    assert.equal(result.key, '123')
  })

  test('sets the name', ({ assert }) => {
    const result = new Certificate(base)
    assert.equal(result.name, 'name')
  })

  test('sets the certificate data', ({ assert }) => {
    const result = new Certificate(base)
    assert.equal(result.cert.data, cert)
  })

  test('sets the certificate key data', ({ assert }) => {
    const result = new Certificate({ ...base, type: 'pem', certKey: { data: key } })
    assert.equal(result.cert.data, cert)
  })

  test('sets the type', ({ assert }) => {
    const result = new Certificate(base)
    assert.equal(result.type, 'p12')
  })

  test('sets the created', ({ assert }) => {
    const result = new Certificate(base)
    assert.equal(result.created, 123456)
  })

  test('restores stored certificate data', ({ assert }) => {
    const contents = Buffer.from('test value')
    const result = new Certificate({ ...base, cert: { data: bufferToBase64(contents), type: 'buffer' } })
    assert.typeOf(result.cert.data, 'Uint8Array', 'has the restored data')
    assert.isUndefined(result.cert.type, 'removes the type property')
  })

  test('restores stored certificate key', ({ assert }) => {
    const contents = Buffer.from('test value')
    const result = new Certificate({
      ...base,
      type: 'pem',
      certKey: { data: bufferToBase64(contents), type: 'buffer' },
    })
    assert.typeOf(result.certKey!.data, 'Uint8Array', 'has the restored data')
    assert.isUndefined(result.certKey!.type, 'removes the type property')
  })
})

test.group('toJSON()', () => {
  const cert = 'test-cert'
  const key = 'test-key'

  const base: IP12Certificate = {
    cert: { data: cert },
    key: '123',
    kind: 'Core#Certificate',
    name: 'name',
    type: 'p12',
    created: 123456,
  }

  test('sets the kind', ({ assert }) => {
    const c = new Certificate(base)
    const result = c.toJSON()
    assert.equal(result.kind, CertificateKind)
  })

  test('sets the key (id)', ({ assert }) => {
    const c = new Certificate(base)
    const result = c.toJSON()
    assert.equal(result.key, '123')
  })

  test('sets the name', ({ assert }) => {
    const c = new Certificate(base)
    const result = c.toJSON()
    assert.equal(result.name, 'name')
  })

  test('sets the type', ({ assert }) => {
    const c = new Certificate(base)
    const result = c.toJSON()
    assert.equal(result.type, 'p12')
  })

  test('sets the cert from string', ({ assert }) => {
    const c = new Certificate(base)
    const result = c.toJSON()
    assert.equal(result.cert.data, cert)
  })

  test('sets the cert from a Buffer', ({ assert }) => {
    const contents = Buffer.from('test value')
    const certificate = Certificate.fromP12(contents)
    const result = certificate.toJSON()
    assert.equal(result.cert.data, 'dGVzdCB2YWx1ZQ==')
  })

  test('sets the cert from an Uint8Array', async ({ assert }) => {
    const blob = new Blob(['test value'], { type: 'text/plain' })
    const contents = await fileToBuffer(blob)
    const certificate = Certificate.fromP12(contents)
    const result = certificate.toJSON()
    assert.equal(result.cert.data, 'dGVzdCB2YWx1ZQ==')
  })

  test('sets the cert from ArrayBuffer', async ({ assert }) => {
    const blob = new Blob(['test value'], { type: 'text/plain' })
    const contents = await fileToBuffer(blob)
    const certificate = Certificate.fromP12(Uint8Array.from(contents).buffer)
    const result = certificate.toJSON()
    assert.equal(result.cert.data, 'dGVzdCB2YWx1ZQ==')
  })

  test('sets the cert key from string', ({ assert }) => {
    const c = new Certificate({ ...base, type: 'pem', certKey: { data: key } })
    const result = c.toJSON() as IPemCertificate
    assert.equal(result.certKey.data, key)
  })

  test('sets the cert key from a Buffer', async ({ assert }) => {
    const contents = Buffer.from('test value')
    const c = new Certificate({ ...base, type: 'pem', certKey: { data: bufferToBase64(contents), type: 'buffer' } })
    const result = c.toJSON() as IPemCertificate
    assert.equal(result.certKey.data, 'dGVzdCB2YWx1ZQ==')
    assert.equal(result.certKey.type, 'buffer')
  })
})
