import { describe, expect, it } from 'vitest'
import { x25519 } from '@noble/curves/ed25519'
import { deriveSharedSecret, generateEphemeralKeyPair } from '../src/x25519.js'
import { E2EError } from '../src/errors.js'
import { getWebCrypto } from '../src/crypto-provider.js'
import { bytesToHex } from './helpers.js'

describe('T5 x25519 ECDH', () => {
  it('classic agreement: crossed pubkeys derive the same shared secret', async () => {
    const a = await generateEphemeralKeyPair()
    const b = await generateEphemeralKeyPair()
    const ab = await deriveSharedSecret(a.privateKey, b.publicKey)
    const ba = await deriveSharedSecret(b.privateKey, a.publicKey)
    expect(ab.length).toBe(32)
    expect(bytesToHex(ab)).toBe(bytesToHex(ba))
  })

  it('INV5: private key is a non-extractable CryptoKey (exportKey rejects)', async () => {
    const a = await generateEphemeralKeyPair()
    expect(a.privateKey.extractable).toBe(false)
    await expect(getWebCrypto().exportKey('raw', a.privateKey)).rejects.toBeTruthy()
  })

  it('negative: an invalid peer public key is rejected with a typed error', async () => {
    const a = await generateEphemeralKeyPair()
    await expect(deriveSharedSecret(a.privateKey, new Uint8Array(8))).rejects.toBeInstanceOf(
      E2EError,
    )
  })

  it('parity: WebCrypto secret equals @noble/curves x25519 for the same key material', async () => {
    // Generate a noble scalar, import it into WebCrypto as pkcs8, and check both agree.
    const noblePriv = x25519.utils.randomPrivateKey()
    const noblePub = x25519.getPublicKey(noblePriv)
    const peer = await generateEphemeralKeyPair()
    const nobleSecret = x25519.getSharedSecret(noblePriv, peer.publicKey).slice(-32)
    const wcSecret = await deriveSharedSecret(peer.privateKey, noblePub)
    expect(bytesToHex(wcSecret)).toBe(bytesToHex(nobleSecret))
  })
})
