// `forgeGatewayEventFrame` produces one Connect HTTP/1.1
// server-streaming frame carrying a `GatewayEvent` signed with the
// fixture private key. The Playwright `turn-ready.spec.ts` route
// handler returns this body when the UI opens `SubscribeEvents` so
// the production verification path (`core.verifyEvent`) accepts the
// frame under the matching public key the dev server picks up via
// `VITE_GATEWAY_RESPONSE_PUBLIC_KEY`.
//
// Connect HTTP/1.1 server-streaming framing per request:
//   1 byte  flag    (0x00 = message)
//   4 bytes length  (big-endian, payload size)
//   N bytes payload (JSON-encoded GatewayEvent for the JSON codec)
//
// The route handler closes the response after one frame; the UI's
// `events.svelte.ts` reconnect loop treats the abrupt end-of-body as
// a transient error and backs off, which keeps the toast visible
// long enough for the test to assert on it.

import { create, toJsonString } from "@bufbuild/protobuf";
import { webcrypto } from "node:crypto";
import { GatewayEventSchema } from "../../../src/proto/galaxy/gateway/v1/edge_gateway_pb";
import { buildEventSigningInput } from "./canon";
import {
	FIXTURE_PRIVATE_KEY_PKCS8_BASE64,
	decodeBase64,
} from "./gateway-key";

export interface ForgedEventInput {
	eventType: string;
	eventId: string;
	timestampMs: bigint;
	requestId: string;
	traceId: string;
	payloadBytes: Uint8Array;
}

let cachedPrivateKey: CryptoKey | null = null;

async function privateKey(): Promise<CryptoKey> {
	if (cachedPrivateKey !== null) {
		return cachedPrivateKey;
	}
	const pkcs8 = decodeBase64(FIXTURE_PRIVATE_KEY_PKCS8_BASE64);
	cachedPrivateKey = await webcrypto.subtle.importKey(
		"pkcs8",
		pkcs8,
		{ name: "Ed25519" },
		false,
		["sign"],
	);
	return cachedPrivateKey;
}

async function sha256(payload: Uint8Array): Promise<Uint8Array> {
	const digest = await webcrypto.subtle.digest("SHA-256", payload);
	return new Uint8Array(digest);
}

export async function forgeGatewayEventFrame(
	input: ForgedEventInput,
): Promise<Uint8Array> {
	const payloadHash = await sha256(input.payloadBytes);
	const canonical = buildEventSigningInput({
		eventType: input.eventType,
		eventId: input.eventId,
		timestampMs: input.timestampMs,
		requestId: input.requestId,
		traceId: input.traceId,
		payloadHash,
	});
	const signatureBuf = await webcrypto.subtle.sign(
		{ name: "Ed25519" },
		await privateKey(),
		canonical,
	);
	const event = create(GatewayEventSchema, {
		eventType: input.eventType,
		eventId: input.eventId,
		timestampMs: input.timestampMs,
		payloadBytes: input.payloadBytes,
		payloadHash,
		signature: new Uint8Array(signatureBuf),
		requestId: input.requestId,
		traceId: input.traceId,
	});
	const body = new TextEncoder().encode(
		toJsonString(GatewayEventSchema, event),
	);
	const frame = new Uint8Array(5 + body.length);
	frame[0] = 0x00; // message frame
	new DataView(frame.buffer).setUint32(1, body.length, false);
	frame.set(body, 5);
	return frame;
}
