/**
 * T5 — ephemeral X25519 keygen + ECDH shared secret via WebCrypto.
 *
 * The private key is a NON-EXTRACTABLE `CryptoKey` (INV5): it never leaves as bytes —
 * `exportKey('raw', priv)` rejects. Fresh per handshake (forward secrecy).
 */
import { getWebCrypto } from './crypto-provider.js'
import { E2EError } from './errors.js'

const ALG = 'X25519'
const SHARED_SECRET_BITS = 256

export interface EphemeralKeyPair {
  readonly publicKey: Uint8Array
  readonly privateKey: CryptoKey
}

/** Generate a fresh X25519 keypair; private key is non-extractable. */
export async function generateEphemeralKeyPair(): Promise<EphemeralKeyPair> {
  const subtle = getWebCrypto()
  const pair = (await subtle.generateKey({ name: ALG }, false, ['deriveBits'])) as CryptoKeyPair
  const rawPub = await subtle.exportKey('raw', pair.publicKey)
  return { publicKey: new Uint8Array(rawPub), privateKey: pair.privateKey }
}

/** Derive the 32-byte raw ECDH shared secret from our private key + the peer's raw public key. */
export async function deriveSharedSecret(
  privateKey: CryptoKey,
  peerPublicKey: Uint8Array,
): Promise<Uint8Array> {
  const subtle = getWebCrypto()
  // Copy into a fresh ArrayBuffer-backed view so it satisfies DOM `BufferSource` (not SharedArrayBuffer).
  const peerRaw = new Uint8Array(peerPublicKey.length)
  peerRaw.set(peerPublicKey)
  let peer: CryptoKey
  try {
    peer = await subtle.importKey('raw', peerRaw, { name: ALG }, false, [])
  } catch {
    throw new E2EError('E2E_BAD_PEER_KEY', 'peer X25519 public key is invalid')
  }
  try {
    const bits = await subtle.deriveBits({ name: ALG, public: peer }, privateKey, SHARED_SECRET_BITS)
    return new Uint8Array(bits)
  } catch {
    throw new E2EError('E2E_ECDH_FAILED', 'X25519 ECDH derivation failed')
  }
}
