package formula

import (
	"strings"
	"testing"

	"github.com/nexuscrm/kernel/pkg/formula/spec"
	"github.com/nexuscrm/kernel/pkg/utils"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func testFormulaFunctionBundlePayload() []byte {
	return []byte(`{
		"functions": [
			{
				"name": "PACK_PREFIX",
				"minArgs": 1,
				"maxArgs": 1,
				"return": { "kind": "fixed", "type": "string" },
				"args": [{ "type": "string", "nullable": true }],
				"evaluator": {
					"kind": "formula_expr_v1",
					"params": ["value"],
					"body": "value + \"-probe\""
				}
			}
		]
	}`)
}

func testFormulaFunctionBundle(t *testing.T) FunctionBundle {
	t.Helper()
	return testFormulaFunctionBundleWithPayload(t, testFormulaFunctionBundlePayload())
}

func testFormulaFunctionBundleWithPayload(t *testing.T, payload []byte) FunctionBundle {
	t.Helper()
	bundle, err := ParseFunctionBundlePayload(payload)
	if err != nil {
		t.Fatalf("ParseFunctionBundlePayload: %v", err)
	}
	return bundle
}

func testIncludesFunctionBundle(t *testing.T) FunctionBundle {
	t.Helper()
	return testFormulaFunctionBundleWithPayload(t, []byte(`{
		"functions": [
			{
				"name": "INCLUDES",
				"minArgs": 2,
				"maxArgs": 2,
				"return": { "kind": "fixed", "type": "boolean" },
				"args": [
					{ "type": "string", "nullable": true },
					{ "type": "string", "nullable": true }
				],
				"evaluator": {
					"kind": "formula_expr_v1",
					"params": ["field", "value"],
					"body": "INCLUDES(field, value)"
				}
			}
		]
	}`))
}

func TestEngineRegistersSerializableFormulaExpressionBundle(t *testing.T) {
	engine := NewEngine()
	if err := engine.RegisterFunctionBundle(testFormulaFunctionBundle(t)); err != nil {
		t.Fatalf("RegisterFunctionBundle: %v", err)
	}

	if err := engine.ValidateSyntax(`PACK_PREFIX(Name)`); err != nil {
		t.Fatalf("ValidateSyntax external function = %v; want nil", err)
	}
	got, err := engine.Evaluate(`PACK_PREFIX(Name)`, &Context{Record: map[string]interface{}{"Name": "alpha"}})
	if err != nil {
		t.Fatalf("Evaluate external function: %v", err)
	}
	if got != "alpha-probe" {
		t.Fatalf("Evaluate external function = %v, want alpha-probe", got)
	}
}

func TestRegisterFunctionBundleClearsNormalizedFormulaCache(t *testing.T) {
	engine := NewEngine(WithoutRuntimeFunctionBundles())

	got := engine.normalizeFormulaExpression(`if(true, "a", "b")`)
	if !strings.Contains(got, `IF(`) {
		t.Fatalf("normalizeFormulaExpression = %q, want IF canonicalized", got)
	}
	if len(engine.normalizedCache) != 1 {
		t.Fatalf("normalized cache length after first normalize = %d, want 1", len(engine.normalizedCache))
	}

	if err := engine.RegisterFunctionBundle(testFormulaFunctionBundle(t)); err != nil {
		t.Fatalf("RegisterFunctionBundle: %v", err)
	}
	if len(engine.normalizedCache) != 0 {
		t.Fatalf("normalized cache length after function bundle registration = %d, want 0", len(engine.normalizedCache))
	}
	if got := engine.normalizeFormulaExpression(`pack_prefix(Name)`); got != `PACK_PREFIX(Name)` {
		t.Fatalf("normalize bundle function = %q, want PACK_PREFIX(Name)", got)
	}
}

func TestFormulaExprV1BundleSupportsOptionalTrailingArguments(t *testing.T) {
	bundle, err := ParseFunctionBundlePayload([]byte(`{
		"functions": [
			{
				"name": "HYPERLINK",
				"minArgs": 2,
				"maxArgs": 3,
				"return": { "kind": "fixed", "type": "string" },
				"evaluator": {
					"kind": "formula_expr_v1",
					"params": ["url", "friendlyName"],
					"body": "friendlyName"
				}
			}
		]
	}`))
	if err != nil {
		t.Fatalf("ParseFunctionBundlePayload HYPERLINK: %v", err)
	}

	engine := NewEngine(WithoutRuntimeFunctionBundles())
	if err := engine.RegisterFunctionBundle(bundle); err != nil {
		t.Fatalf("RegisterFunctionBundle HYPERLINK: %v", err)
	}
	for _, expr := range []string{
		`HYPERLINK("https://example.com", "Example")`,
		`HYPERLINK("https://example.com", "Example", "_blank")`,
	} {
		t.Run(expr, func(t *testing.T) {
			if err := engine.ValidateSyntax(expr); err != nil {
				t.Fatalf("ValidateSyntax(%s): %v", expr, err)
			}
			got, err := engine.Evaluate(expr, &Context{})
			if err != nil {
				t.Fatalf("Evaluate(%s): %v", expr, err)
			}
			if got != "Example" {
				t.Fatalf("Evaluate(%s) = %v, want Example", expr, got)
			}
		})
	}
	if err := engine.ValidateSyntax(`HYPERLINK("https://example.com", "Example", "_blank", "extra")`); err == nil {
		t.Fatal("ValidateSyntax HYPERLINK with 4 args = nil; want max-arity rejection")
	}
}

func TestDefaultEngineDoesNotInheritSalesforcePicklistBundle(t *testing.T) {
	engine := NewEngine(WithoutRuntimeFunctionBundles())

	if err := engine.ValidateSyntax(`ISPICKVAL(Status, "Active")`); err == nil || !strings.Contains(err.Error(), "ISPICKVAL") {
		t.Fatalf("ValidateSyntax ISPICKVAL without active bundle = %v, want unknown function", err)
	}
}

func TestDefaultEngineDoesNotInheritSalesforceHyperlinkBundle(t *testing.T) {
	engine := NewEngine(WithoutRuntimeFunctionBundles())

	if err := engine.ValidateSyntax(`HYPERLINK("https://example.com", "Example")`); err == nil || !strings.Contains(err.Error(), "HYPERLINK") {
		t.Fatalf("ValidateSyntax HYPERLINK without active bundle = %v, want unknown function", err)
	}
}

func TestDefaultFormulaRegistryDoesNotOwnIncludes(t *testing.T) {
	if _, ok := spec.DefaultRegistry().Lookup("INCLUDES"); ok {
		t.Fatal("spec.DefaultRegistry().Lookup(INCLUDES) = true; want CRM L1 bundle ownership")
	}
}

func TestIncludesBundleDelegatesToBuiltinMultipicklistEvaluator(t *testing.T) {
	engine := NewEngine(WithoutRuntimeFunctionBundles())
	if err := engine.ValidateSyntax(`INCLUDES(Field, "Others")`); err == nil || !strings.Contains(err.Error(), "INCLUDES") {
		t.Fatalf("ValidateSyntax INCLUDES without active bundle = %v, want unknown function", err)
	}
	if err := engine.RegisterFunctionBundle(testIncludesFunctionBundle(t)); err != nil {
		t.Fatalf("RegisterFunctionBundle INCLUDES: %v", err)
	}
	if err := engine.ValidateSyntax(`INCLUDES(Field, "Others")`); err != nil {
		t.Fatalf("ValidateSyntax INCLUDES with bundle = %v", err)
	}

	got, err := engine.Evaluate(`INCLUDES(Field, "Others")`, &Context{
		Record: map[string]interface{}{"Field": `["Others"]`},
		Schema: &models.ObjectMetadata{Fields: []models.FieldMetadata{{
			APIName: "Field",
			Type:    models.FieldType(constants.FieldTypeMultiPicklist),
			Options: models.PicklistOptions{{Value: "Others", Label: "Others"}},
		}}},
	})
	if err != nil {
		t.Fatalf("Evaluate INCLUDES with bundle: %v", err)
	}
	if got != true {
		t.Fatalf(`Evaluate INCLUDES with bundle = %v, want true for schema-aware storage envelope`, got)
	}
}

func TestRegisterFunctionBundleRejectsSpecWithoutEvaluator(t *testing.T) {
	bundle := FunctionBundle{Functions: []FunctionDefinition{{
		Name:    "PACK_PREFIX",
		MinArgs: 1,
		MaxArgs: 1,
		Return:  FunctionReturnDefinition{Kind: "fixed", Type: spec.TypeString},
		Args:    []spec.ArgSpec{{Type: spec.TypeString, Nullable: true}},
	}}}

	engine := NewEngine()
	err := engine.RegisterFunctionBundle(bundle)
	if err == nil || !strings.Contains(err.Error(), "evaluator") {
		t.Fatalf("RegisterFunctionBundle error = %v, want evaluator consistency rejection", err)
	}
	if err := engine.ValidateSyntax(`PACK_PREFIX(Name)`); err == nil || !strings.Contains(err.Error(), "PACK_PREFIX") {
		t.Fatalf("ValidateSyntax after rejected bundle = %v, want external function to remain unavailable", err)
	}
}

func TestValidateFunctionBundlesReportsRegisterContext(t *testing.T) {
	bundle := testFormulaFunctionBundle(t)
	bundle.Functions = append(bundle.Functions, bundle.Functions[0])

	err := ValidateFunctionBundles([]FunctionBundle{bundle})
	if err == nil {
		t.Fatal("ValidateFunctionBundles duplicate function = nil, want error")
	}
	for _, want := range []string{"formula function bundle[0].functions[1]", `register "PACK_PREFIX"`, "duplicate registration"} {
		if !strings.Contains(err.Error(), want) {
			t.Fatalf("ValidateFunctionBundles duplicate error = %q, want substring %q", err.Error(), want)
		}
	}
}

func TestParseFunctionBundleRejectsUnknownPredicate(t *testing.T) {
	_, err := ParseFunctionBundlePayload([]byte(`{
		"functions": [
			{
				"name": "PACK_PREFIX",
				"minArgs": 1,
				"maxArgs": 1,
				"predicate": "vendor_shape",
				"return": { "kind": "fixed", "type": "string" },
				"args": [{ "type": "string", "nullable": true }],
				"evaluator": {
					"kind": "formula_expr_v1",
					"params": ["value"],
					"body": "value + \"-probe\""
				}
			}
		]
	}`))
	if err == nil || !strings.Contains(err.Error(), "predicate") {
		t.Fatalf("ParseFunctionBundlePayload unknown predicate error = %v, want predicate rejection", err)
	}
}

func TestNewEngineSkipsRuntimeFunctionBundleOnRegistrationConflict(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles before test: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})

	bundle := testFormulaFunctionBundle(t)
	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{bundle}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles: %v", err)
	}
	registry := spec.DefaultRegistry().Clone()
	if err := registry.Register(bundle.Functions[0].Spec()); err != nil {
		t.Fatalf("register conflicting external function spec: %v", err)
	}

	var engine *Engine
	func() {
		defer func() {
			if recovered := recover(); recovered != nil {
				t.Fatalf("NewEngine panicked on runtime bundle conflict: %v", recovered)
			}
		}()
		engine = NewEngine(WithFunctionRegistry(registry))
	}()
	if engine == nil {
		t.Fatal("NewEngine returned nil")
	}

	if err := engine.ValidateSyntax(`PACK_PREFIX(Name)`); err != nil {
		t.Fatalf("ValidateSyntax with caller registry = %v; want nil", err)
	}
	got, err := engine.Evaluate(`PACK_PREFIX(Name)`, &Context{Record: map[string]interface{}{"Name": "alpha"}})
	if err == nil {
		t.Fatalf("Evaluate after skipped runtime bundle = %v, nil; want missing evaluator error", got)
	}
	if message := strings.ToLower(err.Error()); !strings.Contains(message, "unknown") || !strings.Contains(err.Error(), "PACK_PREFIX") {
		t.Fatalf("Evaluate after skipped runtime bundle error = %q, want unknown PACK_PREFIX", err.Error())
	}
}

