package authn

import (
	"crypto/ed25519"
	"encoding/binary"
	"errors"
)

const (
	// EventDomainMarkerV1 binds the v1 server event signature to the Galaxy
	// gateway transport contract.
	EventDomainMarkerV1 = "galaxy-event-v1"
)

var (
	// ErrInvalidEventSignature reports that a gateway stream event signature is
	// not a raw Ed25519 signature for the canonical event signing input.
	ErrInvalidEventSignature = errors.New("invalid event signature")
)

// EventSigningFields contains the canonical v1 stream-event fields that are
// bound into the server signing input.
type EventSigningFields struct {
	// EventType identifies the stable client-facing event category.
	EventType string

	// EventID is the stable event correlation identifier.
	EventID string

	// TimestampMS carries the server event timestamp in milliseconds.
	TimestampMS int64

	// RequestID optionally correlates the event to the opening client request.
	RequestID string

	// TraceID optionally carries the client-supplied tracing correlation value.
	TraceID string

	// PayloadHash is the raw SHA-256 digest of event payload bytes.
	PayloadHash []byte
}

// BuildEventSigningInput returns the canonical byte sequence the v1 gateway
// stream-event signature covers. String and byte fields are length-prefixed
// with uvarint(len(field)) followed by raw bytes, while TimestampMS is
// appended as an 8-byte big-endian uint64.
func BuildEventSigningInput(fields EventSigningFields) []byte {
	size := len(EventDomainMarkerV1) +
		len(fields.EventType) +
		len(fields.EventID) +
		len(fields.RequestID) +
		len(fields.TraceID) +
		len(fields.PayloadHash) +
		(6 * binary.MaxVarintLen64) +
		8

	buf := make([]byte, 0, size)
	buf = appendLengthPrefixedString(buf, EventDomainMarkerV1)
	buf = appendLengthPrefixedString(buf, fields.EventType)
	buf = appendLengthPrefixedString(buf, fields.EventID)
	buf = binary.BigEndian.AppendUint64(buf, uint64(fields.TimestampMS))
	buf = appendLengthPrefixedString(buf, fields.RequestID)
	buf = appendLengthPrefixedString(buf, fields.TraceID)
	buf = appendLengthPrefixedBytes(buf, fields.PayloadHash)

	return buf
}

// VerifyEventSignature verifies that signature authenticates fields under
// publicKey using the canonical v1 event signing input.
func VerifyEventSignature(publicKey ed25519.PublicKey, signature []byte, fields EventSigningFields) error {
	if len(publicKey) != ed25519.PublicKeySize || len(signature) != ed25519.SignatureSize {
		return ErrInvalidEventSignature
	}
	if !ed25519.Verify(publicKey, BuildEventSigningInput(fields), signature) {
		return ErrInvalidEventSignature
	}

	return nil
}
