package mapping

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

	analyticssemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/analyticsauthoringv1"
	commonsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionadminv1"
	permissionauthoringsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/permissionauthoringv1"
	resourceaccesssemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/resourceaccessv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/envelope"
	commonv1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/common/v1"
	runtimev1 "github.com/pingcap-inc/nexuscrm-contract/wire-go/gen/runtime/v1"
)

func TestPlatformRequestAdmissionPreservesContextPrecedenceOverPayloadLimits(t *testing.T) {
	canceled, cancel := context.WithCancel(context.Background())
	cancel()
	expired, expire := context.WithDeadline(context.Background(), time.Now().Add(-time.Second))
	defer expire()

	contexts := []struct {
		name             string
		ctx              context.Context
		category         commonsemantic.FailureCategory
		invalidComponent commonsemantic.InvalidComponent
	}{
		{name: "nil", category: commonsemantic.FailureInvalidRequest, invalidComponent: commonsemantic.InvalidComponentDeadline},
		{name: "canceled", ctx: canceled, category: commonsemantic.FailureCanceled},
		{name: "expired", ctx: expired, category: commonsemantic.FailureDeadlineExceeded},
	}
	decoders := []struct {
		name   string
		decode func(context.Context) error
	}{
		{
			name: "resource access",
			decode: func(ctx context.Context) error {
				_, err := FromWireResourceAccessRequest(ctx, &runtimev1.AccessResourceRequest{
					Operation: runtimev1.ResourceAccessOperation_RESOURCE_ACCESS_OPERATION_REPLACE_GRANTS,
					Grants:    make([]*runtimev1.ResourceAccessGrantInput, resourceaccesssemantic.MaxGrants+1),
				})
				return err
			},
		},
		{
			name: "permission read",
			decode: func(ctx context.Context) error {
				_, err := FromWireReadPermissionAdministrationRequest(ctx, &runtimev1.ReadPermissionAdministrationRequest{
					Selector:        runtimev1.PermissionAdministrationReadSelector_PERMISSION_ADMINISTRATION_READ_SELECTOR_SYSTEM_GRANTS,
					PermissionSetId: strings.Repeat("p", permissionsemantic.MaxSemanticRequestBytes+1),
				})
				return err
			},
		},
		{
			name: "permission mutate",
			decode: func(ctx context.Context) error {
				_, err := FromWireMutatePermissionAdministrationRequest(ctx, &runtimev1.MutatePermissionAdministrationRequest{
					Selector: runtimev1.PermissionAdministrationMutationSelector_PERMISSION_ADMINISTRATION_MUTATION_SELECTOR_SYSTEM_GRANTS,
					Grants:   make([]*runtimev1.PermissionAdministrationGrant, permissionsemantic.MaxGrants+1),
				})
				return err
			},
		},
		{
			name: "analytics authoring",
			decode: func(ctx context.Context) error {
				_, err := FromWireMutateAnalyticsDefinitionRequest(ctx, &runtimev1.MutateAnalyticsDefinitionRequest{
					Operation: runtimev1.AnalyticsAuthoringOperation_ANALYTICS_AUTHORING_OPERATION_CREATE_DASHBOARD,
					Dashboard: &runtimev1.AnalyticsDashboardDefinition{ComponentSet: &runtimev1.AnalyticsDashboardComponentSet{
						Components: make([]*runtimev1.AnalyticsDashboardComponent, analyticssemantic.MaxDashboardComponents+1),
					}},
				})
				return err
			},
		},
		{
			name: "permission authoring snapshot",
			decode: func(ctx context.Context) error {
				_, err := FromWireReadPermissionAuthoringSnapshotRequest(ctx, &runtimev1.ReadPermissionAuthoringSnapshotRequest{
					Target: &runtimev1.PermissionAuthoringTarget{
						Kind: runtimev1.PermissionAuthoringTargetKind_PERMISSION_AUTHORING_TARGET_KIND_PROFILE,
						Id:   strings.Repeat("p", permissionauthoringsemantic.MaxSemanticRequestBytes+1),
					},
				})
				return err
			},
		},
		{
			name: "permission authoring save",
			decode: func(ctx context.Context) error {
				_, err := FromWireSavePermissionAuthoringRequest(ctx, &runtimev1.SavePermissionAuthoringRequest{
					Target: &runtimev1.PermissionAuthoringTarget{
						Kind: runtimev1.PermissionAuthoringTargetKind_PERMISSION_AUTHORING_TARGET_KIND_PROFILE,
						Id:   "profile-1",
					},
					Changes: &runtimev1.PermissionAuthoringChanges{Objects: make([]*runtimev1.PermissionAuthoringObjectGrantChange, permissionauthoringsemantic.MaxObjectChanges+1)},
				})
				return err
			},
		},
		{
			name: "permission authoring status",
			decode: func(ctx context.Context) error {
				_, err := FromWireGetPermissionAuthoringStatusRequest(ctx, &runtimev1.GetPermissionAuthoringStatusRequest{
					CommandId: strings.Repeat("c", permissionauthoringsemantic.MaxSemanticRequestBytes+1),
				})
				return err
			},
		},
	}
	for _, decoder := range decoders {
		for _, contextCase := range contexts {
			t.Run(decoder.name+"/"+contextCase.name, func(t *testing.T) {
				assertPlatformFailure(t, decoder.decode(contextCase.ctx), contextCase.category, contextCase.invalidComponent)
			})
		}
	}
}

