package analytics

import (
	"context"
	"reflect"
	"testing"

	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/prometheus/client_golang/prometheus/testutil"
)

type recordingRollupTxManager struct {
	active      bool
	pessimistic bool
}

func (m recordingRollupTxManager) WithTransaction(ctx context.Context, fn func(context.Context) error) error {
	return fn(ctx)
}
func (m recordingRollupTxManager) WithPessimisticTransaction(ctx context.Context, fn func(context.Context) error) error {
	return fn(ctx)
}
func (m recordingRollupTxManager) WithRetry(ctx context.Context, fn func(context.Context) error, _ int) error {
	return fn(ctx)
}
func (m recordingRollupTxManager) WithPessimisticRetry(ctx context.Context, fn func(context.Context) error, _ int) error {
	return fn(ctx)
}
func (m recordingRollupTxManager) IsTransactionActive(context.Context) bool { return m.active }
func (m recordingRollupTxManager) IsPessimisticTx(context.Context) bool     { return m.pessimistic }
func (m recordingRollupTxManager) ExtractTx(context.Context) interface{}    { return nil }
func (m recordingRollupTxManager) DetachTx(ctx context.Context) context.Context {
	return ctx
}

var _ ports.TransactionManager = recordingRollupTxManager{}

type stubDerivedUpdateHistoryPlanner struct {
	*stubDerivedWriteCapture
	fieldIDs []string
}

func (s *stubDerivedUpdateHistoryPlanner) DerivedUpdateHistoryFieldIDs(context.Context, string, ...string) ([]string, error) {
	return append([]string(nil), s.fieldIDs...), nil
}

func assertSkippedLockMetricUnchanged(t *testing.T, before float64) {
	t.Helper()

	after := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	if after != before {
		t.Fatalf("rollupLockSkippedOnOptimistic = %v after happy-path call, want unchanged at %v", after, before)
	}
}

func assertLockCalcUpdateSequence(t *testing.T, events []string, tuples int) {
	t.Helper()

	wantLen := tuples * 3
	if len(events) != wantLen {
		t.Fatalf("events len = %d, want %d (%v)", len(events), wantLen, events)
	}
	for i := 0; i < tuples; i++ {
		base := i * 3
		if events[base] != "lock" || events[base+1] != "calc" || events[base+2] != "update" {
			t.Fatalf("events[%d:%d] = %v, want [lock calc update] (full=%v)", base, base+3, events[base:base+3], events)
		}
	}
}

func TestLockParentFailClosedOnOptimisticTx(t *testing.T) {
	before := testutil.ToFloat64(rollupLockSkippedOnOptimistic)

	svc := &RollupService{
		txManager: recordingRollupTxManager{active: true, pessimistic: false},
		derived:   &stubDerivedWriteCapture{},
	}

	err := svc.lockParent(context.Background(), "Account", "acc-001")
	if err != errRollupLockOnOptimisticTx {
		t.Fatalf("lockParent err = %v, want errRollupLockOnOptimisticTx", err)
	}

	after := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	if after <= before {
		t.Fatalf("rollupLockSkippedOnOptimistic = %v after call, want > %v", after, before)
	}
}

func TestProcessRollupsLocksParentBeforeCalculateAndUpdate(t *testing.T) {
	const (
		parentObj   = "Account"
		parentID    = "acc-001"
		childObj    = "Opportunity"
		rollupField = "TotalAmount__c"
		relField    = "AccountId"
	)

	parentSchema := &models.ObjectMetadata{
		APIName: parentObj,
		Fields: []models.FieldMetadata{{
			APIName:    rollupField,
			Type:       constants.FieldTypeRollupSummary,
			Updateable: false,
			RollupConfig: &models.RollupConfig{
				CalcType:          "SUM",
				SummaryObject:     childObj,
				SummaryField:      "Amount",
				RelationshipField: relField,
			},
		}},
	}

	before := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	var events []string
	meta := newFakeMetadata(map[string]*models.ObjectMetadata{parentObj: parentSchema})
	repo := &stubRollupCalcRepo{calcValue: float64(42), eventSink: &events}
	dw := &stubDerivedWriteCapture{eventSink: &events}
	svc := NewRollupService(repo, meta, recordingRollupTxManager{active: true, pessimistic: true}, noopLogger())
	svc.SetDerivedWriter(dw)

	childRecord := models.SObject{relField: parentID, "Amount": float64(100)}
	if err := svc.ProcessRollups(context.Background(), childObj, childRecord); err != nil {
		t.Fatalf("ProcessRollups: %v", err)
	}

	assertLockCalcUpdateSequence(t, events, 1)
	if len(dw.locks) != 1 {
		t.Fatalf("LockDerivedRecord calls = %d, want 1", len(dw.locks))
	}
	if len(dw.calls) != 1 {
		t.Fatalf("UpdateDerived calls = %d, want 1", len(dw.calls))
	}
	if repo.currentReadCalls != 1 {
		t.Fatalf("CalculateRollupCurrentRead calls = %d, want 1 on locked current-read aggregate path", repo.currentReadCalls)
	}
	if repo.calculateCalls != 0 {
		t.Fatalf("CalculateRollup calls = %d, want 0 when current-read aggregate path is active", repo.calculateCalls)
	}
	assertSkippedLockMetricUnchanged(t, before)
}

