package records

import (
	"context"
	"strings"
	"testing"

	"github.com/nexuscrm/kernel/pkg/recordlocks"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/referenceidentity"
)

type immutableReferenceAuthorityStub struct {
	object  string
	records map[string]models.SObject
	unowned map[string]struct{}
}

func (s immutableReferenceAuthorityStub) ResolveReferenceIdentities(
	_ context.Context,
	objectName string,
	ids []string,
	_ []string,
) (referenceidentity.Resolution, error) {
	if objectName != s.object {
		return referenceidentity.Resolution{}, nil
	}
	result := referenceidentity.Resolution{Identities: make(map[string]referenceidentity.IdentityResolution, len(ids))}
	for _, id := range ids {
		if _, ok := s.unowned[id]; ok {
			continue
		}
		if row := s.records[id]; row != nil {
			result.Identities[id] = referenceidentity.IdentityResolution{Disposition: referenceidentity.Immutable, Record: row}
		} else {
			result.Identities[id] = referenceidentity.IdentityResolution{Disposition: referenceidentity.OwnedMissing}
		}
	}
	return result, nil
}

func TestValidateNonPolymorphicLookupsSupportsCatalogAndLegacyIDsForSameObject(t *testing.T) {
	parent := relationshipDeleteParentSchema()
	schema := relationshipDeleteChildSchema(models.FieldMetadata{
		APIName: "CatalogDefinitionId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Parent__c"}, Createable: true,
	})
	schema.Fields = append(schema.Fields, models.FieldMetadata{
		APIName: "LegacyDefinitionId", Type: constants.FieldTypeReference, ReferenceTo: []string{"Parent__c"}, Createable: true,
	})
	repo := &relationshipDeleteRepo{findRecords: map[string]models.SObject{
		"Parent__c": {constants.FieldID: "legacy-definition"},
	}}
	service := newRollupBoundaryTestService(repo, parent, schema)
	ctx := constants.WithSecurityContext(t.Context(), constants.SystemContext, "mixed logical reference test")
	ctx = referenceidentity.WithAuthority(ctx, immutableReferenceAuthorityStub{
		object: "Parent__c",
		records: map[string]models.SObject{
			"catalog-definition": {constants.FieldID: "catalog-definition"},
		},
		unowned: map[string]struct{}{"legacy-definition": {}},
	})
	err := service.validateNonPolymorphicLookups(ctx, models.SObject{
		"CatalogDefinitionId": "catalog-definition",
		"LegacyDefinitionId":  "legacy-definition",
	}, schema, recordEntrypointUser())
	if err != nil {
		t.Fatalf("validateNonPolymorphicLookups: %v", err)
	}
	if len(repo.calls) != 1 || repo.calls[0] != "find:Parent__c:legacy-definition" {
		t.Fatalf("physical repository calls = %#v, want legacy ID only", repo.calls)
	}
}

func TestValidateNonPolymorphicLookupsUsesImmutableReferenceAuthority(t *testing.T) {
	parent := relationshipDeleteParentSchema()
	schema := relationshipDeleteChildSchema(models.FieldMetadata{
		APIName: "DefinitionId", Type: constants.FieldTypeReference,
		ReferenceTo: []string{"Parent__c"}, Createable: true,
	})
	repo := &relationshipDeleteRepo{}
	service := newRollupBoundaryTestService(repo, parent, schema)
	ctx := constants.WithSecurityContext(t.Context(), constants.SystemContext, "logical reference test")
	ctx = referenceidentity.WithAuthority(ctx, immutableReferenceAuthorityStub{
		object: "Parent__c",
		records: map[string]models.SObject{
			"definition-1": {constants.FieldID: "definition-1", constants.FieldName: "Definition One"},
		},
	})
	if err := service.validateNonPolymorphicLookups(
		ctx, models.SObject{"DefinitionId": "definition-1"}, schema, recordEntrypointUser(),
	); err != nil {
		t.Fatalf("validateNonPolymorphicLookups rejected authoritative identity: %v", err)
	}
	if len(repo.calls) != 0 {
		t.Fatalf("physical repository calls = %#v, want none", repo.calls)
	}
}

func TestValidateNonPolymorphicLookupsFailsClosedForMissingImmutableIdentity(t *testing.T) {
	parent := relationshipDeleteParentSchema()
	schema := relationshipDeleteChildSchema(models.FieldMetadata{
		APIName: "DefinitionId", Type: constants.FieldTypeReference,
		ReferenceTo: []string{"Parent__c"}, Createable: true,
	})
	service := newRollupBoundaryTestService(&relationshipDeleteRepo{}, parent, schema)
	ctx := referenceidentity.WithAuthority(t.Context(), immutableReferenceAuthorityStub{
		object: "Parent__c", records: map[string]models.SObject{},
	})
	err := service.validateNonPolymorphicLookups(
		ctx, models.SObject{"DefinitionId": "missing"}, schema, recordEntrypointUser(),
	)
	if err == nil || !strings.Contains(err.Error(), "referenced Parent__c record") {
		t.Fatalf("error = %v, want neutral missing-reference validation error", err)
	}
}

func TestLockParentRowsSkipsAuthoritativeImmutableIdentity(t *testing.T) {
	repo := &stubUpsertRecordRepo{getLockFn: func(context.Context, string, string) (models.SObject, error) {
		t.Fatal("immutable identity reached the physical parent-lock repository")
		return nil, nil
	}}
	service := &PersistenceService{repo: repo}
	ctx := referenceidentity.WithAuthority(t.Context(), immutableReferenceAuthorityStub{
		object: "LogicalDefinition",
		records: map[string]models.SObject{
			"definition-1": {constants.FieldID: "definition-1"},
		},
	})
	err := service.RunWithParentLockCoordinator(ctx, func(lockCtx context.Context) error {
		held, lockErr := service.LockParentRows(lockCtx, []recordlocks.ParentKey{{
			ObjectName: "LogicalDefinition", RecordID: "definition-1",
		}})
		if len(held) != 0 {
			t.Fatalf("held physical locks = %#v, want none", held)
		}
		return lockErr
	})
	if err != nil {
		t.Fatalf("LockParentRows: %v", err)
	}
}

func TestLockParentRowsSupportsCatalogAndLegacyIDsForSameObject(t *testing.T) {
	var locked []string
	repo := &stubUpsertRecordRepo{getLockFn: func(_ context.Context, _ string, id string) (models.SObject, error) {
		locked = append(locked, id)
		return models.SObject{constants.FieldID: id}, nil
	}}
	service, _ := newAdmittedParentLockTestService(repo)
	ctx := referenceidentity.WithAuthority(parentLaneAdmissionTestContext(), immutableReferenceAuthorityStub{
		object: "ProcessDefinition",
		records: map[string]models.SObject{
			"catalog-definition": {constants.FieldID: "catalog-definition"},
		},
		unowned: map[string]struct{}{"legacy-definition": {}},
	})
	err := service.RunWithParentLockCoordinator(ctx, func(lockCtx context.Context) error {
		_, lockErr := service.LockParentRows(lockCtx, []recordlocks.ParentKey{
			{ObjectName: "ProcessDefinition", RecordID: "catalog-definition"},
			{ObjectName: "ProcessDefinition", RecordID: "legacy-definition"},
		})
		return lockErr
	})
	if err != nil {
		t.Fatalf("LockParentRows: %v", err)
	}
	if len(locked) != 1 || locked[0] != "legacy-definition" {
		t.Fatalf("physically locked IDs = %#v, want legacy ID only", locked)
	}
}