func TestRegistryOnlyExternalFunctionCannotEvaluateWithoutBundle(t *testing.T) {
	registry := spec.DefaultRegistry().Clone()
	if err := registry.Register(testFormulaFunctionBundle(t).Functions[0].Spec()); err != nil {
		t.Fatalf("register external function spec: %v", err)
	}
	engine := NewEngine(WithFunctionRegistry(registry))

	if err := engine.ValidateSyntax(`PACK_PREFIX(Name)`); err != nil {
		t.Fatalf("ValidateSyntax registry-only external function = %v; want nil", err)
	}
	got, err := engine.Evaluate(`PACK_PREFIX(Name)`, &Context{Record: map[string]interface{}{"Name": "alpha"}})
	if err == nil {
		t.Fatalf("Evaluate registry-only external function = %v, nil; want missing evaluator error", got)
	}
}

func TestExecutableFunctionRegistryWithBundlesRetainsBundleDefinition(t *testing.T) {
	registry, err := ExecutableFunctionRegistryWithBundles([]FunctionBundle{testFormulaFunctionBundle(t)})
	if err != nil {
		t.Fatalf("ExecutableFunctionRegistryWithBundles: %v", err)
	}

	if _, ok := registry.SpecRegistry().Lookup("PACK_PREFIX"); !ok {
		t.Fatal("SpecRegistry().Lookup(PACK_PREFIX) = false, want registered spec")
	}
	def, ok := registry.LookupFunctionDefinition("PACK_PREFIX")
	if !ok {
		t.Fatal("LookupFunctionDefinition(PACK_PREFIX) = false, want bundle definition")
	}
	if def.Evaluator.Kind != FormulaExprV1EvaluatorKind || def.Evaluator.Body != `value + "-probe"` {
		t.Fatalf("LookupFunctionDefinition(PACK_PREFIX) evaluator = %#v, want formula_expr_v1 body", def.Evaluator)
	}

	def.Evaluator.Body = "mutated"
	defAgain, ok := registry.LookupFunctionDefinition("PACK_PREFIX")
	if !ok {
		t.Fatal("LookupFunctionDefinition(PACK_PREFIX) after mutation = false")
	}
	if defAgain.Evaluator.Body != `value + "-probe"` {
		t.Fatalf("LookupFunctionDefinition returned mutable definition; got body %q", defAgain.Evaluator.Body)
	}
}

