package runtimeapi

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

	appErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/shared/pkg/models"
	delegation "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/delegationv1"
	semantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recorddefaultsv1"
	"github.com/pingcap-inc/nexuscrm-contract/wire-go/mapping"
)

func TestClassifyCreateDefaultPreviewEngineErrorTreatsObjectMetadataAsInvalidObject(t *testing.T) {
	classified := classifyCreateDefaultPreviewEngineError(
		context.Background(),
		appErrors.NewNotFoundError("Object Metadata", "MissingObject"),
	)
	var failure *semantic.Failure
	if !errors.As(classified, &failure) {
		t.Fatalf("classified error = %T %v, want semantic failure", classified, classified)
	}
	if failure.Category != semantic.FailureInvalidRequest ||
		failure.InvalidComponent != semantic.InvalidComponentObjectID {
		t.Fatalf("classified failure = %#v, want invalid object ID", failure)
	}
}

func TestClassifyCreateDefaultPreviewEngineErrorUsesPlatformDomainMapping(t *testing.T) {
	classified := classifyCreateDefaultPreviewEngineError(
		context.Background(),
		appErrors.NewFieldIntegrityError("ParentId is required", "ParentId"),
	)
	var failure *semantic.Failure
	if !errors.As(classified, &failure) {
		t.Fatalf("classified error = %T %v, want semantic failure", classified, classified)
	}
	if failure.Category != semantic.FailureInvalidRequest ||
		failure.InvalidComponent != semantic.InvalidComponentFieldValue {
		t.Fatalf("classified failure = %#v, want invalid field value", failure)
	}
}

type createDefaultPreviewEngineStub struct {
	request semantic.Request
	user    *models.UserSession
	fields  []semantic.Field
	err     error
}

func (stub *createDefaultPreviewEngineStub) PreviewCreateDefaults(
	_ context.Context,
	request semantic.Request,
	user *models.UserSession,
) ([]semantic.Field, error) {
	stub.request = request
	stub.user = user
	return append([]semantic.Field(nil), stub.fields...), stub.err
}

func TestCreateDefaultPreviewServerBindsPrincipalRequestAndReceipts(t *testing.T) {
	ownerID, ownerName, next := "user-1", "Ada Lovelace", int64(901)
	engine := &createDefaultPreviewEngineStub{fields: []semantic.Field{
		{
			Name: "AssignedPrincipalId", Kind: semantic.ValueReference, Reference: &ownerID,
			DisplayValue: &ownerName, Provenance: semantic.ProvenanceCurrentPrincipal,
		},
		{
			Name: "Sequence", Kind: semantic.ValueInteger, Integer: &next,
			Provenance: semantic.ProvenanceSiblingMaxPlusOne,
		},
	}}
	server, err := NewCreateDefaultPreviewServer(engine, nil, nil)
	if err != nil {
		t.Fatal(err)
	}
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	trusted := newTrustedCall(delegation.Claims{
		DelegationID: "delegation-defaults", OrgID: "org-1", PrincipalID: ownerID,
		SessionID: "session-1", CallerWorkloadID: "appservice-fixture",
		AppInstallationID: "installation-1", RuntimeAudience: "runtime-1",
		Capabilities: []string{semantic.CapabilityName}, AuthEpoch: 2, SecurityEpoch: 7,
	}, models.UserSession{ID: ownerID, Name: ownerName, ProfileID: "profile-1"})
	ctx = context.WithValue(ctx, trustedCallContextKey{}, trusted)
	ctx = context.WithValue(ctx, runtimeExecutionScopeContextKey{}, RuntimeExecutionScope{
		view: queryResponseTestView(t),
		receipts: semantic.Receipts{
			CatalogRevision: 11, SchemaGeneration: 12, EngineGeneration: 13, SecurityEpoch: 7,
		},
	})
	parentField, parentID, minimum := "ParentId", "parent-1", uint64(10)
	request := semantic.Request{
		ObjectID: "RankedChild", ParentFieldID: &parentField, ParentRecordID: &parentID,
		MinCatalogRevision: &minimum,
	}
	wireRequest, err := mapping.ToWirePreviewCreateDefaultsRequest(ctx, request)
	if err != nil {
		t.Fatal(err)
	}
	wireResponse, err := server.PreviewCreateDefaults(ctx, wireRequest)
	if err != nil {
		t.Fatal(err)
	}
	response, err := mapping.FromWirePreviewCreateDefaultsResponse(request, wireResponse)
	if err != nil {
		t.Fatal(err)
	}
	if engine.request.ObjectID != request.ObjectID || engine.user == nil || engine.user.ID != ownerID {
		t.Fatalf("engine request=%#v user=%#v", engine.request, engine.user)
	}
	if len(response.Fields) != 2 || response.Fields[1].Integer == nil || *response.Fields[1].Integer != 901 {
		t.Fatalf("response fields=%#v", response.Fields)
	}
	if response.Receipts.CapabilityVersion != semantic.CapabilityName ||
		response.Receipts.CatalogRevision != 11 || response.Receipts.SecurityEpoch != 7 {
		t.Fatalf("receipts=%#v", response.Receipts)
	}
}