func TestAffectedParentRowsAddsEveryStableDerivedFieldToAttemptPlan(t *testing.T) {
	const (
		parentObj = "Account"
		parentID  = "acc-001"
		childObj  = "Opportunity"
		relField  = "AccountId"
	)
	parentSchema := &models.ObjectMetadata{
		APIName: parentObj,
		Fields: []models.FieldMetadata{
			{
				ID:           "field-history-account-pipeline",
				APIName:      "OpenPipeline__c",
				Type:         constants.FieldTypeRollupSummary,
				TrackHistory: true,
				RollupConfig: &models.RollupConfig{CalcType: "SUM", SummaryObject: childObj, SummaryField: "Amount", RelationshipField: relField},
			},
			{
				ID:           "field-untracked-account-count",
				APIName:      "OpportunityCount__c",
				Type:         constants.FieldTypeRollupSummary,
				TrackHistory: false,
				RollupConfig: &models.RollupConfig{CalcType: "COUNT", SummaryObject: childObj, RelationshipField: relField},
			},
		},
	}
	svc := NewRollupService(
		&stubRollupCalcRepo{},
		newFakeMetadata(map[string]*models.ObjectMetadata{parentObj: parentSchema}),
		recordingRollupTxManager{active: true, pessimistic: true},
		noopLogger(),
	)
	svc.SetDerivedWriter(&stubDerivedUpdateHistoryPlanner{
		stubDerivedWriteCapture: &stubDerivedWriteCapture{},
		fieldIDs:                []string{"field-history-account-pipeline", "field-untracked-account-count", "field-account-systemmodstamp"},
	})

	ctx := ports.WithPlannedHistorySequenceLocks(context.Background(), nil)
	keys, err := svc.AffectedParentRows(ctx, childObj, models.SObject{relField: parentID})
	if err != nil {
		t.Fatalf("AffectedParentRows(): %v", err)
	}
	if len(keys) != 1 || keys[0].ObjectName != parentObj || keys[0].RecordID != parentID {
		t.Fatalf("AffectedParentRows() = %#v, want Account/acc-001", keys)
	}
	if got, want := ports.PlannedHistorySequenceLocks(ctx), []string{"field-account-systemmodstamp", "field-history-account-pipeline", "field-untracked-account-count"}; !reflect.DeepEqual(got, want) {
		t.Fatalf("planned history fields = %#v, want %#v", got, want)
	}
}

