package metadata

import (
	"testing"

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

// TestUserCreate_EssentialsContract_ValidateRequiredForCreate_OmitsFirstName
// verifies that validateRequiredForCreate accepts a User payload that omits
// FirstName. This validates the fix for issue #974:
//
// - User.FirstName is non-required in the authoritative schema
// - The platform accepts LastName-only creates per Salesforce v63 contract
func TestUserCreate_EssentialsContract_ValidateRequiredForCreate_OmitsFirstName(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: constants.TableUser,
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldEmail, Type: constants.FieldTypeEmail, Required: true},
			{APIName: constants.FieldFirstName, Type: constants.FieldTypeString, Required: false},
			{APIName: constants.FieldLastName, Type: constants.FieldTypeString, Required: true},
			{APIName: constants.FieldPassword, Type: constants.FieldTypeString, Required: true, DefaultedOnCreate: true},
			{APIName: constants.FieldProfileID, Type: constants.FieldTypeReference, Required: true},
		},
	}

	// Payload that omits FirstName
	data := map[string]interface{}{
		"Email":     "jane.doe@test.com",
		"LastName":  "Doe",
		"ProfileId": "standard_user",
	}

	// Run validateRequiredForCreate logic
	missing := collectMissingRequiredFields(schema, data)
	if len(missing) > 0 {
		t.Fatalf("validateRequiredForCreate incorrectly flagged missing fields: %v", missing)
	}
}

// TestUserCreate_EssentialsContract_ValidateRequiredForCreate_MissingLastName
// verifies that validateRequiredForCreate rejects a User payload that omits
// LastName even when FirstName is present. LastName is required with
// defaultedOnCreate=false per SF v63 contract.
func TestUserCreate_EssentialsContract_ValidateRequiredForCreate_MissingLastName(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: constants.TableUser,
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldEmail, Type: constants.FieldTypeEmail, Required: true},
			{APIName: constants.FieldFirstName, Type: constants.FieldTypeString, Required: false},
			{APIName: constants.FieldLastName, Type: constants.FieldTypeString, Required: true},
			{APIName: constants.FieldPassword, Type: constants.FieldTypeString, Required: true, DefaultedOnCreate: true},
			{APIName: constants.FieldProfileID, Type: constants.FieldTypeReference, Required: true},
		},
	}

	// Payload that omits LastName but has FirstName
	data := map[string]interface{}{
		"Email":     "test@test.com",
		"FirstName": "Test",
		"ProfileId": "standard_user",
	}

	missing := collectMissingRequiredFields(schema, data)
	foundLastName := false
	for _, m := range missing {
		if m == constants.FieldLastName {
			foundLastName = true
			break
		}
	}
	if !foundLastName {
		t.Fatalf("validateRequiredForCreate should have flagged LastName as missing, got: %v", missing)
	}
}

// TestUserCreate_EssentialsContract_ValidateRequiredForCreate_WithBothNames
// verifies that validateRequiredForCreate accepts a User payload with both
// FirstName and LastName provided.
func TestUserCreate_EssentialsContract_ValidateRequiredForCreate_WithBothNames(t *testing.T) {
	schema := &models.ObjectMetadata{
		APIName: constants.TableUser,
		Fields: []models.FieldMetadata{
			{APIName: constants.FieldEmail, Type: constants.FieldTypeEmail, Required: true},
			{APIName: constants.FieldFirstName, Type: constants.FieldTypeString, Required: false},
			{APIName: constants.FieldLastName, Type: constants.FieldTypeString, Required: true},
			{APIName: constants.FieldPassword, Type: constants.FieldTypeString, Required: true, DefaultedOnCreate: true},
			{APIName: constants.FieldProfileID, Type: constants.FieldTypeReference, Required: true},
		},
	}
	data := map[string]interface{}{
		"Email":     "john.smith@test.com",
		"FirstName": "John",
		"LastName":  "Smith",
		"ProfileId": "standard_user",
	}

	missing := collectMissingRequiredFields(schema, data)
	if len(missing) > 0 {
		t.Fatalf("validateRequiredForCreate incorrectly flagged missing fields: %v", missing)
	}
}

// collectMissingRequiredFields mirrors the logic in sobject_records.go:validateRequiredForCreate.
// It returns the list of required fields that are missing from the payload, skipping
// DefaultedOnCreate fields (which are synthesized by the server).
func collectMissingRequiredFields(schema *models.ObjectMetadata, data map[string]interface{}) []string {
	var missing []string
	for _, f := range schema.Fields {
		if !f.Required {
			continue
		}
		if f.DefaultedOnCreate {
			continue
		}
		if isCompoundParentField(schema, f.APIName) {
			continue
		}
		if fieldSatisfiedByNameComponents(schema, f, data) {
			continue
		}
		v, present := data[f.APIName]
		if !present || isEmptyValue(v) {
			missing = append(missing, f.APIName)
		}
	}
	return missing
}

// isCompoundParentField checks if the field is a compound parent by looking
// for composedFrom on the field itself (via metadata decorators).
func isCompoundParentField(schema *models.ObjectMetadata, fieldAPIName string) bool {
	field := models.FindField(schema, fieldAPIName)
	if field == nil {
		return false
	}
	return field.IsCompound && len(field.ComposedFrom) > 0
}

// fieldSatisfiedByNameComponents checks if a compound parent field is satisfied
// by any of its constituent member fields being present in the data.
func fieldSatisfiedByNameComponents(schema *models.ObjectMetadata, field models.FieldMetadata, data map[string]interface{}) bool {
	if !field.IsCompound || len(field.ComposedFrom) == 0 {
		return false
	}
	for _, component := range field.ComposedFrom {
		if v, ok := data[component]; ok && !isEmptyValue(v) {
			return true
		}
	}
	return false
}

// isEmptyValue reports whether a value is effectively empty for required-field validation.
func isEmptyValue(v interface{}) bool {
	if v == nil {
		return true
	}
	if s, ok := v.(string); ok {
		return s == ""
	}
	return false
}
