package runtimeworker

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

	"github.com/nexuscrm/automation"
	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	contractconnector "github.com/pingcap-inc/nexuscrm-contract/pkg/connector"
	mutationsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/recordmutationv1"
)

func TestRuntimeConnectorInboundFinalizationAuthorityIsRequestBoundAndDetached(t *testing.T) {
	view := runtimeConnectorInboundFinalizationTestView(t)
	resolver := runtimeConnectorInboundFinalizationAuthorityResolver{}
	query := automation.ConnectorPushContractQuery{
		ConnectorOwnerKey: "example-connector-pack",
		FlowDeveloperName: "WorkItem_To_Remote",
		FlowActionName:    "PushWorkItem",
	}

	if _, err := resolver.ResolveConnectorInboundRemoteIdentityFinalizationAuthority(
		context.Background(),
		query,
	); err == nil || !strings.Contains(err.Error(), "view is not bound") {
		t.Fatalf("missing view error = %v", err)
	}

	ctx := metadata.WithRuntimeMetadataView(context.Background(), view)
	got, err := resolver.ResolveConnectorInboundRemoteIdentityFinalizationAuthority(ctx, query)
	if err != nil {
		t.Fatal(err)
	}
	if got == nil || got.Contract.ActionSlotID != "remote-contact-upsert" ||
		got.Profile.CreatedPackInstallID != "profile-installation-9" ||
		got.Profile.RetryPolicy.BaseDelay != time.Second ||
		len(got.OutboundMappings) != 1 || got.OutboundMappings[0].SourceField != "Subject" {
		t.Fatalf("resolved finalization authority = %#v", got)
	}

	got.OutboundMappings[0].SourceField = "mutated"
	got.Profile.RetryPolicy.RetryableStatusCodes[0] = 500
	again, err := resolver.ResolveConnectorInboundRemoteIdentityFinalizationAuthority(ctx, query)
	if err != nil {
		t.Fatal(err)
	}
	if again.OutboundMappings[0].SourceField != "Subject" ||
		again.Profile.RetryPolicy.RetryableStatusCodes[0] != 503 {
		t.Fatalf("caller mutation changed immutable authority: %#v", again)
	}

	missing, err := resolver.ResolveConnectorInboundRemoteIdentityFinalizationAuthority(
		ctx,
		automation.ConnectorPushContractQuery{
			ConnectorOwnerKey: query.ConnectorOwnerKey,
			FlowDeveloperName: query.FlowDeveloperName,
			FlowActionName:    "OtherAction",
		},
	)
	if err != nil || missing != nil {
		t.Fatalf("missing selector = %#v, %v", missing, err)
	}
}

func TestRuntimeConnectorInboundRecoveryUsesSameImmutableView(t *testing.T) {
	view := runtimeConnectorInboundFinalizationTestView(t)
	resolver := runtimeConnectorInboundRecoveryInstalledResolver{}
	selector := automation.ConnectorInboundRemoteIdentityRecoverySelector{
		ConnectorOwnerKey: "example-connector-pack", ConnectorNamespace: "connector-route-pack",
		DestinationSlotID: "remote-contact", RemoteID: "remote-1",
	}
	if _, err := resolver.ResolveConnectorInboundRemoteIdentityRecoveryInstalledBindings(t.Context(), selector); err == nil {
		t.Fatal("recovery resolver accepted a missing Runtime view")
	}
	bound := metadata.WithRuntimeMetadataView(t.Context(), view)
	installed, err := resolver.ResolveConnectorInboundRemoteIdentityRecoveryInstalledBindings(bound, selector)
	if err != nil {
		t.Fatal(err)
	}
	if installed.Generation != string(view.Digest()) || len(installed.Bindings) != 1 ||
		installed.Bindings[0].Snapshot.CreatedPackInstallID != "profile-installation-9" ||
		installed.Bindings[0].Snapshot.ConnectorNamespace != "example-connector-pack" ||
		len(installed.Bindings[0].RecoverySourceEligibility) != 1 ||
		installed.Bindings[0].RecoverySourceEligibility[0] != "Archived" {
		t.Fatalf("installed recovery authority=%#v", installed)
	}

	legacyView := runtimeConnectorInboundFinalizationTestViewWithEligibility(t, false)
	legacyBound := metadata.WithRuntimeMetadataView(t.Context(), legacyView)
	if _, err := resolver.ResolveConnectorInboundRemoteIdentityRecoveryInstalledBindings(
		legacyBound,
		selector,
	); err == nil || !strings.Contains(err.Error(), "no immutable authority") {
		t.Fatalf("legacy authority error=%v, want Runtime-command fail closed", err)
	}
}

