package transcoder

import (
	"fmt"

	commonfbs "galaxy/schema/fbs/common"
	notificationfbs "galaxy/schema/fbs/notification"

	flatbuffers "github.com/google/flatbuffers/go"
	"github.com/google/uuid"
)

// LobbyInviteReceivedEvent is the Go-side payload for the
// `lobby.invite.received` catalog kind.
type LobbyInviteReceivedEvent struct {
	GameID        uuid.UUID
	InviterUserID uuid.UUID
}

// LobbyInviteRevokedEvent is the Go-side payload for the
// `lobby.invite.revoked` catalog kind.
type LobbyInviteRevokedEvent struct {
	GameID uuid.UUID
}

// LobbyApplicationSubmittedEvent is the Go-side payload for the
// `lobby.application.submitted` catalog kind.
type LobbyApplicationSubmittedEvent struct {
	GameID        uuid.UUID
	ApplicationID uuid.UUID
}

// LobbyApplicationApprovedEvent is the Go-side payload for the
// `lobby.application.approved` catalog kind.
type LobbyApplicationApprovedEvent struct {
	GameID uuid.UUID
}

// LobbyApplicationRejectedEvent is the Go-side payload for the
// `lobby.application.rejected` catalog kind.
type LobbyApplicationRejectedEvent struct {
	GameID uuid.UUID
}

// LobbyMembershipRemovedEvent is the Go-side payload for the
// `lobby.membership.removed` catalog kind. Reason carries the
// upstream lifecycle event that triggered the cascade
// (`permanent_blocked`, `deleted`).
type LobbyMembershipRemovedEvent struct {
	Reason string
}

// LobbyMembershipBlockedEvent is the Go-side payload for the
// `lobby.membership.blocked` catalog kind.
type LobbyMembershipBlockedEvent struct {
	GameID uuid.UUID
	Reason string
}

// LobbyRaceNameRegisteredEvent is the Go-side payload for the
// `lobby.race_name.registered` catalog kind.
type LobbyRaceNameRegisteredEvent struct {
	RaceName string
}

// LobbyRaceNamePendingEvent is the Go-side payload for the
// `lobby.race_name.pending` catalog kind. ExpiresAt is an RFC 3339
// timestamp matching the producer in
// `backend/internal/lobby/runtime_hooks.go`.
type LobbyRaceNamePendingEvent struct {
	RaceName  string
	ExpiresAt string
}

// LobbyRaceNameExpiredEvent is the Go-side payload for the
// `lobby.race_name.expired` catalog kind.
type LobbyRaceNameExpiredEvent struct {
	RaceName string
}

// RuntimeImagePullFailedEvent is the Go-side payload for the
// `runtime.image_pull_failed` catalog kind (admin recipient).
type RuntimeImagePullFailedEvent struct {
	GameID   uuid.UUID
	ImageRef string
}

// RuntimeContainerStartFailedEvent is the Go-side payload for the
// `runtime.container_start_failed` catalog kind (admin recipient).
type RuntimeContainerStartFailedEvent struct {
	GameID uuid.UUID
}

// RuntimeStartConfigInvalidEvent is the Go-side payload for the
// `runtime.start_config_invalid` catalog kind (admin recipient).
type RuntimeStartConfigInvalidEvent struct {
	GameID uuid.UUID
	Reason string
}