func TestPermissionAuthoringRequestAdmissionPreservesWireTargetPrecedence(t *testing.T) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	tests := []struct {
		name   string
		decode func() error
	}{
		{
			name: "snapshot",
			decode: func() error {
				_, err := FromWireReadPermissionAuthoringSnapshotRequest(ctx, &runtimev1.ReadPermissionAuthoringSnapshotRequest{
					Target: &runtimev1.PermissionAuthoringTarget{
						Kind: runtimev1.PermissionAuthoringTargetKind(99),
						Id:   strings.Repeat("p", permissionauthoringsemantic.MaxSemanticRequestBytes+1),
					},
				})
				return err
			},
		},
		{
			name: "save",
			decode: func() error {
				_, err := FromWireSavePermissionAuthoringRequest(ctx, &runtimev1.SavePermissionAuthoringRequest{
					Target:  &runtimev1.PermissionAuthoringTarget{Kind: runtimev1.PermissionAuthoringTargetKind(99), Id: "profile-1"},
					Changes: &runtimev1.PermissionAuthoringChanges{Objects: make([]*runtimev1.PermissionAuthoringObjectGrantChange, permissionauthoringsemantic.MaxObjectChanges+1)},
				})
				return err
			},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			assertPlatformFailure(t, test.decode(), commonsemantic.FailureInvalidRequest, commonsemantic.InvalidComponentResponse)
		})
	}
}