func TestRuntimeConnectorInboundInstalledContractsUseExactBoundView(t *testing.T) {
	view := runtimeConnectorInboundFinalizationTestView(t)
	resolver := runtimeConnectorInboundRecoveryInstalledResolver{}
	if _, err := resolver.ResolveConnectorInboundRemoteIdentityInstalledContracts(
		context.Background(), "example-connector-pack",
	); err == nil || !strings.Contains(err.Error(), "view is not bound") {
		t.Fatalf("missing view error = %v", err)
	}
	ctx := metadata.WithRuntimeMetadataView(t.Context(), view)
	set, err := resolver.ResolveConnectorInboundRemoteIdentityInstalledContractsInAmbientTx(ctx, "example-connector-pack")
	if err != nil {
		t.Fatal(err)
	}
	if set.Generation != string(view.Digest()) || len(set.Contracts) != 1 || len(set.Mappings) != 1 ||
		set.LockedPackInstallIDs["example-profile-pack"] != "profile-installation-9" {
		t.Fatalf("installed contracts = %#v", set)
	}
	sets, err := resolver.ResolveConnectorInboundRemoteIdentityInstalledContractSetsInAmbientTx(
		ctx, []string{"example-connector-pack", "example-connector-pack"},
	)
	if err != nil || len(sets) != 1 || sets["example-connector-pack"].Generation != string(view.Digest()) {
		t.Fatalf("batch installed contracts = %#v, %v", sets, err)
	}
}

