package runtimeworker

import (
	"fmt"
	"testing"

	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/nexuscrm/persistence"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

// Warm binding must do identity/authority work, not reconstruct a schema. This
// relative allocation budget catches the incident even when artifact I/O and
// returned pointers are already cached. It permits implementation-independent
// fixed overhead rather than pinning private compiler call order.
func TestCommandBindingWarmWorkDoesNotScaleWithSchema(t *testing.T) {
	var allocations [2]float64
	for index, width := range []int{0, 200} {
		store, command, _, _ := commandBindingReuseViews(t, width)
		binder := commandBindingReuseBinder(t, store, command)
		encoded := commandBindingReuseEncoded(t, command)
		open := func() {
			authority, err := (commandViewBinderAdapter{binder: binder}).OpenCommand(
				t.Context(), encoded, RuntimeFlowCommandKind, RuntimeFlowCommandPayloadFormatVersionV3,
			)
			if err != nil {
				t.Fatal(err)
			}
			if _, err := authority.Bind(t.Context()); err != nil {
				t.Fatal(err)
			}
		}
		open()
		allocations[index] = testing.AllocsPerRun(20, open)
	}
	t.Logf("warm allocations/op: small=%.0f wide=%.0f", allocations[0], allocations[1])
	if allocations[1] > allocations[0]+100 {
		t.Fatalf("warm binding reconstructs metadata: allocations grow from %.0f to %.0f with schema width", allocations[0], allocations[1])
	}
}

func BenchmarkCommandBindingWarm(b *testing.B) {
	for _, width := range []int{0, 200} {
		b.Run(fmt.Sprintf("fields%d", width), func(b *testing.B) {
			store, command, _, _ := commandBindingReuseViews(b, width)
			binder := commandBindingReuseBinder(b, store, command)
			encoded := commandBindingReuseEncoded(b, command)
			adapter := commandViewBinderAdapter{binder: binder}
			if _, err := adapter.OpenCommand(b.Context(), encoded, RuntimeFlowCommandKind, RuntimeFlowCommandPayloadFormatVersionV3); err != nil {
				b.Fatal(err)
			}
			b.ReportAllocs()
			b.ResetTimer()
			for b.Loop() {
				authority, err := adapter.OpenCommand(b.Context(), encoded, RuntimeFlowCommandKind, RuntimeFlowCommandPayloadFormatVersionV3)
				if err != nil {
					b.Fatal(err)
				}
				if _, err := authority.Bind(b.Context()); err != nil {
					b.Fatal(err)
				}
			}
		})
	}
}

func commandBindingReuseViews(t testing.TB, width int) (catalogsnapshot.Store, *catalogsnapshot.RuntimeMetadataView, *catalogsnapshot.RuntimeMetadataView, *catalogsnapshot.RuntimeMetadataView) {
	t.Helper()
	return workerRuntimeFlowCommandViewsWithMetadataClosure(t,
		[]byte(`{"runAtomically":true,"startElementReference":"Assign","assignments":[{"name":"Assign"}]}`),
		"", bindingReuseFields(width), nil,
	)
}

func bindingReuseFields(width int) []models.FieldMetadata {
	fields := make([]models.FieldMetadata, width)
	for index := range fields {
		name := fmt.Sprintf("ExtraField%03d", index)
		fields[index] = models.FieldMetadata{
			ID: "field-" + name, APIName: name, Label: name, PhysicalName: name, Type: constants.FieldTypeString,
		}
	}
	return fields
}

func TestScheduledBindingWarmWorkDoesNotScaleWithSchema(t *testing.T) {
	var allocations [2]float64
	for index, width := range []int{0, 200} {
		store, mutation, flow := workerScheduledFlowView(t, bindingReuseFields(width)...)
		binder := commandBindingReuseBinder(t, store, flow)
		encoded := commandBindingReuseEncoded(t, flow)
		open := func() {
			prepared, err := binder.bindScheduledFlow(t.Context(), encoded, "definition-scheduled", "version-scheduled", "")
			if err != nil {
				t.Fatal(err)
			}
			ctx, err := prepared.InstallExecution(t.Context())
			if err != nil {
				t.Fatal(err)
			}
			assertBindingReuseViews(t, ctx, mutation, flow)
		}
		open()
		allocations[index] = testing.AllocsPerRun(20, open)
	}
	t.Logf("warm scheduled allocations/op: small=%.0f wide=%.0f", allocations[0], allocations[1])
	if allocations[1] > allocations[0]+100 {
		t.Fatalf("warm scheduled binding reconstructs metadata: allocations grow from %.0f to %.0f", allocations[0], allocations[1])
	}
}

func commandBindingReuseBinder(t testing.TB, store catalogsnapshot.Store, view *catalogsnapshot.RuntimeMetadataView) *mutationArtifactBinder {
	t.Helper()
	lineage := view.Lineage()
	binder, err := newTestMutationArtifactBinder(store, persistence.RuntimeWorkScope{
		OrgID: view.OrgID(), AppInstallationID: view.AppInstallationID(), RuntimeCapabilityID: view.CapabilityID(),
	}, catalogsnapshot.Compatibility{SchemaGeneration: lineage.SchemaGeneration, EngineGeneration: lineage.EngineGeneration})
	if err != nil {
		t.Fatal(err)
	}
	return binder
}

func commandBindingReuseEncoded(t testing.TB, view *catalogsnapshot.RuntimeMetadataView) string {
	t.Helper()
	binding, err := catalogsnapshot.RuntimeWorkBindingFromView(view)
	if err != nil {
		t.Fatal(err)
	}
	encoded, err := catalogsnapshot.EncodeRuntimeWorkBinding(binding)
	if err != nil {
		t.Fatal(err)
	}
	return encoded
}