func TestRuntimeFunctionBundlesDriveNewEnginesAndClearFailClosed(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles before test: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})

	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{testFormulaFunctionBundle(t)}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles: %v", err)
	}
	engine := NewEngine()
	got, err := engine.Evaluate(`PACK_PREFIX(Name)`, &Context{Record: map[string]interface{}{"Name": "alpha"}})
	if err != nil {
		t.Fatalf("Evaluate with active runtime bundle: %v", err)
	}
	if got != "alpha-probe" {
		t.Fatalf("Evaluate with active runtime bundle = %v, want alpha-probe", got)
	}

	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles: %v", err)
	}
	engine = NewEngine()
	if err := engine.ValidateSyntax(`PACK_PREFIX(Name)`); err == nil || !strings.Contains(err.Error(), "PACK_PREFIX") {
		t.Fatalf("ValidateSyntax after runtime bundle removal = %v, want fail closed", err)
	}
}

func TestToSQLRequiresRuntimeBundleForPackOwnedFunction(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles: %v", err)
	}

	_, _, err := ToSQL(`ISPICKVAL(Status__c, "Closed")`)
	if err == nil || !strings.Contains(err.Error(), "ISPICKVAL") {
		t.Fatalf("ToSQL ISPICKVAL without runtime bundle = %v, want unsupported function", err)
	}
}

