package controlplane

import (
	"context"
	"os"
	"regexp"
	"sort"
	"strings"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
)

func TestRevertedSCIMSchemaFingerprintIsClosed(t *testing.T) {
	want := map[string]int{
		"_System_ScimBreaker":          8,
		"_System_ScimJournal":          10,
		"_System_ScimPendingManager":   7,
		"_System_ScimProvisioningLink": 8,
		"_System_ScimQuarantine":       13,
	}
	if len(revertedSCIMSchemaFingerprints) != len(want) {
		t.Fatalf("fingerprint table count = %d, want %d", len(revertedSCIMSchemaFingerprints), len(want))
	}
	for _, fingerprint := range revertedSCIMSchemaFingerprints {
		fieldCount, ok := want[fingerprint.name]
		if !ok {
			t.Fatalf("unexpected reverted SCIM table %s", fingerprint.name)
		}
		if len(fingerprint.fieldNames) != fieldCount {
			t.Fatalf("%s field count = %d, want %d", fingerprint.name, len(fingerprint.fieldNames), fieldCount)
		}
		if fingerprint.objectID == "" || fingerprint.label == "" || fingerprint.description == "" {
			t.Fatalf("%s is missing stable object fingerprint facts", fingerprint.name)
		}
		delete(want, fingerprint.name)
	}
	if len(want) != 0 {
		t.Fatalf("missing reverted SCIM fingerprints: %v", want)
	}
}

func TestValidateRevertedSCIMRegistryStateClassifiesExactShapes(t *testing.T) {
	fingerprint := revertedSCIMSchemaFingerprints[0]

	active := exactRevertedSCIMRegistryState(fingerprint, false)
	kind, err := validateRevertedSCIMRegistryState(fingerprint, active)
	if err != nil {
		t.Fatalf("validate active exact state: %v", err)
	}
	if kind != revertedSCIMStateActive {
		t.Fatalf("active exact state kind = %d, want %d", kind, revertedSCIMStateActive)
	}

	retired := exactRevertedSCIMRegistryState(fingerprint, true)
	kind, err = validateRevertedSCIMRegistryState(fingerprint, retired)
	if err != nil {
		t.Fatalf("validate retired exact state: %v", err)
	}
	if kind != revertedSCIMStateRetired {
		t.Fatalf("retired exact state kind = %d, want %d", kind, revertedSCIMStateRetired)
	}

	preReconcile := exactRevertedSCIMRegistryState(fingerprint, false)
	preReconcile.object.tableType = "system_metadata"
	kind, err = validateRevertedSCIMRegistryState(fingerprint, preReconcile)
	if err != nil {
		t.Fatalf("validate pre-reconcile exact state: %v", err)
	}
	if kind != revertedSCIMStateActive {
		t.Fatalf("pre-reconcile exact state kind = %d, want %d", kind, revertedSCIMStateActive)
	}

	partialFieldRetirement := exactRevertedSCIMRegistryState(fingerprint, false)
	setRevertedSCIMFieldDeleted(t, &partialFieldRetirement, "CreatedDate", 1)
	kind, err = validateRevertedSCIMRegistryState(fingerprint, partialFieldRetirement)
	if err != nil {
		t.Fatalf("validate exact partial field retirement: %v", err)
	}
	if kind != revertedSCIMStateActive {
		t.Fatalf("exact partial field retirement kind = %d, want %d", kind, revertedSCIMStateActive)
	}

	kind, err = validateRevertedSCIMRegistryState(fingerprint, revertedSCIMRegistryState{})
	if err != nil {
		t.Fatalf("validate absent state: %v", err)
	}
	if kind != revertedSCIMStateAbsent {
		t.Fatalf("absent state kind = %d, want %d", kind, revertedSCIMStateAbsent)
	}
}

