package automation

import (
	"context"
	"errors"
	"testing"

	"github.com/nexuscrm/kernel/pkg/flowactions"
	"github.com/nexuscrm/shared/pkg/models"
)

type recoveryCandidateProbeStub struct {
	present  bool
	err      error
	calls    int
	selector ConnectorInboundRemoteIdentityRecoverySelector
}

func (s *recoveryCandidateProbeStub) HasConnectorInboundRemoteIdentityRecoveryCandidate(
	_ context.Context,
	selector ConnectorInboundRemoteIdentityRecoverySelector,
) (bool, error) {
	s.calls++
	s.selector = selector
	return s.present, s.err
}

func TestRequestConnectorRemoteIdentityRecoveryRollsBackOnlyForExistingCandidate(t *testing.T) {
	for _, tc := range []struct {
		name      string
		present   bool
		wantError bool
		wantState string
	}{
		{name: "candidate requests owner transition", present: true, wantError: true},
		{name: "missing candidate preserves Flow fallback", wantState: ConnectorInboundRemoteIdentityRecoveryStateNotFound},
	} {
		t.Run(tc.name, func(t *testing.T) {
			probe := &recoveryCandidateProbeStub{present: tc.present}
			executor := &FlowExecutor{persistence: &recoveryActionPersistenceStub{active: true}}
			interview := recoveryActionInterview()
			action := recoveryActionCall()
			action.ActionName = flowactions.ActionTypeRequestConnectorRemoteIdentityRecovery
			action.ActionType = flowactions.ActionTypeRequestConnectorRemoteIdentityRecovery
			ctx := withAtomicFlowBoundary(recoveryActionCompanionContext(recoveryActionVersionID))
			next, err := executor.executeRequestConnectorRemoteIdentityRecovery(ctx, interview, action, nil, probe)
			var requested *ConnectorInboundRemoteIdentityRecoveryRequestedError
			if got := errors.As(err, &requested); got != tc.wantError {
				t.Fatalf("requested error=%v error=%v", got, err)
			}
			if probe.calls != 1 ||
				probe.selector.ConnectorOwnerKey != recoveryActionOwnerPack ||
				probe.selector.ConnectorNamespace != recoveryActionNamespace ||
				probe.selector.RemoteID != "12345" ||
				probe.selector.DestinationSlotID != "hubspot-contact" {
				t.Fatalf("probe=%#v calls=%d", probe.selector, probe.calls)
			}
			if tc.wantError {
				if next != "" {
					t.Fatalf("next=%q, want rollback", next)
				}
				return
			}
			if err != nil || next != "RouteDurableRecovery" || interview.ContextData["durableRecoveryState"] != tc.wantState {
				t.Fatalf("next=%q state=%#v err=%v", next, interview.ContextData["durableRecoveryState"], err)
			}
		})
	}
}

func TestMetadataBoundRecoveryValidatorUsesOnlyPublishedBooleanFields(t *testing.T) {
	validator := MetadataBoundConnectorInboundRemoteIdentityRecoveryValidator{}
	input := ConnectorInboundRemoteIdentityRecoveryLockedRecordInput{
		ObjectName: "GenericSource", RecordID: "source-1",
		Record: models.Record{
			"Archived": false, "Superseded": int64(0), "RemoteKey": "remote-1",
		},
		RemoteIDField: "RemoteKey", RemoteID: "remote-1",
		RequiredFalseBooleanFields: []string{"Archived", "Superseded"},
	}
	status, err := validator.ValidateConnectorInboundRemoteIdentityRecoveryLockedRecord(input)
	if err != nil || status != ConnectorPushCompatibilityMirrorSynced {
		t.Fatalf("status=%q err=%v", status, err)
	}
	input.Record["Superseded"] = true
	if _, err := validator.ValidateConnectorInboundRemoteIdentityRecoveryLockedRecord(input); err == nil {
		t.Fatal("true eligibility field was accepted")
	}
	input.RequiredFalseBooleanFields = nil
	if _, err := validator.ValidateConnectorInboundRemoteIdentityRecoveryLockedRecord(input); err == nil {
		t.Fatal("missing eligibility authority was accepted")
	}
}

func TestRecoveryCandidateUsesResolverAuthorityWithoutGlobalEligibilityPolicy(t *testing.T) {
	store := &connectorInboundRecoveryStoreStub{
		evidence: connectorInboundRecoveryTestEvidence(),
	}
	installed := connectorInboundRecoveryTestInstalled()
	installed.result.Bindings[0].RecoverySourceEligibility = nil
	probe := NewConnectorInboundRemoteIdentityRecoveryCandidateProbe(store, installed)
	selector := connectorInboundRecoveryTestSelector()

	present, err := probe.HasConnectorInboundRemoteIdentityRecoveryCandidate(
		context.Background(), selector,
	)
	if err != nil {
		t.Fatal(err)
	}
	if !present {
		t.Fatal("generic recovery probe overrode its resolver's authority set")
	}
}
