package packs

import (
	"strings"

	"github.com/nexuscrm/shared/pkg/models"
	connector "github.com/pingcap-inc/nexuscrm-contract/pkg/connector"
)

func connectorPushValidationOptionsFromObjectSpecs(specs []ObjectSpec, customFields []declaredCustomFieldExtension, contracts []connector.ConnectorPushDurableMetadataContract, fallbackSchema func(string) *models.ObjectMetadata) connector.ConnectorPushContractValidationOptions {
	fieldsByObject := map[string][]connector.ConnectorPushObjectFieldMetadata{}
	for _, spec := range specs {
		objectAPIName := strings.TrimSpace(spec.APIName)
		if objectAPIName == "" {
			continue
		}
		for _, field := range spec.Fields {
			appendConnectorPushValidationFieldMetadata(fieldsByObject, objectAPIName, field)
		}
	}
	for _, customField := range customFields {
		appendConnectorPushValidationFieldMetadata(fieldsByObject, customField.ObjectAPIName, customField.Field)
	}
	if fallbackSchema != nil {
		fallbackObjectAPINames := make([]string, 0, len(contracts))
		seenFallbackObjects := map[string]struct{}{}
		appendFallbackObjectAPIName := func(objectAPIName string) {
			objectAPIName = strings.TrimSpace(objectAPIName)
			if objectAPIName == "" {
				return
			}
			key := strings.ToLower(objectAPIName)
			if _, ok := seenFallbackObjects[key]; ok {
				return
			}
			seenFallbackObjects[key] = struct{}{}
			fallbackObjectAPINames = append(fallbackObjectAPINames, objectAPIName)
		}
		for _, contract := range contracts {
			appendFallbackObjectAPIName(contract.SourceObject)
			for _, assoc := range contract.Associations {
				if assoc.Junction != nil {
					appendFallbackObjectAPIName(assoc.Junction.Object)
				}
			}
		}
		for _, objectAPIName := range fallbackObjectAPINames {
			appendConnectorPushValidationObjectMetadata(fieldsByObject, fallbackSchema(objectAPIName))
		}
	}
	return connector.ConnectorPushContractValidationOptions{
		ObjectFields:                   fieldsByObject,
		RequireSourceLookupFieldSchema: true,
	}
}

func appendConnectorPushValidationFieldMetadata(fieldsByObject map[string][]connector.ConnectorPushObjectFieldMetadata, objectAPIName string, field FieldSpec) {
	objectAPIName = strings.TrimSpace(objectAPIName)
	fieldAPIName := strings.TrimSpace(field.APIName)
	if objectAPIName == "" || fieldAPIName == "" {
		return
	}
	key := strings.ToLower(objectAPIName)
	for _, existing := range fieldsByObject[key] {
		if strings.TrimSpace(existing.APIName) == fieldAPIName {
			return
		}
	}
	fieldsByObject[key] = append(fieldsByObject[key], connector.ConnectorPushObjectFieldMetadata{
		APIName:     fieldAPIName,
		Type:        strings.TrimSpace(field.Type),
		ReferenceTo: append([]string(nil), field.ReferenceTo...),
	})
}

func appendConnectorPushValidationObjectMetadata(fieldsByObject map[string][]connector.ConnectorPushObjectFieldMetadata, schema *models.ObjectMetadata) {
	if schema == nil {
		return
	}
	objectAPIName := strings.TrimSpace(schema.APIName)
	if objectAPIName == "" {
		return
	}
	for _, field := range schema.Fields {
		apiName := strings.TrimSpace(field.APIName)
		if apiName == "" {
			continue
		}
		appendConnectorPushValidationFieldMetadata(fieldsByObject, objectAPIName, FieldSpec{
			APIName:     apiName,
			Type:        strings.TrimSpace(string(field.Type)),
			ReferenceTo: append([]string(nil), field.ReferenceTo...),
		})
	}
}
