package canon

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

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

// ErrInvalidResponseSignature reports that a server response signature is
// not a raw Ed25519 signature for the canonical response signing input.
var ErrInvalidResponseSignature = errors.New("invalid response signature")

// ResponseSigningFields contains the canonical v1 response fields that are
// bound into the server signing input.
type ResponseSigningFields struct {
	// ProtocolVersion identifies the transport envelope version.
	ProtocolVersion string

	// RequestID is the transport correlation identifier copied from the
	// authenticated request.
	RequestID string

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

	// ResultCode is the opaque downstream result code returned to the client.
	ResultCode string

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

// BuildResponseSigningInput returns the canonical byte sequence the v1 server
// response 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 BuildResponseSigningInput(fields ResponseSigningFields) []byte {
	size := len(ResponseDomainMarkerV1) +
		len(fields.ProtocolVersion) +
		len(fields.RequestID) +
		len(fields.ResultCode) +
		len(fields.PayloadHash) +
		(5 * binary.MaxVarintLen64) +
		8

	buf := make([]byte, 0, size)
	buf = appendLengthPrefixedString(buf, ResponseDomainMarkerV1)
	buf = appendLengthPrefixedString(buf, fields.ProtocolVersion)
	buf = appendLengthPrefixedString(buf, fields.RequestID)
	buf = binary.BigEndian.AppendUint64(buf, uint64(fields.TimestampMS))
	buf = appendLengthPrefixedString(buf, fields.ResultCode)
	buf = appendLengthPrefixedBytes(buf, fields.PayloadHash)

	return buf
}

// VerifyResponseSignature verifies that signature authenticates fields under
// publicKey using the canonical v1 response signing input.
func VerifyResponseSignature(publicKey ed25519.PublicKey, signature []byte, fields ResponseSigningFields) error {
	if len(publicKey) != ed25519.PublicKeySize || len(signature) != ed25519.SignatureSize {
		return ErrInvalidResponseSignature
	}
	if !ed25519.Verify(publicKey, BuildResponseSigningInput(fields), signature) {
		return ErrInvalidResponseSignature
	}

	return nil
}
