package expression

import "testing"

// TestLPAD_RuntimeEvaluation verifies LPAD evaluates correctly in the frozen
// formula runtime (pkg/expression engine). This test is expected to FAIL until
// LPAD is registered in the expression engine.
func TestLPAD_RuntimeEvaluation(t *testing.T) {
	engine := NewEngine()

	cases := []struct {
		name string
		expr string
		env  map[string]interface{}
		want string
	}{
		{
			name: "pads shorter string on left",
			expr: `LPAD("hello", 10, "0")`,
			env:  map[string]interface{}{},
			want: "00000hello",
		},
		{
			name: "string already at target length is returned as-is",
			expr: `LPAD("hello", 5, "0")`,
			env:  map[string]interface{}{},
			want: "hello",
		},
		{
			// Salesforce LPAD truncates from the right: returns leftmost n chars
			name: "string longer than target is truncated from right side",
			expr: `LPAD("hello world", 5, "0")`,
			env:  map[string]interface{}{},
			want: "hello",
		},
		{
			name: "field reference",
			expr: `LPAD(Name, 8, "0")`,
			env:  map[string]interface{}{"Name": "abc"},
			want: "00000abc",
		},
		{
			name: "zero target length returns empty string",
			expr: `LPAD("hello", 0, "0")`,
			env:  map[string]interface{}{},
			want: "",
		},
		{
			name: "nil text treated as empty string",
			expr: `LPAD(NilField, 3, "0")`,
			env:  map[string]interface{}{"NilField": nil},
			want: "000",
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got, err := engine.Evaluate(tc.expr, tc.env)
			if err != nil {
				t.Fatalf("Evaluate(%q) error = %v; want nil", tc.expr, err)
			}
			if got != tc.want {
				t.Fatalf("Evaluate(%q) = %q; want %q", tc.expr, got, tc.want)
			}
		})
	}
}

// TestLPAD_EmptyPadReturnsNil verifies that LPAD with empty pad string returns
// nil (consistent with Salesforce which requires a non-empty pad_string).
func TestLPAD_EmptyPadReturnsNil(t *testing.T) {
	engine := NewEngine()
	got, err := engine.Evaluate(`LPAD("hello", 10, "")`, map[string]interface{}{})
	if err != nil {
		t.Fatalf("Evaluate LPAD empty pad: unexpected error %v", err)
	}
	if got != nil {
		t.Fatalf("Evaluate LPAD empty pad = %v; want nil", got)
	}
}

// TestRPAD_EmptyPadReturnsNil verifies that RPAD with empty pad string returns
// nil (consistent with Salesforce which requires a non-empty pad_string).
func TestRPAD_EmptyPadReturnsNil(t *testing.T) {
	engine := NewEngine()
	got, err := engine.Evaluate(`RPAD("hello", 10, "")`, map[string]interface{}{})
	if err != nil {
		t.Fatalf("Evaluate RPAD empty pad: unexpected error %v", err)
	}
	if got != nil {
		t.Fatalf("Evaluate RPAD empty pad = %v; want nil", got)
	}
}

// TestRPAD_RuntimeEvaluation verifies RPAD evaluates correctly in the frozen
// formula runtime (pkg/expression engine). This test is expected to FAIL until
// RPAD is registered in the expression engine.
func TestRPAD_RuntimeEvaluation(t *testing.T) {
	engine := NewEngine()

	cases := []struct {
		name string
		expr string
		env  map[string]interface{}
		want string
	}{
		{
			name: "pads shorter string on right",
			expr: `RPAD("hello", 10, "0")`,
			env:  map[string]interface{}{},
			want: "hello00000",
		},
		{
			name: "string already at target length is returned as-is",
			expr: `RPAD("hello", 5, "0")`,
			env:  map[string]interface{}{},
			want: "hello",
		},
		{
			name: "string longer than target is truncated from left",
			expr: `RPAD("hello world", 5, "0")`,
			env:  map[string]interface{}{},
			want: "hello",
		},
		{
			name: "field reference",
			expr: `RPAD(Name, 8, " ")`,
			env:  map[string]interface{}{"Name": "abc"},
			want: "abc     ",
		},
		{
			name: "zero target length returns empty string",
			expr: `RPAD("hello", 0, "0")`,
			env:  map[string]interface{}{},
			want: "",
		},
		{
			name: "nil text treated as empty string",
			expr: `RPAD(NilField, 3, "0")`,
			env:  map[string]interface{}{"NilField": nil},
			want: "000",
		},
	}

	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			got, err := engine.Evaluate(tc.expr, tc.env)
			if err != nil {
				t.Fatalf("Evaluate(%q) error = %v; want nil", tc.expr, err)
			}
			if got != tc.want {
				t.Fatalf("Evaluate(%q) = %q; want %q", tc.expr, got, tc.want)
			}
		})
	}
}

