/**
 * T6 — HKDF session-key derivation with DIRECTION-SPLIT subkeys.
 *
 * CRITICAL (finding #1): one shared AEAD key across both directions + a deterministic seq-nonce
 * ⇒ client-seq=N and host-seq=N reuse an identical (key, nonce) ⇒ GCM catastrophe, and lets a
 * relay reflect a c2h frame back as h2c. Fix: derive the §4.4 master, then HKDF-Expand it into two
 * disjoint subkeys under the FROZEN labels so client-write/host-read share one key and
 * host-write/client-read share the OTHER, never the same.
 */
import { hkdf } from '@noble/hashes/hkdf'
import { sha256 } from '@noble/hashes/sha2'
import type { AeadAlg, DirectionalKeys } from 'relay-contracts'
import { HKDF_INFO, HKDF_INFO_C2H, HKDF_INFO_H2C, AEAD_KEY_BYTES } from 'relay-contracts'
import { concatBytes } from 'relay-contracts'
import { wrapAeadKey } from './aead-key.js'

const encoder = new TextEncoder()
const EMPTY = new Uint8Array(0)

/** §4.4 master secret: HKDF-SHA256(ikm=sharedSecret, salt=clientNonce‖hostNonce, info=HKDF_INFO). */
export function deriveMaster(
  sharedSecret: Uint8Array,
  clientNonce: Uint8Array,
  hostNonce: Uint8Array,
): Uint8Array {
  const salt = concatBytes([clientNonce, hostNonce])
  return hkdf(sha256, sharedSecret, salt, encoder.encode(HKDF_INFO), AEAD_KEY_BYTES)
}

/** Expand the master into a single direction subkey under the given frozen label. */
function expandSubkey(master: Uint8Array, label: string): Uint8Array {
  return hkdf(sha256, master, EMPTY, encoder.encode(label), AEAD_KEY_BYTES)
}

/**
 * Derive the direction-split `DirectionalKeys` (c2h / h2c) for a session. `salt` is BOTH nonces in
 * fixed client‖host order, so swapping the nonce roles yields different keys; the two subkeys are
 * disjoint by construction (distinct frozen labels).
 */
export async function deriveSessionKeys(
  sharedSecret: Uint8Array,
  clientNonce: Uint8Array,
  hostNonce: Uint8Array,
  alg: AeadAlg,
): Promise<DirectionalKeys> {
  const master = deriveMaster(sharedSecret, clientNonce, hostNonce)
  const c2h = expandSubkey(master, HKDF_INFO_C2H)
  const h2c = expandSubkey(master, HKDF_INFO_H2C)
  return {
    c2h: wrapAeadKey(c2h, alg, 'c2h'),
    h2c: wrapAeadKey(h2c, alg, 'h2c'),
  }
}