// LobbyInviteReceivedEventToPayload encodes the event into the FlatBuffers
// bytes published on the gateway client event stream.
func LobbyInviteReceivedEventToPayload(event *LobbyInviteReceivedEvent) ([]byte, error) {
	const op = "encode lobby invite received payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	if event.InviterUserID == uuid.Nil {
		return nil, fmt.Errorf("%s: inviter_user_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(64)
	notificationfbs.LobbyInviteReceivedEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.LobbyInviteReceivedEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	hi, lo = uuidToHiLo(event.InviterUserID)
	notificationfbs.LobbyInviteReceivedEventAddInviterUserId(builder, commonfbs.CreateUUID(builder, hi, lo))
	offset := notificationfbs.LobbyInviteReceivedEventEnd(builder)
	notificationfbs.FinishLobbyInviteReceivedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyInviteReceivedEvent decodes FlatBuffers bytes into a
// LobbyInviteReceivedEvent.
func PayloadToLobbyInviteReceivedEvent(data []byte) (result *LobbyInviteReceivedEvent, err error) {
	const op = "decode lobby invite received payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyInviteReceivedEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	inviter := flat.InviterUserId(nil)
	if inviter == nil {
		return nil, fmt.Errorf("%s: inviter_user_id is missing", op)
	}
	return &LobbyInviteReceivedEvent{
		GameID:        uuidFromHiLo(gameID.Hi(), gameID.Lo()),
		InviterUserID: uuidFromHiLo(inviter.Hi(), inviter.Lo()),
	}, nil
}

// LobbyInviteRevokedEventToPayload encodes the event into the FlatBuffers
// bytes published on the gateway client event stream.
func LobbyInviteRevokedEventToPayload(event *LobbyInviteRevokedEvent) ([]byte, error) {
	const op = "encode lobby invite revoked payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(48)
	notificationfbs.LobbyInviteRevokedEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.LobbyInviteRevokedEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	offset := notificationfbs.LobbyInviteRevokedEventEnd(builder)
	notificationfbs.FinishLobbyInviteRevokedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyInviteRevokedEvent decodes FlatBuffers bytes into a
// LobbyInviteRevokedEvent.
func PayloadToLobbyInviteRevokedEvent(data []byte) (result *LobbyInviteRevokedEvent, err error) {
	const op = "decode lobby invite revoked payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyInviteRevokedEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	return &LobbyInviteRevokedEvent{GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo())}, nil
}

// LobbyApplicationSubmittedEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func LobbyApplicationSubmittedEventToPayload(event *LobbyApplicationSubmittedEvent) ([]byte, error) {
	const op = "encode lobby application submitted payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	if event.ApplicationID == uuid.Nil {
		return nil, fmt.Errorf("%s: application_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(64)
	notificationfbs.LobbyApplicationSubmittedEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.LobbyApplicationSubmittedEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	hi, lo = uuidToHiLo(event.ApplicationID)
	notificationfbs.LobbyApplicationSubmittedEventAddApplicationId(builder, commonfbs.CreateUUID(builder, hi, lo))
	offset := notificationfbs.LobbyApplicationSubmittedEventEnd(builder)
	notificationfbs.FinishLobbyApplicationSubmittedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyApplicationSubmittedEvent decodes FlatBuffers bytes
// into a LobbyApplicationSubmittedEvent.
func PayloadToLobbyApplicationSubmittedEvent(data []byte) (result *LobbyApplicationSubmittedEvent, err error) {
	const op = "decode lobby application submitted payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyApplicationSubmittedEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	appID := flat.ApplicationId(nil)
	if appID == nil {
		return nil, fmt.Errorf("%s: application_id is missing", op)
	}
	return &LobbyApplicationSubmittedEvent{
		GameID:        uuidFromHiLo(gameID.Hi(), gameID.Lo()),
		ApplicationID: uuidFromHiLo(appID.Hi(), appID.Lo()),
	}, nil
}

// LobbyApplicationApprovedEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func LobbyApplicationApprovedEventToPayload(event *LobbyApplicationApprovedEvent) ([]byte, error) {
	const op = "encode lobby application approved payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(48)
	notificationfbs.LobbyApplicationApprovedEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.LobbyApplicationApprovedEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	offset := notificationfbs.LobbyApplicationApprovedEventEnd(builder)
	notificationfbs.FinishLobbyApplicationApprovedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyApplicationApprovedEvent decodes FlatBuffers bytes
// into a LobbyApplicationApprovedEvent.
func PayloadToLobbyApplicationApprovedEvent(data []byte) (result *LobbyApplicationApprovedEvent, err error) {
	const op = "decode lobby application approved payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyApplicationApprovedEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	return &LobbyApplicationApprovedEvent{GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo())}, nil
}

// LobbyApplicationRejectedEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func LobbyApplicationRejectedEventToPayload(event *LobbyApplicationRejectedEvent) ([]byte, error) {
	const op = "encode lobby application rejected payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(48)
	notificationfbs.LobbyApplicationRejectedEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.LobbyApplicationRejectedEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	offset := notificationfbs.LobbyApplicationRejectedEventEnd(builder)
	notificationfbs.FinishLobbyApplicationRejectedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyApplicationRejectedEvent decodes FlatBuffers bytes
// into a LobbyApplicationRejectedEvent.
func PayloadToLobbyApplicationRejectedEvent(data []byte) (result *LobbyApplicationRejectedEvent, err error) {
	const op = "decode lobby application rejected payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyApplicationRejectedEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	return &LobbyApplicationRejectedEvent{GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo())}, nil
}

// LobbyMembershipRemovedEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func LobbyMembershipRemovedEventToPayload(event *LobbyMembershipRemovedEvent) ([]byte, error) {
	const op = "encode lobby membership removed payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	builder := flatbuffers.NewBuilder(48)
	var reasonOff flatbuffers.UOffsetT
	if event.Reason != "" {
		reasonOff = builder.CreateString(event.Reason)
	}
	notificationfbs.LobbyMembershipRemovedEventStart(builder)
	if reasonOff != 0 {
		notificationfbs.LobbyMembershipRemovedEventAddReason(builder, reasonOff)
	}
	offset := notificationfbs.LobbyMembershipRemovedEventEnd(builder)
	notificationfbs.FinishLobbyMembershipRemovedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyMembershipRemovedEvent decodes FlatBuffers bytes into
// a LobbyMembershipRemovedEvent.
func PayloadToLobbyMembershipRemovedEvent(data []byte) (result *LobbyMembershipRemovedEvent, err error) {
	const op = "decode lobby membership removed payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyMembershipRemovedEvent(data, 0)
	return &LobbyMembershipRemovedEvent{Reason: string(flat.Reason())}, nil
}

// LobbyMembershipBlockedEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func LobbyMembershipBlockedEventToPayload(event *LobbyMembershipBlockedEvent) ([]byte, error) {
	const op = "encode lobby membership blocked payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(64)
	var reasonOff flatbuffers.UOffsetT
	if event.Reason != "" {
		reasonOff = builder.CreateString(event.Reason)
	}
	notificationfbs.LobbyMembershipBlockedEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.LobbyMembershipBlockedEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	if reasonOff != 0 {
		notificationfbs.LobbyMembershipBlockedEventAddReason(builder, reasonOff)
	}
	offset := notificationfbs.LobbyMembershipBlockedEventEnd(builder)
	notificationfbs.FinishLobbyMembershipBlockedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyMembershipBlockedEvent decodes FlatBuffers bytes into
// a LobbyMembershipBlockedEvent.
func PayloadToLobbyMembershipBlockedEvent(data []byte) (result *LobbyMembershipBlockedEvent, err error) {
	const op = "decode lobby membership blocked payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyMembershipBlockedEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	return &LobbyMembershipBlockedEvent{
		GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo()),
		Reason: string(flat.Reason()),
	}, nil
}

// LobbyRaceNameRegisteredEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func LobbyRaceNameRegisteredEventToPayload(event *LobbyRaceNameRegisteredEvent) ([]byte, error) {
	const op = "encode lobby race name registered payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.RaceName == "" {
		return nil, fmt.Errorf("%s: race_name is empty", op)
	}
	builder := flatbuffers.NewBuilder(48)
	raceName := builder.CreateString(event.RaceName)
	notificationfbs.LobbyRaceNameRegisteredEventStart(builder)
	notificationfbs.LobbyRaceNameRegisteredEventAddRaceName(builder, raceName)
	offset := notificationfbs.LobbyRaceNameRegisteredEventEnd(builder)
	notificationfbs.FinishLobbyRaceNameRegisteredEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyRaceNameRegisteredEvent decodes FlatBuffers bytes into
// a LobbyRaceNameRegisteredEvent.
func PayloadToLobbyRaceNameRegisteredEvent(data []byte) (result *LobbyRaceNameRegisteredEvent, err error) {
	const op = "decode lobby race name registered payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyRaceNameRegisteredEvent(data, 0)
	raceName, err := requiredNotificationString(flat.RaceName(), "race_name")
	if err != nil {
		return nil, fmt.Errorf("%s: %w", op, err)
	}
	return &LobbyRaceNameRegisteredEvent{RaceName: raceName}, nil
}

// LobbyRaceNamePendingEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func LobbyRaceNamePendingEventToPayload(event *LobbyRaceNamePendingEvent) ([]byte, error) {
	const op = "encode lobby race name pending payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.RaceName == "" {
		return nil, fmt.Errorf("%s: race_name is empty", op)
	}
	builder := flatbuffers.NewBuilder(64)
	raceName := builder.CreateString(event.RaceName)
	var expiresAtOff flatbuffers.UOffsetT
	if event.ExpiresAt != "" {
		expiresAtOff = builder.CreateString(event.ExpiresAt)
	}
	notificationfbs.LobbyRaceNamePendingEventStart(builder)
	notificationfbs.LobbyRaceNamePendingEventAddRaceName(builder, raceName)
	if expiresAtOff != 0 {
		notificationfbs.LobbyRaceNamePendingEventAddExpiresAt(builder, expiresAtOff)
	}
	offset := notificationfbs.LobbyRaceNamePendingEventEnd(builder)
	notificationfbs.FinishLobbyRaceNamePendingEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyRaceNamePendingEvent decodes FlatBuffers bytes into a