// TestLPAD_NegativeLenReturnsEmpty verifies that a negative len argument returns
// "" (not nil), matching the SQL walker IF guard that emits '' for len <= 0.
func TestLPAD_NegativeLenReturnsEmpty(t *testing.T) {
	engine := NewEngine()
	got, err := engine.Evaluate(`LPAD("hello", -3, "0")`, map[string]interface{}{})
	if err != nil {
		t.Fatalf("Evaluate LPAD negative len: unexpected error %v", err)
	}
	if got != "" {
		t.Fatalf("Evaluate LPAD negative len = %v; want \"\"", got)
	}
}

// TestRPAD_NegativeLenReturnsEmpty verifies that a negative len argument returns
// "" (not nil), matching the SQL walker IF guard that emits '' for len <= 0.
func TestRPAD_NegativeLenReturnsEmpty(t *testing.T) {
	engine := NewEngine()
	got, err := engine.Evaluate(`RPAD("hello", -3, "0")`, map[string]interface{}{})
	if err != nil {
		t.Fatalf("Evaluate RPAD negative len: unexpected error %v", err)
	}
	if got != "" {
		t.Fatalf("Evaluate RPAD negative len = %v; want \"\"", got)
	}
}

// TestLPAD_NilLenReturnsNil verifies that a nil len argument returns nil (NULL),
// consistent with SQL emitting IS NULL guard for the len parameter.
func TestLPAD_NilLenReturnsNil(t *testing.T) {
	engine := NewEngine()
	got, err := engine.Evaluate(`LPAD("hello", NullableLen, "0")`, map[string]interface{}{"NullableLen": nil})
	if err != nil {
		t.Fatalf("Evaluate LPAD nil len: unexpected error %v", err)
	}
	if got != nil {
		t.Fatalf("Evaluate LPAD nil len = %v; want nil", got)
	}
}

// TestLPAD_NilPadstrReturnsNil verifies that a nil padstr argument returns nil (NULL),
// consistent with SQL emitting IS NULL guard for the padstr parameter.
func TestLPAD_NilPadstrReturnsNil(t *testing.T) {
	engine := NewEngine()
	got, err := engine.Evaluate(`LPAD("hello", 10, NullablePad)`, map[string]interface{}{"NullablePad": nil})
	if err != nil {
		t.Fatalf("Evaluate LPAD nil padstr: unexpected error %v", err)
	}
	if got != nil {
		t.Fatalf("Evaluate LPAD nil padstr = %v; want nil", got)
	}
}

// TestRPAD_NilLenReturnsNil verifies that a nil len argument returns nil (NULL),
// consistent with SQL emitting IS NULL guard for the len parameter.
func TestRPAD_NilLenReturnsNil(t *testing.T) {
	engine := NewEngine()
	got, err := engine.Evaluate(`RPAD("hello", NullableLen, "0")`, map[string]interface{}{"NullableLen": nil})
	if err != nil {
		t.Fatalf("Evaluate RPAD nil len: unexpected error %v", err)
	}
	if got != nil {
		t.Fatalf("Evaluate RPAD nil len = %v; want nil", got)
	}
}

// TestRPAD_NilPadstrReturnsNil verifies that a nil padstr argument returns nil (NULL),
// consistent with SQL emitting IS NULL guard for the padstr parameter.
func TestRPAD_NilPadstrReturnsNil(t *testing.T) {
	engine := NewEngine()
	got, err := engine.Evaluate(`RPAD("hello", 10, NullablePad)`, map[string]interface{}{"NullablePad": nil})
	if err != nil {
		t.Fatalf("Evaluate RPAD nil padstr: unexpected error %v", err)
	}
	if got != nil {
		t.Fatalf("Evaluate RPAD nil padstr = %v; want nil", got)
	}
}