func TestValidateRevertedSCIMRegistryStateFailsClosedOnDrift(t *testing.T) {
	fingerprint := revertedSCIMSchemaFingerprints[0]
	tests := []struct {
		name   string
		mutate func(*revertedSCIMRegistryState)
		want   string
	}{
		{
			name: "object id drift",
			mutate: func(state *revertedSCIMRegistryState) {
				state.object.id = "obj-collision"
			},
			want: "object metadata does not match",
		},
		{
			name: "customized object",
			mutate: func(state *revertedSCIMRegistryState) {
				state.object.isCustomized = 1
			},
			want: "object metadata does not match",
		},
		{
			name: "customized field",
			mutate: func(state *revertedSCIMRegistryState) {
				state.fields[0].isCustomized = 1
			},
			want: "field metadata does not match",
		},
		{
			name: "field type drift",
			mutate: func(state *revertedSCIMRegistryState) {
				state.fields[0].fieldType = "json"
			},
			want: "field type/required/unique/reference shape does not match",
		},
		{
			name: "extra field",
			mutate: func(state *revertedSCIMRegistryState) {
				state.fields = append(state.fields, revertedSCIMFieldRow{apiName: "TenantField", ownerKind: "runtime"})
			},
			want: "field count",
		},
		{
			name: "retired object with active field",
			mutate: func(state *revertedSCIMRegistryState) {
				state.object.isDeleted = 1
				state.table = nil
				for index := range state.fields {
					state.fields[index].isDeleted = 1
				}
				state.fields[0].isDeleted = 0
			},
			want: "partial retirement state",
		},
		{
			name: "invalid field deletion state",
			mutate: func(state *revertedSCIMRegistryState) {
				state.fields[0].isDeleted = 2
			},
			want: "invalid field IsDeleted=2",
		},
		{
			name: "drop pending registry",
			mutate: func(state *revertedSCIMRegistryState) {
				state.table.isDeleted = 1
			},
			want: "drop-pending",
		},
		{
			name: "unexpected relationship",
			mutate: func(state *revertedSCIMRegistryState) {
				state.relationships = []revertedSCIMRelationshipRow{{
					childObject:  fingerprint.name,
					parentObject: "User",
					fieldName:    "UserId",
				}}
			},
			want: "unexpected relationship",
		},
	}
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			state := exactRevertedSCIMRegistryState(fingerprint, false)
			tt.mutate(&state)
			_, err := validateRevertedSCIMRegistryState(fingerprint, state)
			if err == nil || !strings.Contains(err.Error(), tt.want) {
				t.Fatalf("validate drift error = %v, want containing %q", err, tt.want)
			}
		})
	}
}