func TestProcessRollupsForUpdateHappyPathLocksBeforeCalculateAndUpdate(t *testing.T) {
	const (
		parentObj   = "Parent"
		childObj    = "Child"
		rollupField = "TotalAmount"
		relField    = "ParentId"
		oldParentID = "p-002"
		newParentID = "p-001"
	)

	parentSchema := &models.ObjectMetadata{
		APIName: parentObj,
		Fields: []models.FieldMetadata{{
			APIName: rollupField,
			Type:    constants.FieldTypeRollupSummary,
			RollupConfig: &models.RollupConfig{
				CalcType:          "SUM",
				SummaryObject:     childObj,
				SummaryField:      "Amount",
				RelationshipField: relField,
			},
		}},
	}

	before := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	var events []string
	meta := newFakeMetadata(map[string]*models.ObjectMetadata{parentObj: parentSchema})
	repo := &stubRollupCalcRepo{calcValue: float64(42), eventSink: &events}
	dw := &stubDerivedWriteCapture{eventSink: &events}
	svc := NewRollupService(repo, meta, recordingRollupTxManager{active: true, pessimistic: true}, noopLogger())
	svc.SetDerivedWriter(dw)

	finalRecord := models.SObject{relField: newParentID, "Amount": float64(50)}
	oldRecord := models.SObject{relField: oldParentID, "Amount": float64(20)}
	if err := svc.ProcessRollupsForUpdate(context.Background(), childObj, finalRecord, oldRecord); err != nil {
		t.Fatalf("ProcessRollupsForUpdate: %v", err)
	}

	assertLockCalcUpdateSequence(t, events, 2)
	if len(dw.locks) != 2 {
		t.Fatalf("LockDerivedRecord calls = %d, want 2", len(dw.locks))
	}
	if len(dw.calls) != 2 {
		t.Fatalf("UpdateDerived calls = %d, want 2", len(dw.calls))
	}
	if repo.currentReadCalls != 2 {
		t.Fatalf("CalculateRollupCurrentRead calls = %d, want 2 on locked current-read aggregate path", repo.currentReadCalls)
	}
	if repo.calculateCalls != 0 {
		t.Fatalf("CalculateRollup calls = %d, want 0 when current-read aggregate path is active", repo.calculateCalls)
	}
	if dw.locks[0].recordID != newParentID || dw.locks[1].recordID != oldParentID {
		t.Fatalf("LockDerivedRecord order = [%s %s], want [%s %s]", dw.locks[0].recordID, dw.locks[1].recordID, newParentID, oldParentID)
	}
	if dw.calls[0].recordID != newParentID || dw.calls[1].recordID != oldParentID {
		t.Fatalf("UpdateDerived order = [%s %s], want [%s %s]", dw.calls[0].recordID, dw.calls[1].recordID, newParentID, oldParentID)
	}
	assertSkippedLockMetricUnchanged(t, before)
}

func TestProcessBulkRollupsHappyPathLocksBeforeCalculateAndUpdate(t *testing.T) {
	const (
		parentObj   = "Project"
		parentID1   = "proj-001"
		parentID2   = "proj-002"
		childObj    = "Task"
		rollupField = "TotalBudget"
		relField    = "ProjectId"
	)

	parentSchema := &models.ObjectMetadata{
		APIName: parentObj,
		Fields: []models.FieldMetadata{{
			APIName:    rollupField,
			Type:       constants.FieldTypeRollupSummary,
			Updateable: false,
			RollupConfig: &models.RollupConfig{
				CalcType:          "SUM",
				SummaryObject:     childObj,
				SummaryField:      "Budget",
				RelationshipField: relField,
			},
		}},
	}

	before := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	var events []string
	meta := newFakeMetadata(map[string]*models.ObjectMetadata{parentObj: parentSchema})
	repo := &stubRollupCalcRepo{calcValue: float64(42), eventSink: &events}
	dw := &stubDerivedWriteCapture{eventSink: &events}
	svc := NewRollupService(repo, meta, recordingRollupTxManager{active: true, pessimistic: true}, noopLogger())
	svc.SetDerivedWriter(dw)

	childRecords := []models.SObject{
		{relField: parentID2, "Budget": float64(20)},
		{relField: parentID1, "Budget": float64(10)},
	}
	landedRows := []bool{true, true}
	if err := svc.ProcessBulkRollups(context.Background(), childObj, childRecords, landedRows); err != nil {
		t.Fatalf("ProcessBulkRollups: %v", err)
	}

	assertLockCalcUpdateSequence(t, events, 2)
	if len(dw.locks) != 2 {
		t.Fatalf("LockDerivedRecord calls = %d, want 2", len(dw.locks))
	}
	if len(dw.calls) != 2 {
		t.Fatalf("UpdateDerived calls = %d, want 2", len(dw.calls))
	}
	if repo.currentReadCalls != 2 {
		t.Fatalf("CalculateRollupCurrentRead calls = %d, want 2 on locked current-read aggregate path", repo.currentReadCalls)
	}
	if repo.calculateCalls != 0 {
		t.Fatalf("CalculateRollup calls = %d, want 0 when current-read aggregate path is active", repo.calculateCalls)
	}
	if dw.locks[0].recordID != parentID1 || dw.locks[1].recordID != parentID2 {
		t.Fatalf("LockDerivedRecord order = [%s %s], want [%s %s]", dw.locks[0].recordID, dw.locks[1].recordID, parentID1, parentID2)
	}
	if dw.calls[0].recordID != parentID1 || dw.calls[1].recordID != parentID2 {
		t.Fatalf("UpdateDerived order = [%s %s], want [%s %s]", dw.calls[0].recordID, dw.calls[1].recordID, parentID1, parentID2)
	}
	assertSkippedLockMetricUnchanged(t, before)
}

