package permissions

import (
	"strings"
	"testing"

	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
)

func TestSharingRuleCriteriaExpressionPassesFormulaText(t *testing.T) {
	expr, err := SharingRuleCriteriaExpression(`Industry == "Tech"`, nil)
	if err != nil {
		t.Fatalf("SharingRuleCriteriaExpression() error = %v, want nil", err)
	}
	if expr != `Industry == "Tech"` {
		t.Fatalf("SharingRuleCriteriaExpression() = %q, want formula pass-through", expr)
	}
}

func TestSharingRuleCriteriaExpressionNormalizesJSONAndPreservesNumberText(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "Industry", Type: constants.FieldTypeString},
			{APIName: "AnnualRevenue", Type: constants.FieldTypeCurrency},
			{APIName: "IsActive", Type: constants.FieldTypeBoolean},
		},
	}

	expr, err := SharingRuleCriteriaExpression(
		`[{"field":"Industry","op":"=","value":"Tech"},{"field":"AnnualRevenue","op":">","value":9007199254740993.000000000000000001},{"field":"IsActive","op":"=","value":true}]`,
		schema,
	)
	if err != nil {
		t.Fatalf("SharingRuleCriteriaExpression() error = %v, want nil", err)
	}

	if !strings.Contains(expr, `Industry == "Tech"`) {
		t.Fatalf("SharingRuleCriteriaExpression() = %q, missing Industry predicate", expr)
	}
	if !strings.Contains(expr, `AnnualRevenue > "9007199254740993.000000000000000001"`) {
		t.Fatalf("SharingRuleCriteriaExpression() = %q, missing exact numeric literal", expr)
	}
	if strings.Contains(expr, "9007199254740992") {
		t.Fatalf("SharingRuleCriteriaExpression() = %q, contains float64-rounded literal", expr)
	}
	if !strings.Contains(expr, `IsActive == true`) {
		t.Fatalf("SharingRuleCriteriaExpression() = %q, missing boolean predicate", expr)
	}
}

func TestSharingRuleCriteriaExpressionRejectsUnsupportedJSONFields(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "Industry", Type: constants.FieldTypeString},
		},
	}

	if _, err := SharingRuleCriteriaExpression(`[{"field":"Bogus","op":"=","value":"Tech"}]`, schema); err == nil {
		t.Fatal("SharingRuleCriteriaExpression() error = nil, want unknown field rejection")
	}
}

func TestSharingRuleCriteriaExpressionNormalizesDateJSONCriteria(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "RenewalDate__c", Type: constants.FieldTypeDate},
		},
	}

	expr, err := SharingRuleCriteriaExpression(
		`[{"field":"RenewalDate__c","op":"<=","value":"2026-06-01"}]`,
		schema,
	)
	if err != nil {
		t.Fatalf("SharingRuleCriteriaExpression() error = %v, want nil", err)
	}

	if !strings.Contains(expr, `RenewalDate__c <= "2026-06-01"`) {
		t.Fatalf("SharingRuleCriteriaExpression() = %q, missing date predicate", expr)
	}
}

func TestSharingRuleCriteriaExpressionRejectsDateTimeJSONCriteria(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "RenewalDate__c", Type: constants.FieldTypeDate},
			{APIName: "FollowUpAt__c", Type: constants.FieldTypeDatetime},
		},
	}

	if _, err := SharingRuleCriteriaExpression(`[{"field":"RenewalDate__c","op":"=","value":"2026-06-01 04:05:06"}]`, schema); err == nil {
		t.Fatal("SharingRuleCriteriaExpression() error = nil, want Date field datetime literal rejection")
	}
	if _, err := SharingRuleCriteriaExpression(`[{"field":"FollowUpAt__c","op":"=","value":"2026-06-01 04:05:06"}]`, schema); err == nil {
		t.Fatal("SharingRuleCriteriaExpression() error = nil, want DateTime field rejection")
	}
}

func TestSharingRuleRecordAccessCriteriaExpressionAcceptsNumericJSONFields(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "AnnualRevenue", Type: constants.FieldTypeCurrency},
			{APIName: "Score__c", Type: constants.FieldTypeDouble},
			{APIName: "Probability", Type: constants.FieldTypePercent},
		},
	}

	expr, err := SharingRuleRecordAccessCriteriaExpression(
		`[{"field":"AnnualRevenue","op":">","value":9007199254740993.000000000000000001},{"field":"Score__c","op":"<=","value":1.25},{"field":"Probability","op":">=","value":42.5}]`,
		schema,
	)
	if err != nil {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression() error = %v, want nil", err)
	}
	if !strings.Contains(expr, `SAFEGT(AnnualRevenue, "9007199254740993.000000000000000001")`) {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression() = %q, missing exact currency literal", expr)
	}
	if !strings.Contains(expr, `SAFELTE(Score__c, "1.25")`) {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression() = %q, missing double literal", expr)
	}
	if !strings.Contains(expr, `SAFEGTE(Probability, "42.5")`) {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression() = %q, missing percent literal", expr)
	}
}