func TestToSQLUsesRuntimeBundleExecutableLowering(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})
	bundle := testFormulaFunctionBundleWithPayload(t, []byte(`{
		"functions": [
			{
				"name": "ISPICKVAL",
				"minArgs": 2,
				"maxArgs": 2,
				"return": { "kind": "fixed", "type": "boolean" },
				"args": [
					{ "type": "any", "nullable": true },
					{ "type": "string", "nullable": true }
				],
				"evaluator": {
					"kind": "formula_expr_v1",
					"params": ["value", "expected"],
					"body": "value == expected"
				}
			}
		]
	}`))
	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{bundle}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles: %v", err)
	}

	sql, args, err := ToSQLWithMapper(`ISPICKVAL(Status__c, "Closed")`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err != nil {
		t.Fatalf("ToSQLWithMapper ISPICKVAL with runtime bundle: %v", err)
	}
	if sql != "(row.`Status__c` = ?)" {
		t.Fatalf("sql = %q, want bundle-lowered ISPICKVAL comparison", sql)
	}
	if len(args) != 1 || args[0] != "Closed" {
		t.Fatalf("args = %#v, want [Closed]", args)
	}
}

func TestToSQLWithMapperINCLUDESUsesCompatibleMultiPicklistPredicateForSelfDelegatingBundle(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})

	_, _, err := ToSQLWithMapper(`INCLUDES(Regions__c, "North")`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err == nil || !strings.Contains(err.Error(), "INCLUDES") {
		t.Fatalf("ToSQLWithMapper without INCLUDES bundle = %v, want registration error", err)
	}

	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{testIncludesFunctionBundle(t)}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles INCLUDES: %v", err)
	}

	sql, args, err := ToSQLWithMapper(`INCLUDES(Regions__c, "North")`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err != nil {
		t.Fatalf("ToSQLWithMapper INCLUDES with runtime bundle: %v", err)
	}
	wantSQL := utils.MultiPicklistJSONContainsSQL("row.`Regions__c`", "?", true)
	if sql != wantSQL {
		t.Fatalf("sql = %q, want %q", sql, wantSQL)
	}
	if len(args) != 1 || args[0] != "North" {
		t.Fatalf("args = %#v, want [North]", args)
	}

	sql, args, err = ToSQLWithMapper(`INCLUDES(Regions__c, "North;East")`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err != nil {
		t.Fatalf("ToSQLWithMapper multi-token INCLUDES with runtime bundle: %v", err)
	}
	wantSQL = "(" + utils.MultiPicklistJSONContainsSQL("row.`Regions__c`", "?", true) + " AND " + utils.MultiPicklistJSONContainsSQL("row.`Regions__c`", "?", true) + ")"
	if sql != wantSQL {
		t.Fatalf("multi-token sql = %q, want %q", sql, wantSQL)
	}
	if len(args) != 2 || args[0] != "North" || args[1] != "East" {
		t.Fatalf("multi-token args = %#v, want [North East]", args)
	}

	sql, args, err = ToSQLWithMapper(`INCLUDES(Regions__c, WantedTag__c)`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err != nil {
		t.Fatalf("ToSQLWithMapper dynamic INCLUDES with runtime bundle: %v", err)
	}
	wantSQL = utils.MultiPicklistJSONContainsSQL("row.`Regions__c`", "row.`WantedTag__c`", true)
	if sql != wantSQL {
		t.Fatalf("dynamic rhs sql = %q, want %q", sql, wantSQL)
	}
	if len(args) != 0 {
		t.Fatalf("dynamic rhs args = %#v, want []", args)
	}
}

func TestToSQLWithOptionsKeepsRuntimeLowerersWhenMemberPathsAreOptedIn(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})
	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{testIncludesFunctionBundle(t)}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles INCLUDES: %v", err)
	}

	sql, args, err := ToSQLWithOptions(`INCLUDES(Account.Regions__c, "North")`, SQLOptions{
		AllowMemberPaths: true,
		Mapper: func(identifier string) string {
			if identifier != "Account.Regions__c" {
				t.Fatalf("mapper identifier = %q, want relationship member path", identifier)
			}
			return "`j_account`.`Regions__c`"
		},
	})
	if err != nil {
		t.Fatalf("ToSQLWithOptions INCLUDES with runtime bundle and member path: %v", err)
	}
	wantSQL := utils.MultiPicklistJSONContainsSQL("`j_account`.`Regions__c`", "?", true)
	if sql != wantSQL {
		t.Fatalf("sql = %q, want %q", sql, wantSQL)
	}
	if len(args) != 1 || args[0] != "North" {
		t.Fatalf("args = %#v, want [North]", args)
	}
}

