/**
 * T10 — recoverable replay content-key (FROZEN §4.4/§4.5 FIX 3 surface; P4 implements it).
 *
 * §4.4 ephemeral session subkeys give forward secrecy but a FRESH key each reconnect, so ciphertext
 * in the base-app ring buffer would be undecryptable after a refresh. `K_content` is a RECOVERABLE
 * per-session key derived from the HOST-SCOPED `hostContentSecret` (NOT a raw account secret — the
 * agent is unattended; a host-scoped, per-host-revocable secret bounds the blast radius, INV4/INV12).
 * Any authorized device that obtains `hostContentSecret` re-derives the identical `K_content` and
 * decrypts replayed ciphertext; the relay still sees only ciphertext (INV2).
 *
 * Frame routing (frozen, P2/P6 honor it): only replay-bound host→client output is additionally
 * sealed under `K_content`; client→host input is NEVER sealed under it.
 */
import { hkdf } from '@noble/hashes/hkdf'
import { sha256 } from '@noble/hashes/sha2'
import type { AeadKey, E2EEnvelope, ReplayKeyParams } from 'relay-contracts'
import { AEAD_KEY_BYTES, REPLAY_KDF_INFO } from 'relay-contracts'
import { wrapAeadKey } from './aead-key.js'
import { decodeEnvelope } from './envelope.js'
import { openFrame, sealFrame } from './session.js'

const encoder = new TextEncoder()

/**
 * ASCII unit separator (0x1f). Domain-separates sessionId ‖ epoch inside the HKDF salt so
 * distinct (sessionId, epoch) pairs can never alias via concatenation. Neither field contains
 * it: sessionId is an opaque id and epoch is a generated non-secret diversifier.
 */
const SALT_FIELD_SEPARATOR = 0x1f

/** salt = utf8(sessionId) ‖ 0x1f ‖ utf8(epoch) — unambiguous per-session, per-generation binding. */
function replaySalt(sessionId: string, epoch: string): Uint8Array {
  const sessionBytes = encoder.encode(sessionId)
  const epochBytes = encoder.encode(epoch)
  const salt = new Uint8Array(sessionBytes.length + 1 + epochBytes.length)
  salt.set(sessionBytes, 0)
  salt[sessionBytes.length] = SALT_FIELD_SEPARATOR
  salt.set(epochBytes, sessionBytes.length + 1)
  return salt
}

/**
 * HKDF(hostContentSecret, salt=sessionId ‖ 0x1f ‖ epoch, info=REPLAY_KDF_INFO) → per-session,
 * per-host, per-generation content key (F6). A fresh `epoch` per sealer generation yields a fresh
 * key even for the same (hostContentSecret, sessionId), so a seq=0 reset after restart/re-attach
 * can never collide with a prior generation's (key, nonce). Recoverability WITHIN one generation is
 * preserved: same (secret, sessionId, alg, epoch) ⇒ byte-identical key.
 */
export function deriveContentKey(p: ReplayKeyParams): AeadKey {
  const raw = hkdf(
    sha256,
    p.hostContentSecret,
    replaySalt(p.sessionId, p.epoch),
    encoder.encode(REPLAY_KDF_INFO),
    AEAD_KEY_BYTES,
  )
  return wrapAeadKey(raw, p.alg, 'replay')
}

/** Agent (P2) seals a replay-bound host→client frame under K_content. */
export function sealReplayFrame(k: AeadKey, seq: bigint, plaintext: Uint8Array): E2EEnvelope {
  return sealFrame(k, seq, plaintext)
}

/** Browser (P6) decrypts a stored replay/preview frame under K_content. */
export function openReplayCiphertext(k: AeadKey, dataPayload: Uint8Array): Uint8Array {
  const env = decodeEnvelope(dataPayload)
  return openFrame(k, env, env.seq)
}
