package records

import (
	"context"
	"strings"

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

func polymorphicDiscriminatorColumns(schema *models.ObjectMetadata) map[string]struct{} {
	if schema == nil {
		return nil
	}
	out := make(map[string]struct{})
	for i := range schema.Fields {
		field := &schema.Fields[i]
		if !isPolymorphicReferenceField(field) {
			continue
		}
		out[strings.ToLower(schemautil.GetPolymorphicTypeColumnName(field.APIName))] = struct{}{}
	}
	if len(out) == 0 {
		return nil
	}
	return out
}

func isPolymorphicReferenceField(field *models.FieldMetadata) bool {
	return models.IsPolymorphicReferenceField(field)
}

func scrubPolymorphicDiscriminators(schema *models.ObjectMetadata, data models.SObject) {
	if len(data) == 0 {
		return
	}
	discriminators := polymorphicDiscriminatorColumns(schema)
	if len(discriminators) == 0 {
		return
	}
	for key := range data {
		if _, ok := discriminators[strings.ToLower(key)]; ok {
			delete(data, key)
		}
	}
}

func isWritableReferenceTarget(ctx context.Context, field *models.FieldMetadata, resolvedTarget string) bool {
	if field == nil || len(field.WritableReferenceTo) == 0 {
		return true
	}
	if constants.GetSecurityContext(ctx).BypassesReferenceTargetWriteRestrictions() {
		return true
	}
	for _, allowed := range field.WritableReferenceTo {
		if strings.EqualFold(strings.TrimSpace(allowed), strings.TrimSpace(resolvedTarget)) {
			return true
		}
	}
	return false
}

func restorePolymorphicDiscriminatorsFromOldRecord(schema *models.ObjectMetadata, recordToValidate, effectiveUpdates, oldRecord models.SObject) {
	if recordToValidate == nil {
		return
	}
	discriminators := polymorphicDiscriminatorColumns(schema)
	if len(discriminators) == 0 {
		return
	}
	for key := range effectiveUpdates {
		if _, ok := discriminators[strings.ToLower(key)]; ok {
			delete(effectiveUpdates, key)
		}
	}
	for i := range schema.Fields {
		field := &schema.Fields[i]
		if !isPolymorphicReferenceField(field) {
			continue
		}
		typeCol := schemautil.GetPolymorphicTypeColumnName(field.APIName)
		delete(effectiveUpdates, typeCol)
		if oldVal, ok := oldRecord[typeCol]; ok {
			recordToValidate[typeCol] = oldVal
		} else {
			delete(recordToValidate, typeCol)
		}
	}
}

func (ps *PersistenceService) reconcilePolymorphicDiscriminatorUpdates(ctx context.Context, schema *models.ObjectMetadata, recordToValidate, effectiveUpdates, oldRecord models.SObject) error {
	if schema == nil || recordToValidate == nil || effectiveUpdates == nil {
		return nil
	}
	for i := range schema.Fields {
		field := &schema.Fields[i]
		if !isPolymorphicReferenceField(field) {
			continue
		}
		val, changed := effectiveUpdates[field.APIName]
		if !changed {
			continue
		}
		if val == nil {
			typeCol := schemautil.GetPolymorphicTypeColumnName(field.APIName)
			recordToValidate[typeCol] = nil
			effectiveUpdates[typeCol] = nil
			continue
		}
		if id, ok := val.(string); ok && id == "" {
			typeCol := schemautil.GetPolymorphicTypeColumnName(field.APIName)
			recordToValidate[typeCol] = nil
			effectiveUpdates[typeCol] = nil
		}
	}

	resolvedTypes, err := ps.validatePolymorphicLookups(ctx, effectiveUpdates, schema)
	if err != nil {
		return err
	}
	for fieldName, objType := range resolvedTypes {
		typeCol := schemautil.GetPolymorphicTypeColumnName(fieldName)
		recordToValidate[typeCol] = objType
		if oldRecord[typeCol] != objType {
			effectiveUpdates[typeCol] = objType
		}
	}
	return nil
}