// LobbyRaceNamePendingEvent.
func PayloadToLobbyRaceNamePendingEvent(data []byte) (result *LobbyRaceNamePendingEvent, err error) {
	const op = "decode lobby race name pending payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyRaceNamePendingEvent(data, 0)
	raceName, err := requiredNotificationString(flat.RaceName(), "race_name")
	if err != nil {
		return nil, fmt.Errorf("%s: %w", op, err)
	}
	return &LobbyRaceNamePendingEvent{
		RaceName:  raceName,
		ExpiresAt: string(flat.ExpiresAt()),
	}, nil
}

// LobbyRaceNameExpiredEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func LobbyRaceNameExpiredEventToPayload(event *LobbyRaceNameExpiredEvent) ([]byte, error) {
	const op = "encode lobby race name expired payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.RaceName == "" {
		return nil, fmt.Errorf("%s: race_name is empty", op)
	}
	builder := flatbuffers.NewBuilder(48)
	raceName := builder.CreateString(event.RaceName)
	notificationfbs.LobbyRaceNameExpiredEventStart(builder)
	notificationfbs.LobbyRaceNameExpiredEventAddRaceName(builder, raceName)
	offset := notificationfbs.LobbyRaceNameExpiredEventEnd(builder)
	notificationfbs.FinishLobbyRaceNameExpiredEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToLobbyRaceNameExpiredEvent decodes FlatBuffers bytes into a
