import { test } from '@japa/runner'
import sinon from 'sinon'
import { Certificates } from '../../../../src/mocking/legacy/Certificates.js'
import { Certificate } from '../../../../src/models/legacy/models/ClientCertificate.js'
import type { ClientCertificate } from '../../../../src/legacy.js'

test.group('toStore()', (group) => {
  let certs: Certificates

  group.each.setup(() => {
    certs = new Certificates()
  })

  test('returns the same cert when data is string', ({ assert }) => {
    const cert = certs.certificate({
      binary: false,
    })
    const result = certs.toStore(cert)
    assert.deepEqual(result, cert)
  })

  test('transfers buffer data to string', ({ assert }) => {
    const cert = certs.certificate({
      binary: true,
    })
    const result: Certificate = certs.toStore(cert) as Certificate
    assert.notDeepEqual(result, cert, 'returns altered object')
    assert.typeOf(result.data, 'string', 'cert data is a string')
    assert.equal(result.type, 'buffer', 'adds type property')
  })
})

test.group('clientCertificate()', (group) => {
  let certs: Certificates

  group.each.setup(() => {
    certs = new Certificates()
  })

  test('returns an object', ({ assert }) => {
    const result = certs.clientCertificate()
    assert.typeOf(result, 'object')
  })

  test('has the {property} property of a type {type}')
    .with([
      { property: 'type', type: 'string' },
      { property: 'cert', type: 'object' },
      { property: 'key', type: 'object' },
      { property: 'name', type: 'string' },
      { property: 'created', type: 'number' },
    ])
    .run(({ assert }, { property, type }) => {
      const result = certs.clientCertificate()
      assert.typeOf(result[property as keyof ClientCertificate], type)
    })

  test('uses passed type', ({ assert }) => {
    const result = certs.clientCertificate({
      type: 'p12',
    })
    assert.equal(result.type, 'p12')
  })

  test('creates binary data on a certificate', ({ assert }) => {
    const result = certs.clientCertificate({
      binary: true,
    })
    // @ts-expect-error Used in testing
    assert.typeOf(result.cert.data, 'Uint8Array')
  })

  test('adds passphrase to a certificate by default', ({ assert }) => {
    const result = certs.clientCertificate({})
    // @ts-expect-error Used in testing
    assert.typeOf(result.cert.passphrase, 'string')
  })

  test('ignores passphrase on a certificate', ({ assert }) => {
    const result = certs.clientCertificate({
      noPassphrase: true,
    })
    // @ts-expect-error Used in testing
    assert.isUndefined(result.cert.passphrase)
  })
})

test.group('clientCertificates()', (group) => {
  let certs: Certificates

  group.each.setup(() => {
    certs = new Certificates()
  })

  test('Returns an array', ({ assert }) => {
    const result = certs.clientCertificates()
    assert.typeOf(result, 'array')
  })

  test('List has default number of items', ({ assert }) => {
    const result = certs.clientCertificates()
    assert.lengthOf(result, 15)
  })

  test('Returns requested number of items', ({ assert }) => {
    const result = certs.clientCertificates(5)
    assert.lengthOf(result, 5)
  })

  test('Calls clientCertificate()', ({ assert }) => {
    const spy = sinon.spy(certs, 'clientCertificate')
    certs.clientCertificates(5)
    assert.equal(spy.callCount, 5)
  })
})

test.group('exportClientCertificate()', (group) => {
  let certs: Certificates

  group.each.setup(() => {
    certs = new Certificates()
  })

  test('returns an object', ({ assert }) => {
    const result = certs.exportClientCertificate()
    assert.typeOf(result, 'object')
  })

  test('calls clientCertificate()', ({ assert }) => {
    const spy = sinon.spy(certs, 'clientCertificate')
    certs.exportClientCertificate()
    assert.isTrue(spy.calledOnce)
  })

  test('adds kind property', ({ assert }) => {
    const result = certs.exportClientCertificate()
    assert.equal(result.kind, 'ARC#ClientCertificate')
  })

  test('adds key property', ({ assert }) => {
    const result = certs.exportClientCertificate()
    assert.typeOf(result.key, 'string')
  })

  test('moves old key to pKey property', ({ assert }) => {
    const result = certs.exportClientCertificate()
    assert.typeOf(result.pKey, 'object')
  })

  test('ignores pKey when nmo key data', ({ assert }) => {
    const result = certs.exportClientCertificate({
      noKey: true,
    })
    assert.isUndefined(result.pKey)
  })
})

test.group('exportClientCertificates()', (group) => {
  let certs: Certificates

  group.each.setup(() => {
    certs = new Certificates()
  })

  test('returns an array', ({ assert }) => {
    const result = certs.exportClientCertificates()
    assert.typeOf(result, 'array')
  })

  test('has the default number of items', ({ assert }) => {
    const result = certs.exportClientCertificates()
    assert.lengthOf(result, 15)
  })

  test('returns requested number of items', ({ assert }) => {
    const result = certs.exportClientCertificates(5)
    assert.lengthOf(result, 5)
  })

  test('calls exportClientCertificate()', ({ assert }) => {
    const spy = sinon.spy(certs, 'exportClientCertificate')
    certs.exportClientCertificates(5)
    assert.equal(spy.callCount, 5)
  })
})
