package runtimeapi

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

	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/shared/pkg/models"
	delegationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/delegationv1"
	pagelayoutsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/pagelayoutresolutionv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
)

func TestPageLayoutConsumerAdmissionBindsExactViewToRealResolution(t *testing.T) {
	view := pageLayoutResolutionServerTestView(t, false)
	admission, err := NewPageLayoutConsumerAdmission(PageLayoutConsumerSelection{
		OrgID: testOrgID, AppInstallationID: testInstallationID,
		WorkloadID: testWorkloadID, RuntimeMetadataView: view,
		CurrentSecurityEpoch: func() (uint64, bool) { return 3, true },
	})
	if err != nil {
		t.Fatal(err)
	}
	bound, err := admission.bind(pageLayoutConsumerTrustedContext(t, testWorkloadID, 3))
	if err != nil {
		t.Fatal(err)
	}
	if boundView, ok := metadata.RuntimeMetadataViewFromContext(bound); !ok || boundView != view {
		t.Fatal("consumer admission did not bind the exact PageLayout Runtime view")
	}

	server, err := NewPageLayoutResolutionServer(pageLayoutResolverStub{view: view}, nil)
	if err != nil {
		t.Fatal(err)
	}
	request := pagelayoutsemantic.Request{
		ObjectID: "WorkItem", RecordTypeIDs: []string{"rt-enterprise"},
	}
	wireRequest, err := mapping.ToWireResolvePageLayoutRequest(bound, request)
	if err != nil {
		t.Fatal(err)
	}
	wireResponse, err := server.ResolvePageLayout(bound, wireRequest)
	if err != nil {
		t.Fatal(err)
	}
	response, err := mapping.FromWireResolvePageLayoutResponse(request, wireResponse)
	if err != nil {
		t.Fatal(err)
	}
	if len(response.Selections) != 1 ||
		response.Selections[0].LayoutID != "layout-profile-record-type" ||
		response.Receipts.CatalogRevision != uint64(view.Lineage().CatalogRevision) ||
		response.Receipts.SecurityEpoch != 3 {
		t.Fatalf("consumer-bound PageLayout response = %#v", response)
	}
}

func TestPageLayoutConsumerAdmissionRejectsWrongWorkloadAndSecurityEpoch(t *testing.T) {
	view := pageLayoutResolutionServerTestView(t, false)
	admission, err := NewPageLayoutConsumerAdmission(PageLayoutConsumerSelection{
		OrgID: testOrgID, AppInstallationID: testInstallationID,
		WorkloadID: testWorkloadID, RuntimeMetadataView: view,
		CurrentSecurityEpoch: func() (uint64, bool) { return 4, true },
	})
	if err != nil {
		t.Fatal(err)
	}
	for _, test := range []struct {
		name      string
		ctx       context.Context
		want      pagelayoutsemantic.FailureCategory
		staleAxis pagelayoutsemantic.StaleAxis
	}{
		{
			name: "wrong workload",
			ctx:  pageLayoutConsumerTrustedContext(t, "other-workload", 4),
			want: pagelayoutsemantic.FailureForbidden,
		},
		{
			name:      "stale security epoch",
			ctx:       pageLayoutConsumerTrustedContext(t, testWorkloadID, 3),
			want:      pagelayoutsemantic.FailureStaleState,
			staleAxis: pagelayoutsemantic.StaleAxisSecurity,
		},
	} {
		t.Run(test.name, func(t *testing.T) {
			_, bindErr := admission.bind(test.ctx)
			var failure *pagelayoutsemantic.Failure
			if !errors.As(bindErr, &failure) || failure.Category != test.want ||
				failure.StaleAxis != test.staleAxis {
				t.Fatalf("bind error = %#v, want category=%q staleAxis=%q", bindErr, test.want, test.staleAxis)
			}
		})
	}
}

func pageLayoutConsumerTrustedContext(
	t *testing.T,
	workloadID string,
	securityEpoch uint64,
) context.Context {
	t.Helper()
	trusted := newTrustedCall(delegationsemantic.Claims{
		DelegationID: "delegation-user-1", OrgID: testOrgID, PrincipalID: "user-1",
		SessionID: "session-user-1", CallerWorkloadID: workloadID,
		AppInstallationID: testInstallationID, RuntimeAudience: "runtime-test",
		Capabilities: []string{pagelayoutsemantic.CapabilityName},
		AuthEpoch:    2, SecurityEpoch: securityEpoch,
	}, models.UserSession{ID: "user-1", ProfileID: "profile-session"})
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	t.Cleanup(cancel)
	return context.WithValue(ctx, trustedCallContextKey{}, trusted)
}
