package mapping

import (
	"context"
	"errors"
	"testing"
	"time"

	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/identityv1"
	commonv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/common/v1"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
)

func identityDeadlineContext(t *testing.T) context.Context {
	t.Helper()
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	t.Cleanup(cancel)
	return ctx
}

func TestIdentityExchangeMappingRoundTrip(t *testing.T) {
	request := semantic.Request{AppInstallationID: "installation-1", RequestedCapabilities: []string{"runtime.records.read-selected-root.v1"}}
	wireRequest, err := ToWireIdentityExchangeRequest(identityDeadlineContext(t), request)
	if err != nil {
		t.Fatal(err)
	}
	decodedRequest, err := FromWireIdentityExchangeRequest(identityDeadlineContext(t), wireRequest)
	if err != nil || decodedRequest.AppInstallationID != request.AppInstallationID || len(decodedRequest.RequestedCapabilities) != 1 {
		t.Fatalf("request = %#v, %v", decodedRequest, err)
	}
	response := semantic.Response{OpaqueDelegation: "header.payload.signature", ExpiresAtUnixSecond: 42}
	wireResponse, err := ToWireIdentityExchangeResponse(response)
	if err != nil {
		t.Fatal(err)
	}
	decodedResponse, err := FromWireIdentityExchangeResponse(wireResponse)
	if err != nil || decodedResponse != response {
		t.Fatalf("response = %#v, %v", decodedResponse, err)
	}
}

func TestIdentityFailureMappingIsCapabilityBoundAndDisclosureSafe(t *testing.T) {
	for _, failure := range []*semantic.Failure{
		{Category: semantic.FailureUnauthenticated},
		{Category: semantic.FailureForbidden},
		{Category: semantic.FailureStaleState, StaleAxis: semantic.StaleAxisSecurity, RequiredRevision: 9, ServedRevision: 8},
		{Category: semantic.FailureUnavailable},
	} {
		decoded := FromIdentityStatusError(ToIdentityStatusError(failure))
		var got *semantic.Failure
		if !errors.As(decoded, &got) || got.Category != failure.Category {
			t.Fatalf("%#v -> %#v, %v", failure, got, decoded)
		}
	}
	foreign := &commonv1.RuntimeErrorDetail{
		Category: commonv1.ErrorCategory_ERROR_CATEGORY_INCOMPATIBLE,
		Detail: &commonv1.RuntimeErrorDetail_Compatibility{Compatibility: &commonv1.CompatibilityDetail{
			RequiredCapability: "runtime.records.read-selected-root.v1", SupportedProtocolMajor: 1,
		}},
	}
	foreignStatus, err := status.New(codes.FailedPrecondition, "runtime request failed").WithDetails(foreign)
	if err != nil {
		t.Fatal(err)
	}
	var got *semantic.Failure
	if decoded := FromIdentityStatusError(foreignStatus.Err()); !errors.As(decoded, &got) || got.Category != semantic.FailureInternal {
		t.Fatalf("foreign detail = %#v, %v", got, decoded)
	}
}