func TestProcessRollups_MergesMultipleRollupFieldsForSameParent(t *testing.T) {
	const (
		parentObj = "Parent__c"
		parentID  = "parent-001"
		childObj  = "Child__c"
		relField  = "ParentRef__c"
	)

	parentSchema := &models.ObjectMetadata{
		APIName: parentObj,
		Fields: []models.FieldMetadata{
			{
				APIName: "ChildCount__c",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "COUNT",
					SummaryObject:     childObj,
					SummaryField:      "*",
					RelationshipField: relField,
				},
			},
			{
				APIName: "ResponseCount__c",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "COUNT",
					SummaryObject:     childObj,
					SummaryField:      "*",
					RelationshipField: relField,
				},
			},
		},
	}

	before := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	var events []string
	meta := newFakeMetadata(map[string]*models.ObjectMetadata{parentObj: parentSchema})
	repo := &stubRollupCalcRepo{calcValue: float64(42), eventSink: &events}
	dw := &stubDerivedWriteCapture{eventSink: &events}
	svc := NewRollupService(repo, meta, recordingRollupTxManager{active: true, pessimistic: true}, noopLogger())
	svc.SetDerivedWriter(dw)

	childRecord := models.SObject{relField: parentID}
	if err := svc.ProcessRollups(context.Background(), childObj, childRecord); err != nil {
		t.Fatalf("ProcessRollups: %v", err)
	}

	if got := len(dw.locks); got != 1 {
		t.Fatalf("LockDerivedRecord calls = %d, want 1 merged parent lock", got)
	}
	if got := len(dw.calls); got != 1 {
		t.Fatalf("UpdateDerived calls = %d, want 1 merged parent update", got)
	}
	if got := len(events); got != 4 || events[0] != "lock" || events[1] != "calc" || events[2] != "calc" || events[3] != "update" {
		t.Fatalf("events = %v, want [lock calc calc update]", events)
	}
	if updates := dw.calls[0].updates; len(updates) != 2 || updates["ChildCount__c"] == nil || updates["ResponseCount__c"] == nil {
		t.Fatalf("merged updates = %#v, want ChildCount__c + ResponseCount__c", updates)
	}
	if repo.currentReadCalls != 2 {
		t.Fatalf("CalculateRollupCurrentRead calls = %d, want 2 for merged rollup fields on current-read aggregate path", repo.currentReadCalls)
	}
	if repo.calculateCalls != 0 {
		t.Fatalf("CalculateRollup calls = %d, want 0 when current-read aggregate path is active", repo.calculateCalls)
	}
	assertSkippedLockMetricUnchanged(t, before)
}