func TestRuntimeConnectorTransferTargetAuthorityStaysWithinBoundRevisionAndScope(t *testing.T) {
	first := runtimeConnectorInboundFinalizationTestView(t)
	second := runtimeConnectorInboundFinalizationTestViewWithInput(t, true, func(input *catalogsnapshot.CompileInput) {
		input.OrgID = "other-org"
		input.Lineage.CatalogRevision = 32
		for i := range input.Objects {
			input.Objects[i].CatalogRevision = 32
		}
		for i := range input.MutationDefinitions {
			definition := &input.MutationDefinitions[i]
			definition.CatalogRevision = 32
			if definition.ConnectorInboundAuthorship != nil {
				definition.ConnectorInboundAuthorship.Generation = "pack-installation-18"
			}
			if authority := definition.ConnectorInboundFinalization; authority != nil {
				authority.ContractGeneration = "pack-installation-18"
				authority.Profile.Generation = "profile-installation-10"
				authority.Profile.CredentialRef = "other-credential"
			}
		}
		for i := range input.OutboundIntentRoutes {
			route := &input.OutboundIntentRoutes[i]
			route.CatalogRevision = 32
			route.Route.AuthorizedWorkloadID = "other-integration"
			for j := range route.Route.Bindings {
				binding := &route.Route.Bindings[j]
				if binding.OwnerPack == "example-profile-pack" {
					binding.PackInstallationID = "profile-installation-10"
				} else {
					binding.PackInstallationID = "pack-installation-18"
				}
			}
		}
		for i := range input.Projections {
			input.Projections[i].CatalogRevision = 32
			input.Projections[i].AppInstallationID = "other-installation"
		}
	})
	if first.Digest() == second.Digest() {
		t.Fatal("different authority revisions/scopes have the same view digest")
	}
	finalizer := runtimeConnectorInboundFinalizationAuthorityResolver{}
	installed := runtimeConnectorInboundRecoveryInstalledResolver{}
	query := automation.ConnectorPushContractQuery{
		ConnectorOwnerKey: "example-connector-pack",
		FlowDeveloperName: "WorkItem_To_Remote", FlowActionName: "PushWorkItem",
	}
	// Reuse both adapters across two requests and then return to the first.
	// A cached/current-view lookup or a hybrid contract/profile generation
	// would borrow authority across these otherwise identical selectors.
	for _, tc := range []struct {
		view                                                    *catalogsnapshot.RuntimeMetadataView
		org, installation, workload, owner, profile, credential string
	}{
		{first, "org-test", "installation-test", "integration-service", "pack-installation-17", "profile-installation-9", "primary-credential"},
		{second, "other-org", "other-installation", "other-integration", "pack-installation-18", "profile-installation-10", "other-credential"},
		{first, "org-test", "installation-test", "integration-service", "pack-installation-17", "profile-installation-9", "primary-credential"},
	} {
		ctx := metadata.WithRuntimeMetadataView(t.Context(), tc.view)
		set, err := installed.ResolveConnectorInboundRemoteIdentityInstalledContractsInAmbientTx(ctx, query.ConnectorOwnerKey)
		if err != nil {
			t.Fatal(err)
		}
		authority, err := finalizer.ResolveConnectorInboundRemoteIdentityFinalizationAuthority(ctx, query)
		if err != nil || authority == nil {
			t.Fatalf("target finalization authority = %#v, %v", authority, err)
		}
		sealed := tc.view.ConnectorInboundFinalizationAuthorities(query.ConnectorOwnerKey)
		if set.Generation != string(tc.view.Digest()) || len(set.Contracts) != 1 ||
			len(sealed) != 1 || sealed[0].ContractGeneration != tc.owner ||
			set.LockedPackInstallIDs["example-profile-pack"] != tc.profile ||
			authority.Profile.CreatedPackInstallID != tc.profile || authority.Profile.CredentialRef != tc.credential {
			t.Fatalf("mixed target authority generations: installed=%#v finalization=%#v", set, authority)
		}
		contract := authority.Contract
		if contract.ConnectorOwnerKey != query.ConnectorOwnerKey || contract.FlowDeveloperName != query.FlowDeveloperName ||
			contract.FlowActionName != query.FlowActionName || contract.SourceObject != "WorkItem" ||
			contract.ActionSlotID != "remote-contact-upsert" || contract.DestinationSlotID != "remote-contact" ||
			contract.RemoteIDField != "Subject" || contract.UpdatePathTemplate != "/crm/v3/objects/contacts/{remoteId}" ||
			authority.Profile.OwnerPack != contract.ProfileOwnerPack || authority.Profile.ProfileName != contract.ProfileName ||
			len(authority.OutboundMappings) != 1 || authority.OutboundMappings[0].SourceObject != contract.SourceObject {
			t.Fatalf("incomplete target snapshot inputs: %#v", authority)
		}
		routes := tc.view.OutboundIntentRoutes()
		if tc.view.OrgID() != tc.org || tc.view.AppInstallationID() != tc.installation || len(routes) != 1 ||
			routes[0].AuthorizedWorkloadID != tc.workload || routes[0].ConnectorOwnerKey != contract.ConnectorOwnerKey ||
			routes[0].FlowDeveloperName != contract.FlowDeveloperName || routes[0].FlowActionName != contract.FlowActionName ||
			routes[0].SourceObject != contract.SourceObject || routes[0].ActionSlotID != contract.ActionSlotID ||
			routes[0].DestinationSlotID != contract.DestinationSlotID || len(routes[0].Bindings) != 3 {
			t.Fatalf("target route borrowed from another scope: %#v", routes)
		}
		for _, binding := range routes[0].Bindings {
			expectedGeneration := tc.owner
			if binding.OwnerPack == authority.Profile.OwnerPack {
				expectedGeneration = set.LockedPackInstallIDs[binding.OwnerPack]
			}
			if binding.PackInstallationID != expectedGeneration {
				t.Fatalf("route binding generation differs from target authority: %#v", binding)
			}
		}
	}
}