func TestSharingRuleRecordAccessCriteriaExpressionNumericNotEqualStaysComparable(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "AnnualRevenue", Type: constants.FieldTypeCurrency},
		},
	}

	expr, err := SharingRuleRecordAccessCriteriaExpression(`[{"field":"AnnualRevenue","op":"!=","value":10}]`, schema)
	if err != nil {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression() error = %v, want nil", err)
	}
	if !strings.Contains(expr, `(SAFELT(AnnualRevenue, "10") || SAFEGT(AnnualRevenue, "10"))`) {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression() = %q, want numeric != to stay false for non-comparable values", expr)
	}
}

func TestSharingRuleRecordAccessCriteriaExpressionKeepsNumericNullComparisonsPlain(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "AnnualRevenue", Type: constants.FieldTypeCurrency},
		},
	}

	eqExpr, err := SharingRuleRecordAccessCriteriaExpression(`[{"field":"AnnualRevenue","op":"=","value":null}]`, schema)
	if err != nil {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression(= null) error = %v, want nil", err)
	}
	if eqExpr != `AnnualRevenue == nil` {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression(= null) = %q, want plain nil comparison", eqExpr)
	}

	neExpr, err := SharingRuleRecordAccessCriteriaExpression(`[{"field":"AnnualRevenue","op":"!=","value":null}]`, schema)
	if err != nil {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression(!= null) error = %v, want nil", err)
	}
	if neExpr != `AnnualRevenue != nil` {
		t.Fatalf("SharingRuleRecordAccessCriteriaExpression(!= null) = %q, want plain nil comparison", neExpr)
	}
}

func TestSharingRuleRuntimeCriteriaExpressionRejectsNonNumericLiteralsForNumericFields(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "AnnualRevenue", Type: constants.FieldTypeCurrency},
			{APIName: "Score__c", Type: constants.FieldTypeDouble},
		},
	}

	cases := []struct {
		name string
		raw  string
	}{
		{name: "currency bool", raw: `[{"field":"AnnualRevenue","op":"=","value":true}]`},
		{name: "currency string", raw: `[{"field":"AnnualRevenue","op":"=","value":"abc"}]`},
		{name: "double bool", raw: `[{"field":"Score__c","op":"=","value":false}]`},
		{name: "double string", raw: `[{"field":"Score__c","op":"=","value":"1.25"}]`},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if _, err := SharingRuleRuntimeCriteriaExpression(tc.raw, schema); err == nil {
				t.Fatal("SharingRuleRuntimeCriteriaExpression() error = nil, want non-numeric literal rejection")
			}
			if _, err := SharingRuleRecordAccessCriteriaExpression(tc.raw, schema); err == nil {
				t.Fatal("SharingRuleRecordAccessCriteriaExpression() error = nil, want non-numeric literal rejection")
			}
		})
	}
}

func TestSharingRuleRecordAccessCriteriaExpressionRejectsRollupSummaryJSONFields(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: "TotalAmount", Type: constants.FieldTypeRollupSummary},
		},
	}

	cases := []struct {
		name string
		raw  string
	}{
		{name: "rollup summary", raw: `[{"field":"TotalAmount","op":">","value":1}]`},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			if _, err := SharingRuleRuntimeCriteriaExpression(tc.raw, schema); err == nil {
				t.Fatal("SharingRuleRuntimeCriteriaExpression() error = nil, want field rejection")
			}
			if _, err := SharingRuleRecordAccessCriteriaExpression(tc.raw, schema); err == nil {
				t.Fatal("SharingRuleRecordAccessCriteriaExpression() error = nil, want field rejection")
			}
		})
	}
}

func TestSharingRuleRuntimeCriteriaExpressionRejectsSystemJSONFields(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: "Account",
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldOwnerID, Type: constants.FieldTypeReference, IsSystem: true},
		},
	}

	if _, err := SharingRuleRuntimeCriteriaExpression(`[{"field":"OwnerId","op":"=","value":"005-owner"}]`, schema); err == nil {
		t.Fatal("SharingRuleRuntimeCriteriaExpression() error = nil, want system field rejection")
	}
}