func TestToSQLBundleLoweringSupportsOptionalTrailingArguments(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})
	bundle := testFormulaFunctionBundleWithPayload(t, []byte(`{
		"functions": [
			{
				"name": "HYPERLINK",
				"minArgs": 2,
				"maxArgs": 3,
				"return": { "kind": "fixed", "type": "string" },
				"evaluator": {
					"kind": "formula_expr_v1",
					"params": ["url", "friendlyName"],
					"body": "friendlyName"
				}
			}
		]
	}`))
	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{bundle}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles: %v", err)
	}

	sql, args, err := ToSQLWithMapper(`HYPERLINK(Link__c, Label__c, "_blank") == "Shown"`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err != nil {
		t.Fatalf("ToSQLWithMapper HYPERLINK with optional target: %v", err)
	}
	if sql != "(row.`Label__c` = ?)" {
		t.Fatalf("sql = %q, want bundle-lowered HYPERLINK label comparison", sql)
	}
	if len(args) != 1 || args[0] != "Shown" {
		t.Fatalf("args = %#v, want [Shown]", args)
	}
}

func TestNewEngineWithoutRuntimeFunctionBundlesAllowsExplicitBundleRegistration(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles before test: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})

	bundle := testFormulaFunctionBundle(t)
	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{bundle}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles: %v", err)
	}
	engine := NewEngine(WithoutRuntimeFunctionBundles())
	if err := engine.ValidateSyntax(`PACK_PREFIX(Name)`); err == nil || !strings.Contains(err.Error(), "PACK_PREFIX") {
		t.Fatalf("ValidateSyntax before explicit registration = %v, want missing PACK_PREFIX", err)
	}
	if err := engine.RegisterFunctionBundles([]FunctionBundle{bundle}); err != nil {
		t.Fatalf("RegisterFunctionBundles after runtime bundle already active globally: %v", err)
	}
	got, err := engine.Evaluate(`PACK_PREFIX(Name)`, &Context{Record: map[string]interface{}{"Name": "alpha"}})
	if err != nil {
		t.Fatalf("Evaluate explicit bundle on runtime-blind engine: %v", err)
	}
	if got != "alpha-probe" {
		t.Fatalf("Evaluate explicit bundle on runtime-blind engine = %v, want alpha-probe", got)
	}
}