func TestPlatformResponseAdmissionPreservesReceiptCompatibilityPrecedence(t *testing.T) {
	oversizedName := strings.Repeat("x", resourceaccesssemantic.MaxSemanticResponseBytes+1)
	permissionToken, err := permissionauthoringsemantic.NewAuthoringToken("ps-1", 1)
	if err != nil {
		t.Fatal(err)
	}
	permissionRequest := permissionauthoringsemantic.Request{
		CommandID: "cmd-precedence", Target: permissionauthoringsemantic.Target{Kind: permissionauthoringsemantic.TargetPermissionSet, ID: "ps-1"},
		ExpectedAuthoringToken: permissionToken,
		Changes:                permissionauthoringsemantic.Changes{System: []permissionauthoringsemantic.CapabilityGrantChange{{CapabilityID: "ViewSetup", Enabled: true}}},
	}
	permissionStatusRequest, err := permissionauthoringsemantic.StatusRequestFor(permissionRequest)
	if err != nil {
		t.Fatal(err)
	}
	receipts := []struct {
		name    string
		receipt *commonv1.ConsistencyReceipts
	}{
		{
			name: "missing protocol",
			receipt: &commonv1.ConsistencyReceipts{
				CapabilityVersion: &commonv1.CapabilityVersion{Name: "wrong", Major: 1},
			},
		},
		{
			name: "missing capability",
			receipt: &commonv1.ConsistencyReceipts{
				ProtocolVersion: &commonv1.ProtocolVersion{Major: 1},
			},
		},
		{
			name: "oversized incompatible capability",
			receipt: &commonv1.ConsistencyReceipts{
				ProtocolVersion:   &commonv1.ProtocolVersion{Major: 1},
				CapabilityVersion: &commonv1.CapabilityVersion{Name: oversizedName, Major: 1},
			},
		},
	}
	decoders := []struct {
		name   string
		decode func(*commonv1.ConsistencyReceipts) error
	}{
		{
			name: "resource access",
			decode: func(receipt *commonv1.ConsistencyReceipts) error {
				_, err := FromWireResourceAccessResponse(
					resourceaccesssemantic.Request{Operation: resourceaccesssemantic.ListGrants, ResourceType: "folder", ResourceID: "folder-1"},
					&runtimev1.AccessResourceResponse{Receipts: receipt},
				)
				return err
			},
		},
		{
			name: "permission read",
			decode: func(receipt *commonv1.ConsistencyReceipts) error {
				_, err := FromWireReadPermissionAdministrationResponse(
					permissionsemantic.ReadRequest{Selector: permissionsemantic.ReadSystemCatalog},
					&runtimev1.ReadPermissionAdministrationResponse{Receipts: receipt},
				)
				return err
			},
		},
		{
			name: "permission mutate",
			decode: func(receipt *commonv1.ConsistencyReceipts) error {
				_, err := FromWireMutatePermissionAdministrationResponse(
					permissionsemantic.MutateRequest{Selector: permissionsemantic.MutateSystemGrants, PermissionSetID: "ps-1"},
					&runtimev1.MutatePermissionAdministrationResponse{Receipts: receipt},
				)
				return err
			},
		},
		{
			name: "analytics authoring",
			decode: func(receipt *commonv1.ConsistencyReceipts) error {
				_, err := FromWireMutateAnalyticsDefinitionResponse(
					analyticssemantic.Request{CommandID: "delete-1", Operation: analyticssemantic.DeleteFolder, ResourceID: "folder-1"},
					&runtimev1.MutateAnalyticsDefinitionResponse{Receipts: receipt},
				)
				return err
			},
		},
		{
			name: "permission authoring snapshot",
			decode: func(receipt *commonv1.ConsistencyReceipts) error {
				_, err := FromWireReadPermissionAuthoringSnapshotResponse(
					permissionauthoringsemantic.SnapshotRequest{Target: permissionauthoringsemantic.Target{Kind: permissionauthoringsemantic.TargetProfile, ID: "profile-1"}},
					&runtimev1.ReadPermissionAuthoringSnapshotResponse{
						Catalog:  &runtimev1.PermissionAuthoringCatalog{},
						Grants:   &runtimev1.PermissionAuthoringStoredGrants{Objects: make([]*runtimev1.PermissionAuthoringObjectGrant, permissionauthoringsemantic.MaxObjectChanges+1)},
						Receipts: receipt,
					},
				)
				return err
			},
		},
		{
			name: "permission authoring save",
			decode: func(receipt *commonv1.ConsistencyReceipts) error {
				_, err := FromWireSavePermissionAuthoringResponse(
					permissionRequest,
					&runtimev1.SavePermissionAuthoringResponse{CommandId: permissionRequest.CommandID, Receipts: receipt},
				)
				return err
			},
		},
		{
			name: "permission authoring status terminal",
			decode: func(receipt *commonv1.ConsistencyReceipts) error {
				_, err := FromWireGetPermissionAuthoringStatusResponse(
					permissionStatusRequest,
					&runtimev1.GetPermissionAuthoringStatusResponse{
						CommandId: permissionStatusRequest.CommandID,
						State:     runtimev1.PermissionAuthoringCommandState_PERMISSION_AUTHORING_COMMAND_STATE_COMPLETED,
						Result:    &runtimev1.SavePermissionAuthoringResponse{CommandId: permissionRequest.CommandID, Receipts: receipt},
					},
				)
				return err
			},
		},
	}
	for _, decoder := range decoders {
		for _, receiptCase := range receipts {
			t.Run(decoder.name+"/"+receiptCase.name, func(t *testing.T) {
				assertPlatformFailure(t, decoder.decode(receiptCase.receipt), commonsemantic.FailureIncompatible, "")
			})
		}
	}
}