// LobbyRaceNameExpiredEvent.
func PayloadToLobbyRaceNameExpiredEvent(data []byte) (result *LobbyRaceNameExpiredEvent, err error) {
	const op = "decode lobby race name expired payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsLobbyRaceNameExpiredEvent(data, 0)
	raceName, err := requiredNotificationString(flat.RaceName(), "race_name")
	if err != nil {
		return nil, fmt.Errorf("%s: %w", op, err)
	}
	return &LobbyRaceNameExpiredEvent{RaceName: raceName}, nil
}

// RuntimeImagePullFailedEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func RuntimeImagePullFailedEventToPayload(event *RuntimeImagePullFailedEvent) ([]byte, error) {
	const op = "encode runtime image pull failed payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(64)
	var imageRefOff flatbuffers.UOffsetT
	if event.ImageRef != "" {
		imageRefOff = builder.CreateString(event.ImageRef)
	}
	notificationfbs.RuntimeImagePullFailedEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.RuntimeImagePullFailedEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	if imageRefOff != 0 {
		notificationfbs.RuntimeImagePullFailedEventAddImageRef(builder, imageRefOff)
	}
	offset := notificationfbs.RuntimeImagePullFailedEventEnd(builder)
	notificationfbs.FinishRuntimeImagePullFailedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToRuntimeImagePullFailedEvent decodes FlatBuffers bytes into
