package models

import (
	"encoding/json"
	"strings"
	"testing"
)

func TestFlowRecordUpdateRecheckFiltersAtWriteJSONContract(t *testing.T) {
	var update FlowRecordUpdate
	if err := json.Unmarshal([]byte(`{"name":"Guarded","object":"Example__c","recheckFiltersAtWrite":true}`), &update); err != nil {
		t.Fatalf("json.Unmarshal FlowRecordUpdate: %v", err)
	}
	if !update.RecheckFiltersAtWrite {
		t.Fatal("RecheckFiltersAtWrite = false, want true")
	}

	encoded, err := json.Marshal(update)
	if err != nil {
		t.Fatalf("json.Marshal guarded FlowRecordUpdate: %v", err)
	}
	if !strings.Contains(string(encoded), `"recheckFiltersAtWrite":true`) {
		t.Fatalf("guarded JSON = %s, want recheckFiltersAtWrite=true", encoded)
	}

	ordinary, err := json.Marshal(FlowRecordUpdate{Name: "Ordinary"})
	if err != nil {
		t.Fatalf("json.Marshal ordinary FlowRecordUpdate: %v", err)
	}
	if strings.Contains(string(ordinary), "recheckFiltersAtWrite") {
		t.Fatalf("ordinary JSON = %s, want guarded field omitted", ordinary)
	}
}

func TestValidateGuardedFlowRecordFilterContract(t *testing.T) {
	tests := []struct {
		name         string
		filter       FlowRecordFilter
		wantOperator string
		wantErr      string
	}{
		{
			name:         "equal is supported",
			filter:       FlowRecordFilter{Field: "Id", Operator: "EqualTo", Value: "row-1"},
			wantOperator: "EqualTo",
		},
		{
			name:         "not equal is supported case insensitively",
			filter:       FlowRecordFilter{Field: "Inactive", Operator: "notequalto", Value: true},
			wantOperator: "NotEqualTo",
		},
		{
			name:         "blank is supported",
			filter:       FlowRecordFilter{Field: "RemoteKey", Operator: "IsBlank"},
			wantOperator: "IsBlank",
		},
		{
			name:    "equal nil must use is blank",
			filter:  FlowRecordFilter{Field: "RemoteKey", Operator: "EqualTo"},
			wantErr: "nonblank comparison value",
		},
		{
			name:    "equal empty string must use is blank",
			filter:  FlowRecordFilter{Field: "RemoteKey", Operator: "EqualTo", Value: ""},
			wantErr: "nonblank comparison value",
		},
		{
			name:    "not equal whitespace must use is blank",
			filter:  FlowRecordFilter{Field: "RemoteKey", Operator: "NotEqualTo", Value: " \t"},
			wantErr: "nonblank comparison value",
		},
		{
			name:         "authored typed reference resolves at runtime",
			filter:       FlowRecordFilter{Field: "RemoteKey", Operator: "EqualTo", Value: map[string]interface{}{"elementReference": "remoteId"}},
			wantOperator: "EqualTo",
		},
		{
			name:    "unknown operator",
			filter:  FlowRecordFilter{Field: "Score", Operator: "EqualTypo", Value: 10},
			wantErr: "unsupported operator",
		},
		{
			name:    "between is outside the narrow contract",
			filter:  FlowRecordFilter{Field: "Score", Operator: "Between", Value: []interface{}{10, 20}},
			wantErr: "unsupported operator",
		},
		{
			name:    "in is outside the narrow contract",
			filter:  FlowRecordFilter{Field: "Score", Operator: "In", Value: []interface{}{10}},
			wantErr: "unsupported operator",
		},
		{
			name:    "not in is outside the narrow contract",
			filter:  FlowRecordFilter{Field: "Score", Operator: "NotIn", Value: []interface{}{10}},
			wantErr: "unsupported operator",
		},
		{
			name:    "contains is outside the narrow contract",
			filter:  FlowRecordFilter{Field: "Name", Operator: "Contains", Value: "x"},
			wantErr: "unsupported operator",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			canonical, err := ValidateGuardedFlowRecordFilter(tt.filter)
			if tt.wantErr != "" {
				if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
					t.Fatalf("ValidateGuardedFlowRecordFilter() error = %v, want %q", err, tt.wantErr)
				}
				return
			}
			if err != nil {
				t.Fatalf("ValidateGuardedFlowRecordFilter() error = %v", err)
			}
			if canonical.Operator != tt.wantOperator {
				t.Fatalf("canonical operator = %q, want %s", canonical.Operator, tt.wantOperator)
			}
		})
	}
}

func TestValidateFlowFilterLogicRequiresCompleteExpression(t *testing.T) {
	tests := []struct {
		name        string
		logic       string
		filterCount int
		wantErr     string
	}{
		{name: "default and", filterCount: 3},
		{name: "simple and", logic: "AND", filterCount: 3},
		{name: "simple or", logic: "or", filterCount: 3},
		{name: "complete custom", logic: "1 AND (2 OR 3)", filterCount: 3},
		{name: "out of range", logic: "1 AND 3", filterCount: 2, wantErr: "out of range"},
		{name: "missing reference", logic: "1", filterCount: 2, wantErr: "reference 2 is not used"},
		{name: "dangling operator", logic: "1 AND", filterCount: 1, wantErr: "ends before an operand"},
		{name: "bad opening parenthesis", logic: "1 AND (2 OR 3", filterCount: 3, wantErr: "unmatched opening parenthesis"},
		{name: "bad closing parenthesis", logic: "1 AND 2)", filterCount: 2, wantErr: "unexpected token"},
		{name: "unsupported token", logic: "1 XOR 2", filterCount: 2, wantErr: "invalid or out of range"},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := ValidateFlowFilterLogic(tt.logic, tt.filterCount)
			if tt.wantErr == "" {
				if err != nil {
					t.Fatalf("ValidateFlowFilterLogic() error = %v", err)
				}
				return
			}
			if err == nil || !strings.Contains(err.Error(), tt.wantErr) {
				t.Fatalf("ValidateFlowFilterLogic() error = %v, want %q", err, tt.wantErr)
			}
		})
	}
}