func TestProcessRollups_CountInsertUsesDirectDelta(t *testing.T) {
	const (
		parentObj = "Parent__c"
		parentID  = "parent-001"
		childObj  = "Child__c"
		relField  = "ParentRef__c"
	)

	parentSchema := &models.ObjectMetadata{
		APIName: parentObj,
		Fields: []models.FieldMetadata{
			{
				APIName: "ChildCount__c",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "COUNT",
					SummaryObject:     childObj,
					SummaryField:      "*",
					RelationshipField: relField,
					Filter:            stringPtr("ChildRef__c IS NOT NULL"),
				},
			},
			{
				APIName: "ResponseCount__c",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "COUNT",
					SummaryObject:     childObj,
					SummaryField:      "*",
					RelationshipField: relField,
					Filter:            stringPtr("ResponseFlag__c = true"),
				},
			},
		},
	}

	before := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	meta := newFakeMetadata(map[string]*models.ObjectMetadata{parentObj: parentSchema})
	repo := &stubRollupCalcRepo{
		recordMatchByFilter: map[string]bool{
			"ChildRef__c IS NOT NULL": true,
			"ResponseFlag__c = true":  true,
		},
	}
	dw := &stubDerivedWriteCapture{}
	svc := NewRollupService(repo, meta, recordingRollupTxManager{active: true, pessimistic: true}, noopLogger())
	svc.SetDerivedWriter(dw)

	childRecord := models.SObject{
		constants.FieldID: "child-001",
		relField:          parentID,
		"ChildRef__c":     "child-ref-001",
		"ResponseFlag__c": true,
	}
	if err := svc.ProcessInsertRollups(context.Background(), childObj, childRecord); err != nil {
		t.Fatalf("ProcessInsertRollups: %v", err)
	}

	if len(dw.calls) != 1 {
		t.Fatalf("UpdateDerived calls = %d, want 1", len(dw.calls))
	}
	if got := dw.calls[0].updates["ChildCount__c"]; got != int64(1) {
		t.Fatalf("ChildCount__c delta = %#v, want +1", got)
	}
	if got := dw.calls[0].updates["ResponseCount__c"]; got != int64(1) {
		t.Fatalf("ResponseCount__c delta = %#v, want +1", got)
	}
	if repo.calculateCalls != 0 || repo.currentReadCalls != 0 {
		t.Fatalf("aggregate recompute calls = calculate:%d currentRead:%d, want 0/0 on delta path", repo.calculateCalls, repo.currentReadCalls)
	}
	assertSkippedLockMetricUnchanged(t, before)
}

func TestProcessRollups_CountInsertEventualEnqueuesDeltas(t *testing.T) {
	const (
		parentObj = "Parent__c"
		parentID  = "parent-001"
		childObj  = "Child__c"
		relField  = "ParentRef__c"
	)

	parentSchema := &models.ObjectMetadata{
		APIName: parentObj,
		Fields: []models.FieldMetadata{
			{
				APIName: "ChildCount__c",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "COUNT",
					SummaryObject:     childObj,
					SummaryField:      "*",
					RelationshipField: relField,
					Filter:            stringPtr("ChildRef__c IS NOT NULL"),
					MaintenanceMode:   "eventual",
				},
			},
			{
				APIName: "ResponseCount__c",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "COUNT",
					SummaryObject:     childObj,
					SummaryField:      "*",
					RelationshipField: relField,
					Filter:            stringPtr("ResponseFlag__c = true"),
					MaintenanceMode:   "eventual",
				},
			},
		},
	}

	before := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	meta := newFakeMetadata(map[string]*models.ObjectMetadata{parentObj: parentSchema})
	repo := &stubRollupCalcRepo{
		recordMatchByFilter: map[string]bool{
			"ChildRef__c IS NOT NULL": true,
			"ResponseFlag__c = true":  true,
		},
	}
	dw := &stubDerivedWriteCapture{eventual: true}
	svc := NewRollupService(repo, meta, recordingRollupTxManager{active: true, pessimistic: true}, noopLogger())
	svc.SetDerivedWriter(dw)

	childRecord := models.SObject{
		constants.FieldID: "child-001",
		relField:          parentID,
		"ChildRef__c":     "child-ref-001",
		"ResponseFlag__c": true,
	}
	keys, err := svc.AffectedParentRowsForInsert(context.Background(), childObj, childRecord)
	if err != nil {
		t.Fatalf("AffectedParentRowsForInsert: %v", err)
	}
	if len(keys) != 0 {
		t.Fatalf("AffectedParentRowsForInsert keys = %#v, want no parent locks for eventual count insert", keys)
	}
	if err := svc.ProcessInsertRollups(context.Background(), childObj, childRecord); err != nil {
		t.Fatalf("ProcessInsertRollups: %v", err)
	}

	if len(dw.calls) != 0 {
		t.Fatalf("UpdateDerived calls = %d, want 0 for eventual enqueue path", len(dw.calls))
	}
	if len(dw.locks) != 0 {
		t.Fatalf("LockDerivedRecord calls = %d, want 0 before eventual enqueue path", len(dw.locks))
	}
	if len(dw.enqueues) != 1 {
		t.Fatalf("EnqueueDerivedCountRollupDeltas calls = %d, want 1", len(dw.enqueues))
	}
	got := dw.enqueues[0]
	if got.sourceObject != childObj || got.sourceID != "child-001" || got.parentObject != parentObj || got.parentID != parentID {
		t.Fatalf("enqueue identity = %#v, want child and parent identities", got)
	}
	if got.deltas["ChildCount__c"] != 1 || got.deltas["ResponseCount__c"] != 1 {
		t.Fatalf("enqueue deltas = %#v, want both count fields +1", got.deltas)
	}
	if repo.calculateCalls != 0 || repo.currentReadCalls != 0 {
		t.Fatalf("aggregate recompute calls = calculate:%d currentRead:%d, want 0/0 on enqueue path", repo.calculateCalls, repo.currentReadCalls)
	}
	assertSkippedLockMetricUnchanged(t, before)
}

