package formula_test

import (
	"testing"

	"github.com/nexuscrm/backend/pkg/formula"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/stretchr/testify/assert"
)

// TestBuildUserFormulaScope_NilUser verifies the nil-safe contract: a nil
// user returns an empty (non-nil) map so engine.go's range loop is a no-op
// rather than a nil dereference.
func TestBuildUserFormulaScope_NilUser(t *testing.T) {
	scope := formula.BuildUserFormulaScope(nil)
	assert.NotNil(t, scope, "nil user must produce a non-nil empty map")
	assert.Empty(t, scope)
}

// TestBuildUserFormulaScope_StandardUser verifies the shape returned for a
// fully-populated session: __Profile, __User, __UserRole, and flat aliases.
func TestBuildUserFormulaScope_StandardUser(t *testing.T) {
	email := "alice@example.com"
	roleName := "Sales Manager"
	roleID := "role-001"
	user := &models.UserSession{
		ID:          "u-1",
		Name:        "Alice",
		Email:       &email,
		ProfileID:   "sales_profile",
		ProfileName: "Sales User",
		RoleID:      &roleID,
		RoleName:    &roleName,
	}

	scope := formula.BuildUserFormulaScope(user)

	// __Profile shape
	profileMap, ok := scope["__Profile"].(map[string]interface{})
	assert.True(t, ok, "__Profile must be a map")
	assert.Equal(t, "Sales User", profileMap[constants.FieldName], "__Profile.Name")
	assert.Equal(t, "sales_profile", profileMap[constants.FieldID], "__Profile.Id")

	// __User shape
	userMap, ok := scope["__User"].(map[string]interface{})
	assert.True(t, ok, "__User must be a map")
	assert.Equal(t, "u-1", userMap[constants.FieldID], "__User.Id")
	assert.Equal(t, "Alice", userMap[constants.FieldName], "__User.Name")
	assert.Equal(t, "alice@example.com", userMap[constants.FieldEmail], "__User.Email")
	assert.Equal(t, "sales_profile", userMap[constants.FieldProfileID], "__User.ProfileId")

	// __UserRole shape
	roleMap, ok := scope["__UserRole"].(map[string]interface{})
	assert.True(t, ok, "__UserRole must be a map when role is set")
	assert.Equal(t, "Sales Manager", roleMap[constants.FieldName], "__UserRole.Name")
	assert.Equal(t, "role-001", roleMap[constants.FieldID], "__UserRole.Id")

	// Flat aliases for {{User.Email}} merge fields
	assert.Equal(t, "u-1", scope[constants.FieldID], "flat Id alias")
	assert.Equal(t, "Alice", scope[constants.FieldName], "flat Name alias")
	assert.Equal(t, "alice@example.com", scope[constants.FieldEmail], "flat Email alias")
	assert.Equal(t, "sales_profile", scope[constants.FieldProfileID], "flat ProfileId alias")
}

// TestBuildUserFormulaScope_NoRole verifies that __UserRole exists with empty
// strings when the user has no role, preventing nil member-access errors on
// $UserRole.Name expressions.
func TestBuildUserFormulaScope_NoRole(t *testing.T) {
	user := &models.UserSession{
		ID:          "u-2",
		Name:        "Bob",
		ProfileID:   "standard_profile",
		ProfileName: "Standard User",
	}

	scope := formula.BuildUserFormulaScope(user)

	roleMap, ok := scope["__UserRole"].(map[string]interface{})
	assert.True(t, ok, "__UserRole must always be present")
	assert.Equal(t, "", roleMap[constants.FieldID], "__UserRole.Id must be empty string when no role")
	assert.Equal(t, "", roleMap[constants.FieldName], "__UserRole.Name must be empty string when no role")
}

// TestBuildUserFormulaScope_AdminUser verifies the shape for a system admin
// user (simulates the "Automated Process" user used by scheduled flows).
func TestBuildUserFormulaScope_AdminUser(t *testing.T) {
	user := &models.UserSession{
		ID:          "system",
		Name:        "Automated Process",
		ProfileID:   "system_profile",
		ProfileName: "Automated Process",
	}

	scope := formula.BuildUserFormulaScope(user)

	profileMap, ok := scope["__Profile"].(map[string]interface{})
	assert.True(t, ok)
	assert.Equal(t, "Automated Process", profileMap[constants.FieldName])
}

// TestBuildUserEntityMaps_ShapeMatchesFormulaScope verifies that
// BuildUserEntityMaps produces entity maps with the exact same field names and
// values as the inner maps inside BuildUserFormulaScope. This is the R10
// guarantee: both the formula-engine path and the Decision-condition path
// derive their shapes from the same helper, so they can never diverge.
func TestBuildUserEntityMaps_ShapeMatchesFormulaScope(t *testing.T) {
	email := "bob@example.com"
	roleID := "role-99"
	roleName := "Account Exec"
	user := &models.UserSession{
		ID:          "u-99",
		Name:        "Bob",
		Email:       &email,
		ProfileID:   "prof-99",
		ProfileName: "Sales Rep",
		RoleID:      &roleID,
		RoleName:    &roleName,
	}

	maps := formula.BuildUserEntityMaps(user)
	scope := formula.BuildUserFormulaScope(user)

	// Profile entity must be identical in both outputs.
	scopeProfile, ok := scope["__Profile"].(map[string]interface{})
	assert.True(t, ok, "__Profile must be a map in scope")
	assert.Equal(t, maps.Profile[constants.FieldID], scopeProfile[constants.FieldID], "Profile.Id must agree")
	assert.Equal(t, maps.Profile[constants.FieldName], scopeProfile[constants.FieldName], "Profile.Name must agree")

	// User entity must be identical.
	scopeUser, ok := scope["__User"].(map[string]interface{})
	assert.True(t, ok, "__User must be a map in scope")
	assert.Equal(t, maps.User[constants.FieldID], scopeUser[constants.FieldID], "User.Id must agree")
	assert.Equal(t, maps.User[constants.FieldName], scopeUser[constants.FieldName], "User.Name must agree")
	assert.Equal(t, maps.User[constants.FieldEmail], scopeUser[constants.FieldEmail], "User.Email must agree")
	assert.Equal(t, maps.User[constants.FieldProfileID], scopeUser[constants.FieldProfileID], "User.ProfileId must agree")

	// Role entity must be identical.
	scopeRole, ok := scope["__UserRole"].(map[string]interface{})
	assert.True(t, ok, "__UserRole must be a map in scope")
	assert.Equal(t, maps.Role[constants.FieldID], scopeRole[constants.FieldID], "Role.Id must agree")
	assert.Equal(t, maps.Role[constants.FieldName], scopeRole[constants.FieldName], "Role.Name must agree")
}

// TestBuildUserEntityMaps_NilUser verifies that a nil user returns empty
// non-nil maps rather than panicking.
func TestBuildUserEntityMaps_NilUser(t *testing.T) {
	maps := formula.BuildUserEntityMaps(nil)
	assert.NotNil(t, maps.Profile, "Profile map must be non-nil for nil user")
	assert.NotNil(t, maps.User, "User map must be non-nil for nil user")
	assert.NotNil(t, maps.Role, "Role map must be non-nil for nil user")
	assert.Equal(t, "", maps.Profile[constants.FieldName], "Profile.Name must be empty for nil user")
	assert.Equal(t, "", maps.Role[constants.FieldName], "Role.Name must be empty for nil user")
}
