/** Shared test helpers (hex codec + Ed25519 host stub). */
import { ed25519 } from '@noble/curves/ed25519'
import type { AeadAlg, DeviceAuthProofProvider } from 'relay-contracts'
import { createHostHandshake, type HostHandshake } from '../src/handshake.js'
import { nobleEd25519Signer } from '../src/ed25519.js'

export function hexToBytes(hex: string): Uint8Array {
  const out = new Uint8Array(hex.length / 2)
  for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16)
  return out
}

export function bytesToHex(bytes: Uint8Array): string {
  return Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('')
}

export const utf8 = (s: string): Uint8Array => new TextEncoder().encode(s)
export const fromUtf8 = (b: Uint8Array): string => new TextDecoder().decode(b)

/** A deterministic Ed25519 host identity for handshake tests. */
export function makeHostIdentity(): { privateKey: Uint8Array; agentPubkey: Uint8Array } {
  const privateKey = ed25519.utils.randomPrivateKey()
  return { privateKey, agentPubkey: ed25519.getPublicKey(privateKey) }
}

/** A proof provider whose proofs are bound to the ephemeral binding material. */
export function boundProofProvider(): DeviceAuthProofProvider {
  return {
    async proofFor(hostId, binding) {
      return `proof:${hostId}:${bytesToHex(binding.clientEphPub)}:${bytesToHex(binding.clientNonce)}`
    },
  }
}

/** Verify a bound proof matches the presented ephemeral binding (anti-replay). */
export function verifyBoundProof(
  proof: string,
  binding: { clientEphPub: Uint8Array; clientNonce: Uint8Array },
): boolean {
  const expected = `${bytesToHex(binding.clientEphPub)}:${bytesToHex(binding.clientNonce)}`
  return proof.startsWith('proof:') && proof.endsWith(expected)
}

export function makeHost(
  privateKey: Uint8Array,
  agentPubkey: Uint8Array,
  supported: readonly AeadAlg[] = ['xchacha20-poly1305', 'aes-256-gcm'],
): HostHandshake {
  return createHostHandshake({
    signer: nobleEd25519Signer(privateKey),
    agentPubkey,
    supported,
    verifyDeviceProof: async (proof, binding) => verifyBoundProof(proof, binding),
  })
}