func runtimeConnectorInboundFinalizationTestView(t *testing.T) *catalogsnapshot.RuntimeMetadataView {
	return runtimeConnectorInboundFinalizationTestViewWithEligibility(t, true)
}

func runtimeConnectorInboundFinalizationTestViewWithEligibility(
	t *testing.T,
	withEligibility bool,
) *catalogsnapshot.RuntimeMetadataView {
	return runtimeConnectorInboundFinalizationTestViewWithInput(t, withEligibility, nil)
}

func runtimeConnectorInboundFinalizationTestViewWithInput(
	t *testing.T,
	withEligibility bool,
	mutate func(*catalogsnapshot.CompileInput),
) *catalogsnapshot.RuntimeMetadataView {
	t.Helper()
	revision := catalogsnapshot.CatalogRevision(31)
	contract := contractconnector.ConnectorPushDurableMetadataContract{
		ConnectorOwnerKey:  "example-connector-pack",
		ActionSlotID:       "remote-contact-upsert",
		ActionType:         constants.ActionTypePushToConnector,
		FlowDeveloperName:  "WorkItem_To_Remote",
		FlowActionName:     "PushWorkItem",
		SourceObject:       "WorkItem",
		DestinationSlotID:  "remote-contact",
		ProfileOwnerPack:   "example-profile-pack",
		ProfileName:        "primary",
		TargetObject:       "contacts",
		CreatePathTemplate: "/crm/v3/objects/contacts",
		UpdatePathTemplate: "/crm/v3/objects/contacts/{remoteId}",
		ResponseIDPath:     "id",
		RemoteIDField:      "Subject",
	}
	authorship := catalogsnapshot.ConnectorInboundAuthorshipBinding{
		ConnectorOwnerKey: contract.ConnectorOwnerKey,
		Generation:        "pack-installation-17",
		ActionSlotID:      contract.ActionSlotID,
		ActionType:        contract.ActionType,
		SourceObject:      contract.SourceObject,
		DestinationSlotID: contract.DestinationSlotID,
		TargetObject:      contract.TargetObject,
		ProfileOwnerPack:  contract.ProfileOwnerPack,
		ProfileName:       contract.ProfileName,
		RemoteIDField:     contract.RemoteIDField,
	}
	authority := catalogsnapshot.ConnectorInboundFinalizationAuthority{
		ContractGeneration: authorship.Generation,
		Contract:           contract,
		Profile: catalogsnapshot.ConnectorInboundFinalizationProfile{
			Generation: "profile-installation-9", OwnerPack: contract.ProfileOwnerPack,
			ProfileName: contract.ProfileName, CredentialRef: "primary-credential",
			EndpointClass: "crm.objects.write", MaxAttempts: 3,
			BaseDelayNanos: int64(time.Second), Multiplier: 2,
			MaxDelayNanos: int64(10 * time.Second), RetryableStatusCodes: []int{503, 429},
		},
		OutboundMappings: []contractconnector.ConnectorFieldMappingInput{{
			Source: contract.ConnectorOwnerKey, SourceObject: contract.SourceObject,
			TargetObject: contract.TargetObject, ActionType: contract.ActionType,
			SourceField: "Subject", TargetField: "subject",
			Direction: contractconnector.DirectionOutbound,
		}},
	}
	if withEligibility {
		authority.RecoverySourceEligibility = &contractconnector.ConnectorPushRecoverySourceEligibilityContract{
			RequiredFalseBooleanFields: []string{"Archived"},
		}
	}
	input := catalogsnapshot.CompileInput{
		OrgID: "org-test",
		Lineage: catalogsnapshot.Lineage{
			CatalogRevision: revision, SchemaGeneration: 1, EngineGeneration: 1,
		},
		Objects: []catalogsnapshot.ObjectInput{{
			CatalogRevision: revision,
			Metadata: &models.ObjectMetadata{
				ID: "object-work-item", APIName: "WorkItem",
				SharingModel: constants.SharingModelPublicReadWrite,
				Fields: []models.FieldMetadata{
					{ID: "field-work-item-id", APIName: constants.FieldID, PhysicalName: constants.FieldID, Type: constants.FieldTypeString, IsSystem: true},
					{ID: "field-work-item-subject", APIName: "Subject", PhysicalName: "Subject", Type: constants.FieldTypeString},
					{ID: "field-work-item-archived", APIName: "Archived", PhysicalName: "Archived", Type: constants.FieldTypeBoolean},
				},
			},
		}},
		MutationDefinitions: []catalogsnapshot.MutationDefinitionInput{
			{CatalogRevision: revision, ConnectorInboundAuthorship: &authorship},
			{CatalogRevision: revision, ConnectorInboundFinalization: &authority},
		},
		OutboundIntentRoutes: []catalogsnapshot.OutboundIntentRouteInput{{
			CatalogRevision: revision,
			Route: catalogsnapshot.OutboundIntentRoute{
				AuthorizedWorkloadID: "integration-service",
				ConnectorOwnerKey:    contract.ConnectorOwnerKey, FlowDeveloperName: contract.FlowDeveloperName,
				FlowActionName: contract.FlowActionName, SourceObject: contract.SourceObject,
				ActionSlotID: contract.ActionSlotID, DestinationSlotID: contract.DestinationSlotID,
				Bindings: []catalogsnapshot.IntegrationBindingReference{
					{Kind: "CONNECTOR_PUSH_CONTRACT", OwnerPack: contract.ConnectorOwnerKey, PackInstallationID: authorship.Generation, PackPayloadDigest: "nexus-pack-sha256-v1:" + strings.Repeat("a", 64), BusinessKey: "push/work-item", DefinitionVersion: 1},
					{Kind: "CONNECTOR_CALLOUT_PROFILE", OwnerPack: contract.ProfileOwnerPack, PackInstallationID: authority.Profile.Generation, PackPayloadDigest: "nexus-pack-sha256-v1:" + strings.Repeat("b", 64), BusinessKey: "example-profile-pack/primary", DefinitionVersion: 1},
					{Kind: "CONNECTOR_FIELD_MAPPING", OwnerPack: contract.ConnectorOwnerKey, PackInstallationID: authorship.Generation, PackPayloadDigest: "nexus-pack-sha256-v1:" + strings.Repeat("a", 64), BusinessKey: "mapping/work-item", DefinitionVersion: 1},
				},
			},
		}},
		Projections: []catalogsnapshot.RuntimeProjectionInput{{
			CatalogRevision:   revision,
			AppInstallationID: "installation-test",
			CapabilityID:      mutationsemantic.CapabilityName,
			Profile:           catalogsnapshot.ProjectionRecordMutationV1,
			ObjectAPINames:    []string{"WorkItem"},
		}},
	}
	if mutate != nil {
		mutate(&input)
	}
	snapshotArtifact, err := catalogsnapshot.CompileCatalogSnapshot(input)
	if err != nil {
		t.Fatal(err)
	}
	snapshot, err := catalogsnapshot.OpenCatalogSnapshot(snapshotArtifact)
	if err != nil {
		t.Fatal(err)
	}
	viewArtifact, err := catalogsnapshot.ProjectRuntimeMetadataView(
		snapshot,
		catalogsnapshot.ProjectionKey{
			AppInstallationID: input.Projections[0].AppInstallationID,
			CapabilityID:      mutationsemantic.CapabilityName,
		},
	)
	if err != nil {
		t.Fatal(err)
	}
	view, err := catalogsnapshot.OpenRuntimeMetadataView(viewArtifact)
	if err != nil {
		t.Fatal(err)
	}
	return view
}
