package automation

import (
	"context"
	"reflect"
	"testing"

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

func TestINCLUDESMultiTokenRHSConformanceFormulaSurfaces(t *testing.T) {
	const (
		objectAPIName = "Probe__c"
		fieldAPIName  = "Tags__c"
	)

	bundle := includesConformanceFunctionBundle(t)
	schema := &models.ObjectMetadata{
		APIName: objectAPIName,
		Fields: []models.FieldMetadata{
			{APIName: fieldAPIName, Type: models.FieldType(constants.FieldTypeMultiPicklist)},
		},
	}
	formulaExpr := `INCLUDES(Tags__c, "A;B")`
	flowFilterFormula := `INCLUDES({!$Record.Tags__c}, "A;B")`

	engine := formula.NewEngine()
	if err := engine.RegisterFunctionBundle(bundle); err != nil {
		t.Fatalf("RegisterFunctionBundle: %v", err)
	}
	exec := &FlowExecutor{
		formulaEngine: formula.NewEngine(),
		objectSchemaLookup: func(_ context.Context, objectName string) *models.ObjectMetadata {
			if objectName == objectAPIName {
				return schema
			}
			return nil
		},
	}
	if err := exec.formulaEngine.RegisterFunctionBundle(bundle); err != nil {
		t.Fatalf("RegisterFunctionBundle on Flow executor: %v", err)
	}

	for _, tc := range []struct {
		name   string
		record map[string]interface{}
		want   bool
	}{
		{
			name:   "all authored RHS tokens selected",
			record: map[string]interface{}{fieldAPIName: "B;A"},
			want:   true,
		},
		{
			name:   "one authored RHS token missing",
			record: map[string]interface{}{fieldAPIName: "A"},
			want:   false,
		},
	} {
		t.Run(tc.name, func(t *testing.T) {
			gotFormula, err := engine.Evaluate(formulaExpr, &formula.Context{
				Record: tc.record,
				Schema: schema,
			})
			if err != nil {
				t.Fatalf("formula runtime Evaluate: %v", err)
			}
			if gotFormula != tc.want {
				t.Fatalf("formula runtime INCLUDES = %v, want %v", gotFormula, tc.want)
			}

			gotFlow := exec.checkEntryConditionsCtx(
				context.Background(),
				&models.FlowMetadata{Start: &models.FlowStart{
					Object:        objectAPIName,
					TriggerType:   "RecordBeforeSave",
					FilterFormula: flowFilterFormula,
				}},
				map[string]interface{}{"$Record": models.SObject(tc.record)},
				nil,
			)
			if gotFlow != tc.want {
				t.Fatalf("Flow filterFormula INCLUDES = %v, want %v", gotFlow, tc.want)
			}
		})
	}

	if err := formula.RegisterRuntimeFunctionBundles(nil); err != nil {
		t.Fatalf("clear runtime formula bundles: %v", err)
	}
	t.Cleanup(func() {
		_ = formula.RegisterRuntimeFunctionBundles(nil)
	})
	if err := formula.RegisterRuntimeFunctionBundles([]formula.FunctionBundle{bundle}); err != nil {
		t.Fatalf("RegisterRuntimeFunctionBundles: %v", err)
	}

	sql, args, err := formula.ToSQLWithMapper(formulaExpr, func(identifier string) string {
		return "row.`" + identifier + "`"
	})
	if err != nil {
		t.Fatalf("formula SQL lowering: %v", err)
	}
	wantFormulaSQL := "(" + utils.MultiPicklistJSONContainsSQL("row.`Tags__c`", "?", true) + " AND " + utils.MultiPicklistJSONContainsSQL("row.`Tags__c`", "?", true) + ")"
	if sql != wantFormulaSQL {
		t.Fatalf("formula SQL = %q, want %q", sql, wantFormulaSQL)
	}
	if !reflect.DeepEqual(args, []interface{}{"A", "B"}) {
		t.Fatalf("formula SQL args = %#v, want %#v", args, []interface{}{"A", "B"})
	}

	ast, err := analytics.Parse(formulaExpr)
	if err != nil {
		t.Fatalf("analytics Parse: %v", err)
	}
	executables, err := formula.ExecutableFunctionRegistryWithBundles([]formula.FunctionBundle{bundle})
	if err != nil {
		t.Fatalf("ExecutableFunctionRegistryWithBundles: %v", err)
	}
	analyticsSQL, err := analytics.Compile(ast, &analytics.CompileContext{
		TableAlias:          "t",
		QualifyFields:       true,
		FunctionExecutables: executables,
	})
	if err != nil {
		t.Fatalf("analytics SQL lowering: %v", err)
	}
	wantAnalyticsSQL := "(" + utils.MultiPicklistJSONContainsSQL("t.`Tags__c`", "'A'", true) + " AND " + utils.MultiPicklistJSONContainsSQL("t.`Tags__c`", "'B'", true) + ")"
	if analyticsSQL != wantAnalyticsSQL {
		t.Fatalf("analytics SQL = %q, want %q", analyticsSQL, wantAnalyticsSQL)
	}
}

func includesConformanceFunctionBundle(t *testing.T) formula.FunctionBundle {
	t.Helper()
	bundle, err := formula.ParseFunctionBundlePayload([]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)"
				}
			}
		]
	}`))
	if err != nil {
		t.Fatalf("ParseFunctionBundlePayload: %v", err)
	}
	return bundle
}