func TestRetireRevertedSCIMSchemaGreenfieldClosesDerivedResidueAndStamps(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	expectMigrationApplied(mock, migrationIDRetireRevertedSCIMSchema, false)
	mock.ExpectBegin()
	for _, fingerprint := range revertedSCIMSchemaFingerprints {
		expectRevertedSCIMRegistryLoad(mock, fingerprint, nil)
		mock.ExpectExec("UPDATE ObjectPermissions").
			WithArgs(fingerprint.name).
			WillReturnResult(sqlmock.NewResult(0, 0))
		mock.ExpectExec("UPDATE FieldPermissions").
			WithArgs(fingerprint.name).
			WillReturnResult(sqlmock.NewResult(0, 0))
	}
	mock.ExpectCommit()
	expectRevertedSCIMRetirementClosure(mock, 0)
	expectRevertedSCIMMigrationStamp(mock)

	retired, err := RetireRevertedSCIMSchema(context.Background(), db)
	if err != nil {
		t.Fatalf("RetireRevertedSCIMSchema: %v", err)
	}
	if retired != 0 {
		t.Fatalf("RetireRevertedSCIMSchema retired = %d, want 0", retired)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRetireRevertedSCIMSchemaStampedClosureIsReadOnly(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	expectMigrationApplied(mock, migrationIDRetireRevertedSCIMSchema, true)
	expectRevertedSCIMRetirementClosure(mock, 0)

	retired, err := RetireRevertedSCIMSchema(context.Background(), db)
	if err != nil {
		t.Fatalf("RetireRevertedSCIMSchema: %v", err)
	}
	if retired != 0 {
		t.Fatalf("RetireRevertedSCIMSchema retired = %d, want 0", retired)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRetireRevertedSCIMSchemaRetiresExactLegacyMetadata(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	expectMigrationApplied(mock, migrationIDRetireRevertedSCIMSchema, false)
	mock.ExpectBegin()
	expectExactRevertedSCIMRetirement(mock)
	mock.ExpectCommit()
	expectRevertedSCIMRetirementClosure(mock, 0)
	expectRevertedSCIMMigrationStamp(mock)

	retired, err := RetireRevertedSCIMSchema(context.Background(), db)
	if err != nil {
		t.Fatalf("RetireRevertedSCIMSchema: %v", err)
	}
	if retired != len(revertedSCIMSchemaFingerprints) {
		t.Fatalf("RetireRevertedSCIMSchema retired = %d, want %d", retired, len(revertedSCIMSchemaFingerprints))
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRetireRevertedSCIMSchemaCompletesExactPartialFieldRetirement(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	fingerprint := revertedSCIMSchemaFingerprints[0]
	partial := exactRevertedSCIMRegistryState(fingerprint, false)
	setRevertedSCIMFieldDeleted(t, &partial, "CreatedDate", 1)

	expectMigrationApplied(mock, migrationIDRetireRevertedSCIMSchema, false)
	mock.ExpectBegin()
	for index, candidate := range revertedSCIMSchemaFingerprints {
		if index == 0 {
			expectRevertedSCIMRegistryLoad(mock, candidate, &partial)
			mock.ExpectExec("UPDATE _System_Field SET IsDeleted = 1").
				WithArgs(partial.object.id).
				WillReturnResult(sqlmock.NewResult(0, int64(len(partial.fields)-1)))
			mock.ExpectExec("UPDATE _System_Object SET IsDeleted = 1").
				WithArgs(partial.object.id).
				WillReturnResult(sqlmock.NewResult(0, 1))
			mock.ExpectExec("DELETE FROM _System_Table").
				WithArgs(candidate.name).
				WillReturnResult(sqlmock.NewResult(0, 1))
		} else {
			expectRevertedSCIMRegistryLoad(mock, candidate, nil)
		}
		mock.ExpectExec("UPDATE ObjectPermissions").
			WithArgs(candidate.name).
			WillReturnResult(sqlmock.NewResult(0, 0))
		mock.ExpectExec("UPDATE FieldPermissions").
			WithArgs(candidate.name).
			WillReturnResult(sqlmock.NewResult(0, 0))
	}
	mock.ExpectCommit()
	expectRevertedSCIMRetirementClosure(mock, 0)
	expectRevertedSCIMMigrationStamp(mock)

	retired, err := RetireRevertedSCIMSchema(context.Background(), db)
	if err != nil {
		t.Fatalf("RetireRevertedSCIMSchema: %v", err)
	}
	if retired != 1 {
		t.Fatalf("RetireRevertedSCIMSchema retired = %d, want 1", retired)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRetireRevertedSCIMSchemaStampedResurrectionRepairsAgain(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	expectMigrationApplied(mock, migrationIDRetireRevertedSCIMSchema, true)
	mock.ExpectQuery(regexp.QuoteMeta("SELECT COUNT(*) FROM _System_Object")).
		WillReturnRows(sqlmock.NewRows([]string{"COUNT(*)"}).AddRow(len(revertedSCIMSchemaFingerprints)))
	mock.ExpectBegin()
	expectExactRevertedSCIMRetirement(mock)
	mock.ExpectCommit()
	expectRevertedSCIMRetirementClosure(mock, 0)
	expectRevertedSCIMMigrationStamp(mock)

	retired, err := RetireRevertedSCIMSchema(context.Background(), db)
	if err != nil {
		t.Fatalf("RetireRevertedSCIMSchema: %v", err)
	}
	if retired != len(revertedSCIMSchemaFingerprints) {
		t.Fatalf("RetireRevertedSCIMSchema retired = %d, want %d", retired, len(revertedSCIMSchemaFingerprints))
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRetireRevertedSCIMSchemaCollisionRollsBackWithoutStamp(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	fingerprint := revertedSCIMSchemaFingerprints[0]
	collision := exactRevertedSCIMRegistryState(fingerprint, false)
	collision.object.isCustomized = 1
	expectMigrationApplied(mock, migrationIDRetireRevertedSCIMSchema, false)
	mock.ExpectBegin()
	expectRevertedSCIMRegistryLoad(mock, fingerprint, &collision)
	mock.ExpectRollback()

	if _, err := RetireRevertedSCIMSchema(context.Background(), db); err == nil || !strings.Contains(err.Error(), "object metadata does not match") {
		t.Fatalf("RetireRevertedSCIMSchema collision error = %v, want fail-closed object mismatch", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRetireRevertedSCIMSchemaOrphanLegacyFieldsFailClosed(t *testing.T) {
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock.New: %v", err)
	}
	defer db.Close()

	fingerprint := revertedSCIMSchemaFingerprints[0]
	orphan := revertedSCIMRegistryState{
		fields: exactRevertedSCIMRegistryState(fingerprint, false).fields,
	}
	expectMigrationApplied(mock, migrationIDRetireRevertedSCIMSchema, false)
	mock.ExpectBegin()
	expectRevertedSCIMRegistryLoad(mock, fingerprint, &orphan)
	mock.ExpectRollback()

	if _, err := RetireRevertedSCIMSchema(context.Background(), db); err == nil || !strings.Contains(err.Error(), "field metadata exists without its object fingerprint") {
		t.Fatalf("RetireRevertedSCIMSchema orphan-field error = %v, want fail-closed partial-state error", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestRetireRevertedSCIMSchemaContainsNoPhysicalDDL(t *testing.T) {
	source, err := os.ReadFile("retire_reverted_scim_schema.go")
	if err != nil {
		t.Fatalf("read migration source: %v", err)
	}
	text := string(source)
	for _, forbidden := range []string{
		"DROP TABLE",
		"ALTER TABLE",
		"CREATE TABLE",
		"UPDATE _System_Table SET IsDeleted",
		"DELETE FROM _System_Relationship",
	} {
		if strings.Contains(text, forbidden) {
			t.Fatalf("migration contains destructive physical-schema authority %q", forbidden)
		}
	}
	for _, required := range []string{
		"UPDATE _System_Field SET IsDeleted = 1",
		"UPDATE _System_Object SET IsDeleted = 1",
		"DELETE FROM _System_Table",
	} {
		if !strings.Contains(text, required) {
			t.Fatalf("migration source missing required metadata transition %q", required)
		}
	}
}

func TestRunRepairRetiresRevertedSCIMBeforeOtherUpgradeWork(t *testing.T) {
	source, err := os.ReadFile("operations.go")
	if err != nil {
		t.Fatalf("read operations.go: %v", err)
	}
	text := string(source)
	start := strings.Index(text, "func RunRepairWithOptionsAndRunnerOptions(")
	end := strings.Index(text, "func RunL0Repair(")
	if start < 0 || end < 0 || start >= end {
		t.Fatalf("RunRepair source bounds missing: start=%d end=%d", start, end)
	}
	runRepair := text[start:end]
	reconcile := strings.Index(runRepair, `trackStepFatal("ReconcileManagedTableColumns"`)
	retire := strings.Index(runRepair, `trackStepFatal("RetireRevertedSCIMSchema"`)
	packInstall := strings.Index(runRepair, `trackStepFatal("MigratePackInstallIdBackfill"`)
	polymorphic := strings.Index(runRepair, `trackStepFatal("BackfillPolymorphicDiscriminators"`)
	if reconcile < 0 || retire < 0 || packInstall < 0 || polymorphic < 0 {
		t.Fatalf("RunRepair migration anchors missing: reconcile=%d retire=%d packInstall=%d polymorphic=%d", reconcile, retire, packInstall, polymorphic)
	}
	if !(reconcile < retire && retire < packInstall && retire < polymorphic) {
		t.Fatalf("RetireRevertedSCIMSchema order is unsafe: reconcile=%d retire=%d packInstall=%d polymorphic=%d", reconcile, retire, packInstall, polymorphic)
	}
}

func TestApplyMetadataRetiresRevertedSCIMAfterDDLReconnectBeforeManagedReconcile(t *testing.T) {
	source, err := os.ReadFile("operations.go")
	if err != nil {
		t.Fatalf("read operations.go: %v", err)
	}
	text := string(source)
	start := strings.Index(text, "func ApplyMetadata(")
	end := strings.Index(text, "func EnsureL0SchemaDDL(")
	if start < 0 || end < 0 || start >= end {
		t.Fatalf("ApplyMetadata source bounds missing: start=%d end=%d", start, end)
	}
	applyMetadata := text[start:end]
	reconnect := strings.Index(applyMetadata, "db = newDB")
	retire := strings.Index(applyMetadata, `if err := job.trackStepFatal("RetireRevertedSCIMSchema"`)
	managedReconcile := strings.Index(applyMetadata, `job.trackStep("ReconcileManagedStandardObjectAccessPolicies"`)
	if reconnect < 0 || retire < 0 || managedReconcile < 0 {
		t.Fatalf(
			"ApplyMetadata retirement anchors missing: reconnect=%d retire=%d managedReconcile=%d",
			reconnect,
			retire,
			managedReconcile,
		)
	}
	if !(reconnect < retire && retire < managedReconcile) {
		t.Fatalf(
			"ApplyMetadata retirement order is unsafe: reconnect=%d retire=%d managedReconcile=%d",
			reconnect,
			retire,
			managedReconcile,
		)
	}
}

func exactRevertedSCIMRegistryState(fingerprint revertedSCIMTableFingerprint, retired bool) revertedSCIMRegistryState {
	deleted := 0
	if retired {
		deleted = 1
	}
	state := revertedSCIMRegistryState{
		object: &revertedSCIMObjectRow{
			id:                      fingerprint.objectID,
			apiName:                 fingerprint.name,
			tableType:               "system_core",
			label:                   fingerprint.label,
			description:             fingerprint.description,
			requiredWritePermission: "BLOCKED",
			requiredReadPermission:  "BLOCKED",
			deleteMode:              "hard",
			searchable:              1,
			isDeleted:               deleted,
		},
	}
	if !retired {
		state.table = &revertedSCIMTableRow{
			tableName:     fingerprint.name,
			tableType:     "system_core",
			category:      "auth",
			description:   fingerprint.description,
			isManaged:     0,
			schemaVersion: "",
			createdBy:     "bootstrap",
		}
	}
	names := append([]string(nil), fingerprint.fieldNames...)
	sort.Strings(names)
	for _, fieldName := range names {
		fieldType, required, unique, references := expectedRevertedSCIMFieldShape(fingerprint, fieldName)
		state.fields = append(state.fields, revertedSCIMFieldRow{
			apiName:     fieldName,
			fieldType:   fieldType,
			required:    required,
			unique:      unique,
			referenceTo: references,
			isDeleted:   deleted,
			ownerKind:   "runtime",
		})
	}
	return state
}

func setRevertedSCIMFieldDeleted(t *testing.T, state *revertedSCIMRegistryState, apiName string, deleted int) {
	t.Helper()
	for index := range state.fields {
		if state.fields[index].apiName == apiName {
			state.fields[index].isDeleted = deleted
			return
		}
	}
	t.Fatalf("reverted SCIM state is missing field %s", apiName)
}

func expectExactRevertedSCIMRetirement(mock sqlmock.Sqlmock) {
	for _, fingerprint := range revertedSCIMSchemaFingerprints {
		state := exactRevertedSCIMRegistryState(fingerprint, false)
		expectRevertedSCIMRegistryLoad(mock, fingerprint, &state)
		mock.ExpectExec("UPDATE _System_Field SET IsDeleted = 1").
			WithArgs(state.object.id).
			WillReturnResult(sqlmock.NewResult(0, int64(len(state.fields))))
		mock.ExpectExec("UPDATE _System_Object SET IsDeleted = 1").
			WithArgs(state.object.id).
			WillReturnResult(sqlmock.NewResult(0, 1))
		mock.ExpectExec("DELETE FROM _System_Table").
			WithArgs(fingerprint.name).
			WillReturnResult(sqlmock.NewResult(0, 1))
		mock.ExpectExec("UPDATE ObjectPermissions").
			WithArgs(fingerprint.name).
			WillReturnResult(sqlmock.NewResult(0, 0))
		mock.ExpectExec("UPDATE FieldPermissions").
			WithArgs(fingerprint.name).
			WillReturnResult(sqlmock.NewResult(0, 0))
	}
}

func expectRevertedSCIMRegistryLoad(mock sqlmock.Sqlmock, fingerprint revertedSCIMTableFingerprint, state *revertedSCIMRegistryState) {
	objectRows := sqlmock.NewRows([]string{
		"Id", "ApiName", "TableType", "Label", "Description", "RequiredWritePermission", "RequiredReadPermission", "DeleteMode", "IsCustom", "Searchable", "IsCustomized", "IsDeleted",
	})
	if state != nil && state.object != nil {
		object := state.object
		objectRows.AddRow(
			object.id,
			object.apiName,
			object.tableType,
			object.label,
			object.description,
			object.requiredWritePermission,
			object.requiredReadPermission,
			object.deleteMode,
			object.isCustom,
			object.searchable,
			object.isCustomized,
			object.isDeleted,
		)
	}
	mock.ExpectQuery("SELECT Id, ApiName, TableType, Label").
		WithArgs(fingerprint.name).
		WillReturnRows(objectRows)

	tableRows := sqlmock.NewRows([]string{
		"TableName", "TableType", "Category", "Description", "IsManaged", "SchemaVersion", "CreatedBy", "IsDeleted",
	})
	if state != nil && state.table != nil {
		table := state.table
		tableRows.AddRow(
			table.tableName,
			table.tableType,
			table.category,
			table.description,
			table.isManaged,
			table.schemaVersion,
			table.createdBy,
			table.isDeleted,
		)
	}
	mock.ExpectQuery("SELECT TableName, TableType").
		WithArgs(fingerprint.name).
		WillReturnRows(tableRows)

	fieldRows := sqlmock.NewRows([]string{"ApiName", "Type", "Required", "IsUnique", "ReferenceTo", "IsCustomized", "IsDeleted", "OwnerKind", "OwnerPack"})
	if state != nil {
		for _, field := range state.fields {
			referenceJSON := "[]"
			if len(field.referenceTo) > 0 {
				referenceJSON = `["` + strings.Join(field.referenceTo, `","`) + `"]`
			}
			fieldRows.AddRow(
				field.apiName,
				field.fieldType,
				field.required,
				field.unique,
				referenceJSON,
				field.isCustomized,
				field.isDeleted,
				field.ownerKind,
				field.ownerPack,
			)
		}
	}
	mock.ExpectQuery("SELECT ApiName, Type, COALESCE\\(Required, 0\\)").
		WithArgs(fingerprint.objectID).
		WillReturnRows(fieldRows)

	relationshipRows := sqlmock.NewRows([]string{"ChildObjectApiName", "ParentObjectApiName", "FieldApiName"})
	if state != nil {
		for _, relationship := range state.relationships {
			relationshipRows.AddRow(relationship.childObject, relationship.parentObject, relationship.fieldName)
		}
	}
	mock.ExpectQuery("SELECT ChildObjectApiName, ParentObjectApiName, FieldApiName").
		WithArgs(fingerprint.name, fingerprint.name).
		WillReturnRows(relationshipRows)
}

func expectRevertedSCIMRetirementClosure(mock sqlmock.Sqlmock, remaining int) {
	for _, prefix := range []string{
		"SELECT COUNT(*) FROM _System_Object",
		"SELECT COUNT(*) FROM _System_Field WHERE ObjectId IN",
		"SELECT COUNT(*) FROM _System_Table",
		"SELECT COUNT(*) FROM ObjectPermissions",
		"SELECT COUNT(*) FROM FieldPermissions",
		"SELECT COUNT(*) FROM _System_Relationship",
	} {
		mock.ExpectQuery(regexp.QuoteMeta(prefix)).
			WillReturnRows(sqlmock.NewRows([]string{"COUNT(*)"}).AddRow(remaining))
	}
}

func expectRevertedSCIMMigrationStamp(mock sqlmock.Sqlmock) {
	mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO _System_Migration (Id, Version, Description, AppliedAt, CreatedDate, LastModifiedDate)
			VALUES (?, ?, ?, NOW(), NOW(), NOW())
			ON DUPLICATE KEY UPDATE LastModifiedDate = NOW()`)).
		WithArgs(migrationIDRetireRevertedSCIMSchema, 1, sqlmock.AnyArg()).
		WillReturnResult(sqlmock.NewResult(0, 1))
}