// TestCASESAFEIDSQLWalkerFailsClosed verifies that when CASESAFEID is lowered
// via the expression SQL walker path (formula.ToSQLWithMapper / lowerFormulaExprV1ExpressionSQL),
// the & concatenation operator in the bundle body causes a fail-closed error
// rather than silently emitting numeric SQL +. Expr-lang's parser does not
// accept &, so ToSQLWithOptions returns a parse error. This is the safe
// behavior until the expression SQL walker learns string-concat semantics.
func TestCASESAFEIDSQLWalkerFailsClosed(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})

	bundle := testFormulaFunctionBundleWithPayload(t, []byte(`{
		"functions": [
			{
				"name": "CASESAFEID",
				"minArgs": 1,
				"maxArgs": 1,
				"return": { "kind": "fixed", "type": "string" },
				"args": [
					{ "type": "string", "nullable": true }
				],
				"evaluator": {
					"kind": "formula_expr_v1",
					"params": ["arg0"],
					"body": "IF(ISBLANK(arg0), arg0, IF(LEN(arg0) == 18, arg0, IF(LEN(arg0) == 15, arg0 & MID(\"ABC\", 1, 1), arg0)))"
				}
			}
		]
	}`))
	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{bundle}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles: %v", err)
	}

	_, _, err := ToSQLWithMapper(`CASESAFEID(AccountId__c)`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err == nil {
		t.Fatal("ToSQLWithMapper CASESAFEID error = nil; want fail-closed on & operator")
	}
	// The error should mention the bundle name so the caller can diagnose.
	if !strings.Contains(err.Error(), "CASESAFEID") {
		t.Fatalf("ToSQLWithMapper CASESAFEID error = %q; want it to mention CASESAFEID", err.Error())
	}
}

