package packs

import (
	"context"
	"os"
	"path/filepath"
	"regexp"
	"testing"

	"github.com/DATA-DOG/go-sqlmock"
	domainschema "github.com/nexuscrm/kernel/pkg/schema"
	"github.com/nexuscrm/shared/pkg/models"
)

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

	query := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ?")
	mock.ExpectExec(query).
		WithArgs(
			"jira-like-sample",
			"objects",
			string(ManageabilityDefaultsOnly),
			`[{"parentLookupField":"Parent__c","sourceObject":"Source__c","sourceLookupField":"Parent__c","targetObject":"Target__c","targetLookupField":"Parent__c","fieldMappings":{"Name":"Name"}}]`,
			domainschema.GenerateObjectID("Parent__c"),
		).
		WillReturnResult(sqlmock.NewResult(0, 1))

	err = persistObjectSpecChildCloneRulePackOwnership(context.Background(), db, "jira-like-sample", "objects", ManageabilityDefaultsOnly, []ObjectSpec{{
		APIName: "Parent__c",
		ChildCloneRules: []models.ChildCloneRule{{
			ParentLookupField: "Parent__c",
			SourceObject:      "Source__c",
			SourceLookupField: "Parent__c",
			TargetObject:      "Target__c",
			TargetLookupField: "Parent__c",
			FieldMappings:     map[string]string{"Name": "Name"},
		}},
	}}, true)
	if err != nil {
		t.Fatalf("persistObjectSpecChildCloneRulePackOwnership = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	query := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ?")
	mock.ExpectExec(query).
		WithArgs(
			"jira-like-sample",
			"objects",
			string(ManageabilityDefaultsOnly),
			"[]",
			domainschema.GenerateObjectID("Root__c"),
		).
		WillReturnResult(sqlmock.NewResult(0, 1))

	err = persistObjectSpecChildCloneRulePackOwnership(context.Background(), db, "jira-like-sample", "objects", ManageabilityDefaultsOnly, []ObjectSpec{{
		APIName: "Root__c",
	}}, true)
	if err != nil {
		t.Fatalf("persistObjectSpecChildCloneRulePackOwnership = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	id := domainschema.GenerateObjectID("Root__c")
	liveRules, err := canonicalChildCloneRulesPackValue([]models.ChildCloneRule{testPackChildCloneRule("Name")})
	if err != nil {
		t.Fatalf("canonical live rules: %v", err)
	}

	selectSQL := regexp.QuoteMeta("SELECT `Id`, `ApiName`, `ChildCloneRules`, COALESCE(`ChildCloneRulesCustomized`, 0), COALESCE(`ChildCloneRulesPackName`, ''), COALESCE(`ChildCloneRulesPackBusinessKey`, ''), COALESCE(`ChildCloneRulesPackManageability`, ''), `ChildCloneRulesPackValue` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0 AND `Id` IN (?) ORDER BY `Id`")
	mock.ExpectQuery(selectSQL).
		WithArgs(id).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ApiName", "ChildCloneRules", "ChildCloneRulesCustomized", "ChildCloneRulesPackName", "ChildCloneRulesPackBusinessKey", "ChildCloneRulesPackManageability", "ChildCloneRulesPackValue"}).
			AddRow(id, "Root__c", liveRules, 0, "", "", "", nil))

	markCustomizedSQL := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesCustomized` = 1, `LastModifiedDate` = NOW() WHERE `Id` = ? AND COALESCE(`ChildCloneRulesCustomized`, 0) = 0")
	mock.ExpectExec(markCustomizedSQL).
		WithArgs(id).
		WillReturnResult(sqlmock.NewResult(0, 1))

	err = prepareObjectSpecChildCloneRulePackOwnershipForApply(context.Background(), db, "jira-like-sample", "objects", ManageabilityDefaultsOnly, []ObjectSpec{{
		APIName: "Root__c",
	}})
	if err != nil {
		t.Fatalf("prepareObjectSpecChildCloneRulePackOwnershipForApply = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	id := domainschema.GenerateObjectID("Root__c")
	oldRules, err := canonicalChildCloneRulesPackValue([]models.ChildCloneRule{testPackChildCloneRule("OldName")})
	if err != nil {
		t.Fatalf("canonical old rules: %v", err)
	}
	newRule := testPackChildCloneRule("NewName")

	selectSQL := regexp.QuoteMeta("SELECT `Id`, `ApiName`, `ChildCloneRules`, COALESCE(`ChildCloneRulesCustomized`, 0), COALESCE(`ChildCloneRulesPackName`, ''), COALESCE(`ChildCloneRulesPackBusinessKey`, ''), COALESCE(`ChildCloneRulesPackManageability`, ''), `ChildCloneRulesPackValue` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0 AND `Id` IN (?) ORDER BY `Id`")
	mock.ExpectQuery(selectSQL).
		WithArgs(id).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ApiName", "ChildCloneRules", "ChildCloneRulesCustomized", "ChildCloneRulesPackName", "ChildCloneRulesPackBusinessKey", "ChildCloneRulesPackManageability", "ChildCloneRulesPackValue"}).
			AddRow(id, "Root__c", oldRules, 0, "jira-like-sample", "objects", string(ManageabilityDefaultsOnly), oldRules))

	err = prepareObjectSpecChildCloneRulePackOwnershipForApply(context.Background(), db, "jira-like-sample", "objects", ManageabilityDefaultsOnly, []ObjectSpec{{
		APIName:         "Root__c",
		ChildCloneRules: []models.ChildCloneRule{newRule},
	}})
	if err != nil {
		t.Fatalf("prepareObjectSpecChildCloneRulePackOwnershipForApply = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	id := domainschema.GenerateObjectID("Root__c")
	selectSQL := regexp.QuoteMeta("SELECT `Id`, COALESCE(`ChildCloneRulesCustomized`, 0), `ChildCloneRulesPackName`, `ChildCloneRulesPackBusinessKey`, `ChildCloneRulesPackManageability`, `ChildCloneRulesPackValue` FROM `_System_Object` WHERE `Id` IN (?) ORDER BY `Id`")
	mock.ExpectQuery(selectSQL).
		WithArgs(id).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ChildCloneRulesCustomized", "ChildCloneRulesPackName", "ChildCloneRulesPackBusinessKey", "ChildCloneRulesPackManageability", "ChildCloneRulesPackValue"}).
			AddRow(id, 1, "active-pack", "objects", string(ManageabilityDefaultsOnly), `[]`))

	snapshot, err := snapshotObjectSpecChildCloneRulePackOwnership(context.Background(), db, []string{id})
	if err != nil {
		t.Fatalf("snapshotObjectSpecChildCloneRulePackOwnership = %v", err)
	}

	restoreSQL := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesCustomized` = ?, `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ?")
	mock.ExpectExec(restoreSQL).
		WithArgs(1, "active-pack", "objects", string(ManageabilityDefaultsOnly), `[]`, id).
		WillReturnResult(sqlmock.NewResult(0, 1))

	if err := snapshot.Restore(context.Background(), db); err != nil {
		t.Fatalf("snapshot.Restore = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	id := domainschema.GenerateObjectID("Root__c")
	selectSQL := regexp.QuoteMeta("SELECT `Id`, COALESCE(`ChildCloneRulesCustomized`, 0), `ChildCloneRulesPackName`, `ChildCloneRulesPackBusinessKey`, `ChildCloneRulesPackManageability`, `ChildCloneRulesPackValue` FROM `_System_Object` WHERE `Id` IN (?) ORDER BY `Id`")
	mock.ExpectQuery(selectSQL).
		WithArgs(id).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ChildCloneRulesCustomized", "ChildCloneRulesPackName", "ChildCloneRulesPackBusinessKey", "ChildCloneRulesPackManageability", "ChildCloneRulesPackValue"}))

	snapshot, err := snapshotObjectSpecChildCloneRulePackOwnership(context.Background(), db, []string{id})
	if err != nil {
		t.Fatalf("snapshotObjectSpecChildCloneRulePackOwnership = %v", err)
	}

	restoreSQL := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesCustomized` = ?, `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ?")
	mock.ExpectExec(restoreSQL).
		WithArgs(0, nil, nil, nil, nil, id).
		WillReturnResult(sqlmock.NewResult(0, 1))

	if err := snapshot.Restore(context.Background(), db); err != nil {
		t.Fatalf("snapshot.Restore = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func TestBackfillObjectSpecChildCloneRulePackOwnershipFromActivePacks(t *testing.T) {
	tests := []struct {
		name        string
		writeSource func(*testing.T) string
		// overlayRules, when non-nil, pins the exact canonical
		// ChildCloneRulesPackValue written for Parent__c (the split-source
		// overlay case); nil accepts any value.
		overlayRules []models.ChildCloneRule
	}{
		{name: "WritesMissingSidecar", writeSource: writeChildCloneRuleOwnershipSource},
		{
			name:         "UsesOverlaidChildCloneRulesComponent",
			writeSource:  writeChildCloneRuleOwnershipSplitSource,
			overlayRules: []models.ChildCloneRule{splitPackChildCloneRule()},
		},
	}
	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			sourceDir := tc.writeSource(t)
			db, mock, err := sqlmock.New()
			if err != nil {
				t.Fatalf("sqlmock: %v", err)
			}
			defer db.Close()

			mock.ExpectQuery("SELECT Name, Category, ManifestFormatVersion, SourceKind, SourceURI").
				WithArgs(string(AppPack)).
				WillReturnRows(sqlmock.NewRows([]string{"Name", "Category", "ManifestFormatVersion", "SourceKind", "SourceURI"}).
					AddRow("jira-like-sample", string(AppPack), SupportedManifestFormatVersion, string(PackSourceKindLocalPath), sourceDir))

			var parentPackValue any = sqlmock.AnyArg()
			if tc.overlayRules != nil {
				wantRules, err := canonicalChildCloneRulesPackValue(tc.overlayRules)
				if err != nil {
					t.Fatalf("canonicalChildCloneRulesPackValue: %v", err)
				}
				parentPackValue = wantRules
			}

			updateSQL := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ? AND COALESCE(`IsDeleted`, 0) = 0 AND ((COALESCE(`ChildCloneRulesPackName`, '') = '' AND COALESCE(`ChildCloneRulesPackBusinessKey`, '') = '') OR (`ChildCloneRulesPackName` = ? AND `ChildCloneRulesPackBusinessKey` = ? AND (`ChildCloneRulesPackValue` IS NULL OR TRIM(`ChildCloneRulesPackValue`) = '')))")
			mock.ExpectExec(updateSQL).
				WithArgs("jira-like-sample", "objects", string(ManageabilityLocked), "[]", domainschema.GenerateObjectID("Root__c"), "jira-like-sample", "objects").
				WillReturnResult(sqlmock.NewResult(0, 1))
			mock.ExpectExec(updateSQL).
				WithArgs("jira-like-sample", "objects", string(ManageabilityLocked), parentPackValue, domainschema.GenerateObjectID("Parent__c"), "jira-like-sample", "objects").
				WillReturnResult(sqlmock.NewResult(0, 1))
			mock.ExpectExec(updateSQL).
				WithArgs("jira-like-sample", "objects", string(ManageabilityLocked), "[]", domainschema.GenerateObjectID("Source__c"), "jira-like-sample", "objects").
				WillReturnResult(sqlmock.NewResult(0, 1))
			mock.ExpectExec(updateSQL).
				WithArgs("jira-like-sample", "objects", string(ManageabilityLocked), "[]", domainschema.GenerateObjectID("Target__c"), "jira-like-sample", "objects").
				WillReturnResult(sqlmock.NewResult(0, 1))

			selectSQL := regexp.QuoteMeta("SELECT `Id`, `ApiName`, COALESCE(`IsCustom`, 0), `ChildCloneRules`, COALESCE(`ChildCloneRulesPackName`, ''), COALESCE(`ChildCloneRulesPackBusinessKey`, ''), COALESCE(`ChildCloneRulesPackManageability`, ''), `ChildCloneRulesPackValue` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0 AND `ChildCloneRules` IS NOT NULL AND COALESCE(`ChildCloneRulesCustomized`, 0) = 0 ORDER BY `Id`")
			mock.ExpectQuery(selectSQL).
				WillReturnRows(sqlmock.NewRows([]string{"Id", "ApiName", "IsCustom", "ChildCloneRules", "ChildCloneRulesPackName", "ChildCloneRulesPackBusinessKey", "ChildCloneRulesPackManageability", "ChildCloneRulesPackValue"}))

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

func TestBackfillObjectSpecChildCloneRulePackOwnershipFromActivePacksUsesDBSchemaForActiveConnectorAssociationLookup(t *testing.T) {
	rawSourceDir := writeRuntimeConnectorAssociationPackSource(t)
	sourceDir, err := filepath.EvalSymlinks(rawSourceDir)
	if err != nil {
		t.Fatalf("EvalSymlinks source: %v", err)
	}
	db, mock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("sqlmock: %v", err)
	}
	defer db.Close()

	mock.ExpectQuery("SELECT Name, Category, ManifestFormatVersion, SourceKind, SourceURI").
		WithArgs(string(AppPack)).
		WillReturnRows(sqlmock.NewRows([]string{"Name", "Category", "ManifestFormatVersion", "SourceKind", "SourceURI"}).
			AddRow("jira-like-sample", string(AppPack), SupportedManifestFormatVersion, string(PackSourceKindLocalPath), sourceDir))
	expectRuntimeMetadataOpportunityAssociationLookupSchemaLoad(mock)

	updateSQL := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ? AND COALESCE(`IsDeleted`, 0) = 0 AND ((COALESCE(`ChildCloneRulesPackName`, '') = '' AND COALESCE(`ChildCloneRulesPackBusinessKey`, '') = '') OR (`ChildCloneRulesPackName` = ? AND `ChildCloneRulesPackBusinessKey` = ? AND (`ChildCloneRulesPackValue` IS NULL OR TRIM(`ChildCloneRulesPackValue`) = '')))")
	mock.ExpectExec(updateSQL).
		WithArgs("jira-like-sample", "crm", string(ManageabilityLocked), "[]", domainschema.GenerateObjectID("Account"), "jira-like-sample", "crm").
		WillReturnResult(sqlmock.NewResult(0, 0))
	mock.ExpectExec(updateSQL).
		WithArgs("jira-like-sample", "crm", string(ManageabilityLocked), "[]", domainschema.GenerateObjectID("Opportunity"), "jira-like-sample", "crm").
		WillReturnResult(sqlmock.NewResult(0, 0))

	updated, err := BackfillObjectSpecChildCloneRulePackOwnershipFromActivePacks(context.Background(), db)
	if err != nil {
		t.Fatalf("BackfillObjectSpecChildCloneRulePackOwnershipFromActivePacks rejected resolver-backed active connector association lookup: %v", err)
	}
	if updated != 0 {
		t.Fatalf("updated = %d, want 0", updated)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	id := domainschema.GenerateObjectID("Parent__c")
	liveRules, err := canonicalChildCloneRulesPackValue([]models.ChildCloneRule{testPackChildCloneRule("Name")})
	if err != nil {
		t.Fatalf("canonical live rules: %v", err)
	}

	selectSQL := regexp.QuoteMeta("SELECT `Id`, `ApiName`, `ChildCloneRules`, COALESCE(`ChildCloneRulesCustomized`, 0), COALESCE(`ChildCloneRulesPackName`, ''), COALESCE(`ChildCloneRulesPackBusinessKey`, ''), COALESCE(`ChildCloneRulesPackManageability`, ''), `ChildCloneRulesPackValue` FROM `_System_Object` WHERE COALESCE(`IsDeleted`, 0) = 0 AND `Id` IN (?) ORDER BY `Id`")
	mock.ExpectQuery(selectSQL).
		WithArgs(id).
		WillReturnRows(sqlmock.NewRows([]string{"Id", "ApiName", "ChildCloneRules", "ChildCloneRulesCustomized", "ChildCloneRulesPackName", "ChildCloneRulesPackBusinessKey", "ChildCloneRulesPackManageability", "ChildCloneRulesPackValue"}).
			AddRow(id, "Parent__c", liveRules, 0, "", "", "", nil))

	err = prepareObjectSpecChildCloneRulePackOwnershipForApplyWithOptions(context.Background(), db, "jira-like-sample", "objects", ManageabilityDefaultsOnly, []ObjectSpec{{
		APIName: "Parent__c",
	}}, childCloneRuleApplyOwnershipOptions{convergeSidecarlessEmptyDefaultsOnly: true})
	if err != nil {
		t.Fatalf("prepareObjectSpecChildCloneRulePackOwnershipForApplyWithOptions = %v", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	emptyDefaultsOnlySQL := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ? AND COALESCE(`IsDeleted`, 0) = 0 AND ((COALESCE(`ChildCloneRulesPackName`, '') = '' AND COALESCE(`ChildCloneRulesPackBusinessKey`, '') = '') OR (`ChildCloneRulesPackName` = ? AND `ChildCloneRulesPackBusinessKey` = ? AND (`ChildCloneRulesPackValue` IS NULL OR TRIM(`ChildCloneRulesPackValue`) = ''))) AND (`ChildCloneRules` IS NULL OR TRIM(`ChildCloneRules`) = '' OR TRIM(`ChildCloneRules`) = '[]')")
	mock.ExpectExec(emptyDefaultsOnlySQL).
		WithArgs("jira-like-sample", "objects", string(ManageabilityDefaultsOnly), "[]", domainschema.GenerateObjectID("Parent__c"), "jira-like-sample", "objects").
		WillReturnResult(sqlmock.NewResult(0, 0))

	updated, err := backfillObjectSpecChildCloneRulePackOwnership(
		context.Background(),
		db,
		"jira-like-sample",
		"objects",
		ManageabilityDefaultsOnly,
		[]ObjectSpec{{APIName: "Parent__c"}},
	)
	if err != nil {
		t.Fatalf("backfillObjectSpecChildCloneRulePackOwnership = %v", err)
	}
	if updated != 0 {
		t.Fatalf("updated = %d, want 0", updated)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	id := domainschema.GenerateObjectID("Root__c")

	updateSQL := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ?")
	mock.ExpectExec(updateSQL).
		WithArgs("jira-like-sample", "objects", string(ManageabilityLocked), "[]", id).
		WillReturnResult(sqlmock.NewResult(0, 0))

	existsSQL := regexp.QuoteMeta("SELECT 1 FROM `_System_Object` WHERE `Id` = ? AND COALESCE(`IsDeleted`, 0) = 0 LIMIT 1")
	mock.ExpectQuery(existsSQL).
		WithArgs(id).
		WillReturnRows(sqlmock.NewRows([]string{"1"}).AddRow(1))

	err = persistObjectSpecChildCloneRulePackOwnership(context.Background(), db, "jira-like-sample", "objects", ManageabilityLocked, []ObjectSpec{{
		APIName: "Root__c",
	}}, true)
	if err != nil {
		t.Fatalf("persistObjectSpecChildCloneRulePackOwnership = %v; want nil (idempotent no-op when row exists)", err)
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

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

	id := domainschema.GenerateObjectID("Missing__c")

	updateSQL := regexp.QuoteMeta("UPDATE `_System_Object` SET `ChildCloneRulesPackName` = ?, `ChildCloneRulesPackBusinessKey` = ?, `ChildCloneRulesPackManageability` = ?, `ChildCloneRulesPackValue` = ?, `LastModifiedDate` = NOW() WHERE `Id` = ?")
	mock.ExpectExec(updateSQL).
		WithArgs("jira-like-sample", "objects", string(ManageabilityLocked), "[]", id).
		WillReturnResult(sqlmock.NewResult(0, 0))

	existsSQL := regexp.QuoteMeta("SELECT 1 FROM `_System_Object` WHERE `Id` = ? AND COALESCE(`IsDeleted`, 0) = 0 LIMIT 1")
	mock.ExpectQuery(existsSQL).
		WithArgs(id).
		WillReturnRows(sqlmock.NewRows([]string{"1"}))

	err = persistObjectSpecChildCloneRulePackOwnership(context.Background(), db, "jira-like-sample", "objects", ManageabilityLocked, []ObjectSpec{{
		APIName: "Missing__c",
	}}, true)
	if err == nil {
		t.Fatal("persistObjectSpecChildCloneRulePackOwnership = nil; want error when row is missing")
	}
	if err := mock.ExpectationsWereMet(); err != nil {
		t.Fatalf("sql expectations: %v", err)
	}
}

func testPackChildCloneRule(fieldName string) models.ChildCloneRule {
	return models.ChildCloneRule{
		ParentLookupField: "Parent__c",
		SourceObject:      "Source__c",
		SourceLookupField: "Parent__c",
		TargetObject:      "Target__c",
		TargetLookupField: "Parent__c",
		FieldMappings:     map[string]string{"Name": fieldName},
	}
}

func splitPackChildCloneRule() models.ChildCloneRule {
	return models.ChildCloneRule{
		ParentLookupField: "Root__c",
		SourceObject:      "Source__c",
		SourceLookupField: "Root__c",
		TargetObject:      "Target__c",
		TargetLookupField: "Parent__c",
		FieldMappings:     map[string]string{"Name": "Name"},
	}
}

func writeChildCloneRuleOwnershipSource(t *testing.T) string {
	t.Helper()
	root := t.TempDir()
	objectsDir := filepath.Join(root, "objects")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir objects: %v", err)
	}
	manifest := `{
  "manifestFormatVersion": "1",
  "name": "jira-like-sample",
  "displayName": "Jira-like Sample",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {"min": "0.2.0", "maxExclusive": "0.3.0"},
  "components": [{
    "kind": "object_spec",
    "businessKey": "objects",
    "manageability": "locked",
    "path": "objects/objects.json"
  }]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack.json: %v", err)
	}
	payload := `[
  {
    "apiName": "Root__c",
    "label": "Root",
    "tableType": "custom_object",
    "sharingModel": "Private",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "isNameField": true}
    ]
  },
  {
    "apiName": "Parent__c",
    "label": "Parent",
    "tableType": "custom_object",
    "sharingModel": "Private",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "isNameField": true},
      {"apiName": "Root__c", "label": "Root", "type": "reference", "referenceTo": ["Root__c"], "createable": true, "updateable": true}
    ],
    "childCloneRules": [{
      "parentLookupField": "Root__c",
      "sourceObject": "Source__c",
      "sourceLookupField": "Root__c",
      "targetObject": "Target__c",
      "targetLookupField": "Parent__c",
      "fieldMappings": {"Name": "Name"}
    }]
  },
  {
    "apiName": "Source__c",
    "label": "Source",
    "tableType": "custom_object",
    "sharingModel": "Private",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "isNameField": true},
      {"apiName": "Root__c", "label": "Root", "type": "reference", "referenceTo": ["Root__c"], "createable": true, "updateable": true}
    ]
  },
  {
    "apiName": "Target__c",
    "label": "Target",
    "tableType": "custom_object",
    "sharingModel": "Private",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "isNameField": true, "createable": true, "updateable": true},
      {"apiName": "Parent__c", "label": "Parent", "type": "reference", "referenceTo": ["Parent__c"], "createable": true, "updateable": true}
    ]
  }
]`
	if err := os.WriteFile(filepath.Join(objectsDir, "objects.json"), []byte(payload), 0o644); err != nil {
		t.Fatalf("write object spec: %v", err)
	}
	return root
}

func writeChildCloneRuleOwnershipSplitSource(t *testing.T) string {
	t.Helper()
	root := t.TempDir()
	objectsDir := filepath.Join(root, "objects")
	rulesDir := filepath.Join(root, "child_clone_rules")
	if err := os.MkdirAll(objectsDir, 0o755); err != nil {
		t.Fatalf("mkdir objects: %v", err)
	}
	if err := os.MkdirAll(rulesDir, 0o755); err != nil {
		t.Fatalf("mkdir child_clone_rules: %v", err)
	}
	manifest := `{
  "manifestFormatVersion": "1",
  "name": "jira-like-sample",
  "displayName": "Jira-like Sample",
  "category": "app-pack",
  "version": "0.1.0",
  "platformCompatibility": {"min": "0.2.0", "maxExclusive": "0.3.0"},
  "components": [
    {
      "kind": "object_spec",
      "businessKey": "objects",
      "manageability": "locked",
      "path": "objects/objects.json"
    },
    {
      "kind": "child_clone_rules",
      "businessKey": "rules",
      "manageability": "locked",
      "path": "child_clone_rules/rules.json",
      "dependsOn": [{"kind": "object_spec", "businessKey": "objects"}]
    }
  ]
}`
	if err := os.WriteFile(filepath.Join(root, "pack.json"), []byte(manifest), 0o644); err != nil {
		t.Fatalf("write pack.json: %v", err)
	}
	payload := `[
  {
    "apiName": "Root__c",
    "label": "Root",
    "tableType": "custom_object",
    "sharingModel": "Private",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "isNameField": true}
    ]
  },
  {
    "apiName": "Parent__c",
    "label": "Parent",
    "tableType": "custom_object",
    "sharingModel": "Private",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "isNameField": true},
      {"apiName": "Root__c", "label": "Root", "type": "reference", "referenceTo": ["Root__c"], "createable": true, "updateable": true}
    ]
  },
  {
    "apiName": "Source__c",
    "label": "Source",
    "tableType": "custom_object",
    "sharingModel": "Private",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "isNameField": true},
      {"apiName": "Root__c", "label": "Root", "type": "reference", "referenceTo": ["Root__c"], "createable": true, "updateable": true}
    ]
  },
  {
    "apiName": "Target__c",
    "label": "Target",
    "tableType": "custom_object",
    "sharingModel": "Private",
    "fields": [
      {"apiName": "Name", "label": "Name", "type": "string", "isNameField": true, "createable": true, "updateable": true},
      {"apiName": "Parent__c", "label": "Parent", "type": "reference", "referenceTo": ["Parent__c"], "createable": true, "updateable": true}
    ]
  }
]`
	if err := os.WriteFile(filepath.Join(objectsDir, "objects.json"), []byte(payload), 0o644); err != nil {
		t.Fatalf("write object spec: %v", err)
	}
	rulesPayload := `{
  "Parent__c": [{
    "parentLookupField": "Root__c",
    "sourceObject": "Source__c",
    "sourceLookupField": "Root__c",
    "targetObject": "Target__c",
    "targetLookupField": "Parent__c",
    "fieldMappings": {"Name": "Name"}
  }]
}`
	if err := os.WriteFile(filepath.Join(rulesDir, "rules.json"), []byte(rulesPayload), 0o644); err != nil {
		t.Fatalf("write child clone rules: %v", err)
	}
	return root
}