func TestProcessRollups_CountNonInsertUsesFullRecalc(t *testing.T) {
	const (
		parentObj = "Parent__c"
		parentID  = "parent-001"
		childObj  = "Child__c"
		relField  = "ParentRef__c"
	)

	parentSchema := &models.ObjectMetadata{
		APIName: parentObj,
		Fields: []models.FieldMetadata{
			{
				APIName: "ChildCount__c",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "COUNT",
					SummaryObject:     childObj,
					SummaryField:      "*",
					RelationshipField: relField,
					Filter:            stringPtr("ChildRef__c IS NOT NULL"),
				},
			},
			{
				APIName: "ResponseCount__c",
				Type:    constants.FieldTypeRollupSummary,
				RollupConfig: &models.RollupConfig{
					CalcType:          "COUNT",
					SummaryObject:     childObj,
					SummaryField:      "*",
					RelationshipField: relField,
					Filter:            stringPtr("ResponseFlag__c = true"),
				},
			},
		},
	}

	before := testutil.ToFloat64(rollupLockSkippedOnOptimistic)
	var events []string
	meta := newFakeMetadata(map[string]*models.ObjectMetadata{parentObj: parentSchema})
	repo := &stubRollupCalcRepo{
		calcValue: float64(0),
		recordMatchByFilter: map[string]bool{
			"ChildRef__c IS NOT NULL": true,
			"ResponseFlag__c = true":  true,
		},
		eventSink: &events,
	}
	dw := &stubDerivedWriteCapture{
		eventSink: &events,
		rows: map[string]models.SObject{
			parentObj + "|" + parentID: {
				"ChildCount__c":    "41",
				"ResponseCount__c": float64(9),
			},
		},
	}
	svc := NewRollupService(repo, meta, recordingRollupTxManager{active: true, pessimistic: true}, noopLogger())
	svc.SetDerivedWriter(dw)

	childRecord := models.SObject{
		constants.FieldID: "child-001",
		relField:          parentID,
		"ChildRef__c":     "child-ref-001",
		"ResponseFlag__c": true,
	}
	if err := svc.ProcessRollups(context.Background(), childObj, childRecord); err != nil {
		t.Fatalf("ProcessRollups: %v", err)
	}

	if got := events; len(got) != 4 || got[0] != "lock" || got[1] != "calc" || got[2] != "calc" || got[3] != "update" {
		t.Fatalf("events = %v, want [lock calc calc update]", got)
	}
	if len(dw.calls) != 1 {
		t.Fatalf("UpdateDerived calls = %d, want 1", len(dw.calls))
	}
	if got := dw.calls[0].updates["ChildCount__c"]; got != float64(0) {
		t.Fatalf("ChildCount__c update = %#v, want 0 from full recalc", got)
	}
	if got := dw.calls[0].updates["ResponseCount__c"]; got != float64(0) {
		t.Fatalf("ResponseCount__c update = %#v, want 0 from full recalc", got)
	}
	if repo.calculateCalls != 0 || repo.currentReadCalls != 2 {
		t.Fatalf("aggregate recompute calls = calculate:%d currentRead:%d, want 0/2 on non-insert current-read path", repo.calculateCalls, repo.currentReadCalls)
	}
	assertSkippedLockMetricUnchanged(t, before)
}

func stringPtr(v string) *string { return &v }
