package runtimegrpc

import (
	"context"
	"strings"
	"testing"

	"google.golang.org/grpc/metadata"
)

func TestRequestIDCorrelationSurvivesProtocolEnvelope(t *testing.T) {
	ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
		"traceparent", "trace-1",
		RequestIDMetadataKey, "stale",
	))
	ctx = WithRequestID(ctx, "req-123:attempt_1")
	ctx = withClientProtocol(ctx, 1, 0, "runtime.test.v1")

	outgoing, ok := metadata.FromOutgoingContext(ctx)
	if !ok {
		t.Fatal("outgoing metadata is missing")
	}
	assertExactly(t, outgoing.Get(RequestIDMetadataKey), "req-123:attempt_1")
	assertExactly(t, outgoing.Get("traceparent"), "trace-1")

	incoming := metadata.NewIncomingContext(context.Background(), outgoing)
	if got, ok := IncomingRequestID(incoming); !ok || got != "req-123:attempt_1" {
		t.Fatalf("IncomingRequestID() = %q, %t", got, ok)
	}
}

func TestRequestIDCorrelationRejectsAmbiguousAndMalformedValues(t *testing.T) {
	tests := []struct {
		name   string
		values []string
	}{
		{name: "missing"},
		{name: "multiple", values: []string{"req-1", "req-2"}},
		{name: "spaces", values: []string{" req-1 "}},
		{name: "control", values: []string{"req\n1"}},
		{name: "oversized", values: []string{strings.Repeat("a", maxRequestIDBytes+1)}},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			ctx := metadata.NewIncomingContext(context.Background(), metadata.MD{
				RequestIDMetadataKey: test.values,
			})
			if got, ok := IncomingRequestID(ctx); ok || got != "" {
				t.Fatalf("IncomingRequestID() = %q, %t", got, ok)
			}
		})
	}
}

func TestWithRequestIDRemovesInvalidPriorCorrelation(t *testing.T) {
	ctx := metadata.NewOutgoingContext(context.Background(), metadata.Pairs(
		RequestIDMetadataKey, "stale",
		"traceparent", "trace-1",
	))
	ctx = WithRequestID(ctx, "invalid request id")
	values, ok := metadata.FromOutgoingContext(ctx)
	if !ok {
		t.Fatal("outgoing metadata is missing")
	}
	if got := values.Get(RequestIDMetadataKey); len(got) != 0 {
		t.Fatalf("request id metadata = %#v, want absent", got)
	}
	assertExactly(t, values.Get("traceparent"), "trace-1")
}