// TestToSQLWithMapperIMAGEUsesArg0Passthrough verifies the public
// query-filter path lowers IMAGE through its formula_expr_v1 runtime
// bundle. IMAGE is a display-only function whose body is arg0
// (passthrough of the image URL). The filter must ignore optional
// alt-text/height/width args and substitute the first mapped argument.
// Without the bundle, ToSQLWithMapper must fail closed with an IMAGE
// registration error.
func TestToSQLWithMapperIMAGEUsesArg0Passthrough(t *testing.T) {
	if err := RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime bundles: %v", err)
	}
	t.Cleanup(func() {
		_ = RegisterRuntimeFunctionBundles(nil)
	})

	// Without bundle: fail closed.
	_, _, err := ToSQLWithMapper(`IMAGE(URL__c, "alt")`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err == nil || !strings.Contains(err.Error(), "IMAGE") {
		t.Fatalf("ToSQLWithMapper without IMAGE bundle: err=%v, want IMAGE registration error", err)
	}

	bundle := testFormulaFunctionBundleWithPayload(t, []byte(`{
		"functions": [
			{
				"name": "IMAGE",
				"minArgs": 2,
				"maxArgs": 4,
				"return": { "kind": "fixed", "type": "string" },
				"args": [{ "type": "string", "nullable": true }],
				"evaluator": {
					"kind": "formula_expr_v1",
					"params": ["arg0", "arg1"],
					"body": "arg0"
				}
			}
		]
	}`))
	if err := RegisterRuntimeFunctionBundles([]FunctionBundle{bundle}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles: %v", err)
	}

	sql, args, err := ToSQLWithMapper(`IMAGE(URL__c, "alt") == "x"`, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err != nil {
		t.Fatalf("ToSQLWithMapper IMAGE with runtime bundle: %v", err)
	}
	if sql != "(row.`URL__c` = ?)" {
		t.Fatalf("sql = %q, want (row.`URL__c` = ?)", sql)
	}
	if len(args) != 1 || args[0] != "x" {
		t.Fatalf("args = %#v, want [x]", args)
	}

	// Optional alt-text, height, and width args must be ignored on the
	// public filter path — only the first mapped argument (URL) survives.
	for _, tc := range []struct {
		name string
		expr string
		want string
	}{
		{
			name: "four args",
			expr: `IMAGE(URL__c, "alt", 80, 80) == "x"`,
			want: "(row.`URL__c` = ?)",
		},
		{
			name: "mixed case",
			expr: `image(URL__c, "alt") == "x"`,
			want: "(row.`URL__c` = ?)",
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			sql, args, err := ToSQLWithMapper(tc.expr, func(identifier string) string {
				return "row.`" + identifier + "`"
			})
			if err != nil {
				t.Fatalf("ToSQLWithMapper IMAGE with optional args: %v", err)
			}
			if sql != tc.want {
				t.Fatalf("sql = %q, want %q", sql, tc.want)
			}
			if len(args) != 1 || args[0] != "x" {
				t.Fatalf("args = %#v, want [x]", args)
			}
		})
	}
}