// a RuntimeImagePullFailedEvent.
func PayloadToRuntimeImagePullFailedEvent(data []byte) (result *RuntimeImagePullFailedEvent, err error) {
	const op = "decode runtime image pull failed payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsRuntimeImagePullFailedEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	return &RuntimeImagePullFailedEvent{
		GameID:   uuidFromHiLo(gameID.Hi(), gameID.Lo()),
		ImageRef: string(flat.ImageRef()),
	}, nil
}

// RuntimeContainerStartFailedEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func RuntimeContainerStartFailedEventToPayload(event *RuntimeContainerStartFailedEvent) ([]byte, error) {
	const op = "encode runtime container start failed payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(48)
	notificationfbs.RuntimeContainerStartFailedEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.RuntimeContainerStartFailedEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	offset := notificationfbs.RuntimeContainerStartFailedEventEnd(builder)
	notificationfbs.FinishRuntimeContainerStartFailedEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToRuntimeContainerStartFailedEvent decodes FlatBuffers bytes
// into a RuntimeContainerStartFailedEvent.
func PayloadToRuntimeContainerStartFailedEvent(data []byte) (result *RuntimeContainerStartFailedEvent, err error) {
	const op = "decode runtime container start failed payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsRuntimeContainerStartFailedEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	return &RuntimeContainerStartFailedEvent{GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo())}, nil
}

// RuntimeStartConfigInvalidEventToPayload encodes the event into the
// FlatBuffers bytes published on the gateway client event stream.
func RuntimeStartConfigInvalidEventToPayload(event *RuntimeStartConfigInvalidEvent) ([]byte, error) {
	const op = "encode runtime start config invalid payload"
	if event == nil {
		return nil, fmt.Errorf("%s: event is nil", op)
	}
	if event.GameID == uuid.Nil {
		return nil, fmt.Errorf("%s: game_id is empty", op)
	}
	builder := flatbuffers.NewBuilder(64)
	var reasonOff flatbuffers.UOffsetT
	if event.Reason != "" {
		reasonOff = builder.CreateString(event.Reason)
	}
	notificationfbs.RuntimeStartConfigInvalidEventStart(builder)
	hi, lo := uuidToHiLo(event.GameID)
	notificationfbs.RuntimeStartConfigInvalidEventAddGameId(builder, commonfbs.CreateUUID(builder, hi, lo))
	if reasonOff != 0 {
		notificationfbs.RuntimeStartConfigInvalidEventAddReason(builder, reasonOff)
	}
	offset := notificationfbs.RuntimeStartConfigInvalidEventEnd(builder)
	notificationfbs.FinishRuntimeStartConfigInvalidEventBuffer(builder, offset)
	return builder.FinishedBytes(), nil
}

// PayloadToRuntimeStartConfigInvalidEvent decodes FlatBuffers bytes
// into a RuntimeStartConfigInvalidEvent.
func PayloadToRuntimeStartConfigInvalidEvent(data []byte) (result *RuntimeStartConfigInvalidEvent, err error) {
	const op = "decode runtime start config invalid payload"
	if len(data) == 0 {
		return nil, fmt.Errorf("%s: data is empty", op)
	}
	defer recoverNotificationDecodePanic(op, &result, &err)
	flat := notificationfbs.GetRootAsRuntimeStartConfigInvalidEvent(data, 0)
	gameID := flat.GameId(nil)
	if gameID == nil {
		return nil, fmt.Errorf("%s: game_id is missing", op)
	}
	return &RuntimeStartConfigInvalidEvent{
		GameID: uuidFromHiLo(gameID.Hi(), gameID.Lo()),
		Reason: string(flat.Reason()),
	}, nil
}

func requiredNotificationString(value []byte, field string) (string, error) {
	if len(value) == 0 {
		return "", fmt.Errorf("%s is missing", field)
	}
	return string(value), nil
}

func recoverNotificationDecodePanic[T any](message string, result **T, err *error) {
	if recovered := recover(); recovered != nil {
		*result = nil
		*err = fmt.Errorf("%s: panic recovered: %v", message, recovered)
	}
}
