package formula

// isblank_whitespace_test.go — parity and correctness tests for ISBLANK / ISBLANKX
// whitespace-only string semantics (Issue #466).
//
// Salesforce v63 oracle: ISBLANK treats only nil and "" as blank; whitespace-only
// strings ("   ") are NOT blank. Numbers (0) and booleans are never blank.

import (
	"testing"
)

// TestISBLANKWhitespaceFormula verifies that the formula engine's ISBLANK
// function follows Salesforce v63 semantics: whitespace-only strings are NOT blank.
func TestISBLANKWhitespaceFormula(t *testing.T) {
	engine := NewEngine()

	tests := []struct {
		name string
		expr string
		want bool
	}{
		// Salesforce oracle: ISBLANK("") → true
		{name: "empty_string", expr: `ISBLANK("")`, want: true},
		// Salesforce v63 parity: whitespace-only is NOT blank ("a field containing a blank space is not empty").
		{name: "spaces_only", expr: `ISBLANK("   ")`, want: false},
		{name: "tab_only", expr: `ISBLANK("\t")`, want: false},
		// Non-blank text is not blank.
		{name: "non_blank_text", expr: `ISBLANK("hello")`, want: false},
		{name: "text_with_spaces", expr: `ISBLANK(" hello ")`, want: false},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got, err := engine.Evaluate(tc.expr, &Context{})
			if err != nil {
				t.Fatalf("Evaluate(%q) error = %v", tc.expr, err)
			}
			gotBool, ok := got.(bool)
			if !ok {
				t.Fatalf("Evaluate(%q) returned %T (%v), want bool", tc.expr, got, got)
			}
			if gotBool != tc.want {
				t.Errorf("Evaluate(%q) = %v, want %v", tc.expr, gotBool, tc.want)
			}
		})
	}
}

// TestISBLANKXWhitespaceFormula verifies that the ISBLANKX extension function
// matches ISBLANK semantics: whitespace-only strings are NOT blank.
func TestISBLANKXWhitespaceFormula(t *testing.T) {
	engine := NewEngine()

	tests := []struct {
		name string
		expr string
		want bool
	}{
		// wasNull=false, value is whitespace-only → not blank (Salesforce v63 parity)
		{name: "spaces_wasNull_false", expr: `ISBLANKX("   ", false)`, want: false},
		// wasNull=false, value is empty → blank
		{name: "empty_wasNull_false", expr: `ISBLANKX("", false)`, want: true},
		// wasNull=true, any value → blank (the was-null flag overrides)
		{name: "spaces_wasNull_true", expr: `ISBLANKX("hello", true)`, want: true},
		// wasNull=false, non-blank text → not blank
		{name: "text_wasNull_false", expr: `ISBLANKX("hello", false)`, want: false},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got, err := engine.Evaluate(tc.expr, &Context{})
			if err != nil {
				t.Fatalf("Evaluate(%q) error = %v", tc.expr, err)
			}
			gotBool, ok := got.(bool)
			if !ok {
				t.Fatalf("Evaluate(%q) returned %T (%v), want bool", tc.expr, got, got)
			}
			if gotBool != tc.want {
				t.Errorf("Evaluate(%q) = %v, want %v", tc.expr, gotBool, tc.want)
			}
		})
	}
}

// TestIsFormulaSafeCompareBlank_WhitespaceIsBlank confirms that the comparison
// helper (already correct before this PR) treats whitespace-only as blank.
// This test pins the already-correct behavior so regressions are caught.
// Note: isFormulaSafeCompareBlank is used in safeFormulaCompare for ordered
// comparisons, not for ISBLANK evaluation; its TrimSpace semantics are intentional.
func TestIsFormulaSafeCompareBlank_WhitespaceIsBlank(t *testing.T) {
	tests := []struct {
		name  string
		input interface{}
		want  bool
	}{
		{name: "spaces", input: "   ", want: true},
		{name: "tab", input: "\t", want: true},
		{name: "empty", input: "", want: true},
		{name: "nil", input: nil, want: true},
		{name: "text", input: "hello", want: false},
		{name: "number", input: 0, want: false},
	}

	for _, tc := range tests {
		t.Run(tc.name, func(t *testing.T) {
			got := isFormulaSafeCompareBlank(tc.input)
			if got != tc.want {
				t.Errorf("isFormulaSafeCompareBlank(%#v) = %v, want %v", tc.input, got, tc.want)
			}
		})
	}
}
