/**
 * §4.4 E2E type surface (FIX 2 + FIX 3). relay-contracts owns the SHAPES + the ENVELOPE
 * binary codec; the AEAD/ECDH crypto IMPLEMENTATIONS live in P4 `relay-e2e/` (which
 * re-exports these signatures). No plan redefines any of these locally.
 */
import { z } from 'zod'

/** AEAD algorithm (string-literal union, §4.4). */
export type AeadAlg = 'aes-256-gcm' | 'xchacha20-poly1305'
export const AeadAlgSchema = z.enum(['aes-256-gcm', 'xchacha20-poly1305'])

/** Nonce widths per AEAD (§4.4: 12B GCM / 24B XChaCha). */
export const NONCE_BYTES: Readonly<Record<AeadAlg, number>> = {
  'aes-256-gcm': 12,
  'xchacha20-poly1305': 24,
}

/** AEAD key length in bytes (both algs use a 256-bit key). */
export const AEAD_KEY_BYTES = 32 as const

/**
 * Opaque wrapper over a 32-byte key (NOT a WebCrypto CryptoKey) — FIX 2. The `unique symbol`
 * makes it nominal so a raw Uint8Array can't be passed where an AeadKey is required.
 */
export type AeadKey = { readonly __aeadKey: unique symbol }

export type SessionRole = 'client' | 'host'

/** Direction-split subkeys (FIX 2): client seals c2h/opens h2c; host seals h2c/opens c2h. */
export interface DirectionalKeys {
  readonly c2h: AeadKey
  readonly h2c: AeadKey
}

/** HKDF label constants (§4.4) — frozen so both sides derive identical subkeys. */
export const HKDF_INFO = 'relay-e2e/v1' as const
export const HKDF_INFO_C2H = 'relay-e2e/v1/c2h' as const
export const HKDF_INFO_H2C = 'relay-e2e/v1/h2c' as const

/** client_hello (§4.4). `deviceAuthProof` is minted by P5, shape-validated by P4 (FIX 6b). */
export interface ClientHello {
  readonly clientEphPub: Uint8Array
  readonly clientNonce: Uint8Array
  readonly aeadOffer: readonly AeadAlg[]
  readonly deviceAuthProof: string
}
export const ClientHelloSchema = z
  .object({
    clientEphPub: z.instanceof(Uint8Array),
    clientNonce: z.instanceof(Uint8Array),
    aeadOffer: z.array(AeadAlgSchema).nonempty(),
    deviceAuthProof: z.string().min(1),
  })
  .strict()

/** host_hello (§4.4). `sig` = Ed25519(agent_pubkey) over the transcript. */
export interface HostHello {
  readonly hostEphPub: Uint8Array
  readonly hostNonce: Uint8Array
  readonly aeadChoice: AeadAlg
  readonly enrollFpr: string
  readonly sig: Uint8Array
}
export const HostHelloSchema = z
  .object({
    hostEphPub: z.instanceof(Uint8Array),
    hostNonce: z.instanceof(Uint8Array),
    aeadChoice: AeadAlgSchema,
    enrollFpr: z.string().min(1),
    sig: z.instanceof(Uint8Array),
  })
  .strict()

/** Encrypted frame envelope carried inside every §4.1 DATA payload once E2E is live (§4.4). */
export interface E2EEnvelope {
  readonly seq: bigint // strictly monotonic per direction (anti-replay, INV13)
  readonly nonce: Uint8Array // DETERMINISTIC f(seq)
  readonly ciphertext: Uint8Array // AEAD(subkey, plaintext, aad = directionLabel‖seq)
  readonly tag: Uint8Array // AEAD auth tag; failure ⇒ drop + tear down
}

export interface HandshakeResult {
  readonly keys: DirectionalKeys
  readonly aead: AeadAlg
  readonly transcript: Uint8Array
}

/** Stateful directional wrapper over a stream (holds BOTH subkeys + role + seq guards). */
export interface E2ESession {
  readonly role: SessionRole
  seal(plaintext: Uint8Array): Uint8Array
  open(dataPayload: Uint8Array): Uint8Array
  rederive(next: HandshakeResult): void
}

export interface ClientHandshake {
  start(): Promise<ClientHello>
  onHostHello(msg: HostHello, agentPubkey: Uint8Array): Promise<HandshakeResult>
}

/** P4-internal helper (declared here only to type AuthorizedDeviceContext) — TOFU/pin store. */
export interface DevicePinStore {
  get(hostId: string): Promise<string | null>
  pin(hostId: string, enrollFpr: string): Promise<void>
}

export interface DeviceAuthProofProvider {
  proofFor(
    hostId: string,
    binding: { clientEphPub: Uint8Array; clientNonce: Uint8Array },
  ): Promise<string>
}

export interface AuthorizedDeviceContext {
  readonly deviceAuthProofProvider: DeviceAuthProofProvider // P5 — per-handshake proof (FIX 6b)
  readonly pinStore: DevicePinStore // P6 — TOFU/pin over recomputed fingerprints
  readonly hostContentSecret: Uint8Array // §4.5 host-scoped secret; NEVER logged/sent to relay
}

/**
 * FIX 3 replay content-key derivation params (§4.4).
 *
 * FIX 3b / F6 (anti-nonce-reuse): K_content = HKDF(hostContentSecret, salt=sessionId, info=const)
 * was byte-identical for a given (host, sessionId), yet the agent-side sealer resets its
 * deterministic-nonce seq to 0 on every reconstruction (restart / re-attach). Two sealer
 * GENERATIONS would therefore seal distinct plaintext under the SAME (key, nonce) — a
 * catastrophic AEAD reuse. The per-generation `epoch` is folded into K_content so a restart /
 * new generation yields a FRESH key even for the same (hostContentSecret, sessionId); seq=0 can
 * never collide across generations. Recoverability WITHIN one generation (same epoch ⇒ same key)
 * is preserved.
 */
export interface ReplayKeyParams {
  readonly hostContentSecret: Uint8Array
  readonly sessionId: string
  readonly alg: AeadAlg
  readonly epoch: string // FIX 3b / F6: fresh per-sealer-generation diversifier folded into K_content;
  // NON-SECRET, carried with the ring buffer so the browser re-derives the key.
}
export const REPLAY_KDF_INFO = 'relay-e2e/replay/v1' as const