func TestPermissionAuthoringResponseAdmissionClassifiesPeerPayloadAfterCompatibility(t *testing.T) {
	token, err := permissionauthoringsemantic.NewAuthoringToken("ps-1", 1)
	if err != nil {
		t.Fatal(err)
	}
	request := permissionauthoringsemantic.Request{
		CommandID: "cmd-peer-payload", Target: permissionauthoringsemantic.Target{Kind: permissionauthoringsemantic.TargetPermissionSet, ID: "ps-1"},
		ExpectedAuthoringToken: token,
		Changes:                permissionauthoringsemantic.Changes{System: []permissionauthoringsemantic.CapabilityGrantChange{{CapabilityID: "ViewSetup", Enabled: true}}},
	}
	statusRequest, err := permissionauthoringsemantic.StatusRequestFor(request)
	if err != nil {
		t.Fatal(err)
	}
	validReceipts := toWireReceipts(permissionAuthoringReceipts(), platformCapability(permissionauthoringsemantic.CapabilityName))
	validDigest, err := digestToWire(statusRequest.CommandDigest)
	if err != nil {
		t.Fatal(err)
	}
	tests := []struct {
		name   string
		decode func() error
	}{
		{
			name: "snapshot missing catalog",
			decode: func() error {
				_, err := FromWireReadPermissionAuthoringSnapshotResponse(
					permissionauthoringsemantic.SnapshotRequest{Target: request.Target},
					&runtimev1.ReadPermissionAuthoringSnapshotResponse{Grants: &runtimev1.PermissionAuthoringStoredGrants{}, Receipts: validReceipts},
				)
				return err
			},
		},
		{
			name: "save malformed digest",
			decode: func() error {
				_, err := FromWireSavePermissionAuthoringResponse(request, &runtimev1.SavePermissionAuthoringResponse{Receipts: validReceipts})
				return err
			},
		},
		{
			name: "status malformed outer digest",
			decode: func() error {
				_, err := FromWireGetPermissionAuthoringStatusResponse(
					statusRequest,
					&runtimev1.GetPermissionAuthoringStatusResponse{
						CommandId: request.CommandID,
						State:     runtimev1.PermissionAuthoringCommandState_PERMISSION_AUTHORING_COMMAND_STATE_COMPLETED,
						Result:    &runtimev1.SavePermissionAuthoringResponse{Receipts: validReceipts},
					},
				)
				return err
			},
		},
		{
			name: "status malformed terminal digest",
			decode: func() error {
				_, err := FromWireGetPermissionAuthoringStatusResponse(
					statusRequest,
					&runtimev1.GetPermissionAuthoringStatusResponse{
						CommandId: request.CommandID, SemanticSha256: validDigest,
						State:  runtimev1.PermissionAuthoringCommandState_PERMISSION_AUTHORING_COMMAND_STATE_COMPLETED,
						Result: &runtimev1.SavePermissionAuthoringResponse{CommandId: request.CommandID, Receipts: validReceipts},
					},
				)
				return err
			},
		},
		{
			name: "status nonterminal result",
			decode: func() error {
				_, err := FromWireGetPermissionAuthoringStatusResponse(
					statusRequest,
					&runtimev1.GetPermissionAuthoringStatusResponse{
						CommandId: request.CommandID, SemanticSha256: validDigest,
						State:  runtimev1.PermissionAuthoringCommandState_PERMISSION_AUTHORING_COMMAND_STATE_ACCEPTED,
						Result: &runtimev1.SavePermissionAuthoringResponse{},
					},
				)
				return err
			},
		},
		{
			name: "status completed without result",
			decode: func() error {
				_, err := FromWireGetPermissionAuthoringStatusResponse(
					statusRequest,
					&runtimev1.GetPermissionAuthoringStatusResponse{
						CommandId: request.CommandID, SemanticSha256: validDigest,
						State: runtimev1.PermissionAuthoringCommandState_PERMISSION_AUTHORING_COMMAND_STATE_COMPLETED,
					},
				)
				return err
			},
		},
		{
			name: "save oversized frame",
			decode: func() error {
				_, err := FromWireSavePermissionAuthoringResponse(
					request,
					&runtimev1.SavePermissionAuthoringResponse{
						SemanticSha256: validDigest, Receipts: validReceipts,
						OutcomeExpiresAtUtc: strings.Repeat("x", envelope.MaxResponseWireBytes),
					},
				)
				return err
			},
		},
		{
			name: "status oversized frame",
			decode: func() error {
				_, err := FromWireGetPermissionAuthoringStatusResponse(
					statusRequest,
					&runtimev1.GetPermissionAuthoringStatusResponse{
						CommandId: strings.Repeat("x", envelope.MaxResponseWireBytes), SemanticSha256: validDigest,
						State:  runtimev1.PermissionAuthoringCommandState_PERMISSION_AUTHORING_COMMAND_STATE_COMPLETED,
						Result: &runtimev1.SavePermissionAuthoringResponse{SemanticSha256: validDigest, Receipts: validReceipts},
					},
				)
				return err
			},
		},
	}
	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			assertPlatformFailure(t, test.decode(), commonsemantic.FailureInternal, "")
		})
	}
}

func assertPlatformFailure(
	t *testing.T,
	err error,
	category commonsemantic.FailureCategory,
	invalidComponent commonsemantic.InvalidComponent,
) {
	t.Helper()
	var failure *commonsemantic.Failure
	if !errors.As(err, &failure) || failure.Category != category || failure.InvalidComponent != invalidComponent {
		t.Fatalf("failure = %#v, want %s/%s", err, category, invalidComponent)
	}
}
