package records

import (
	"context"
	"strings"

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

// applyCompoundNameSynthesis keeps Name aligned with metadata-declared compound
// components when callers write those constituents but do not explicitly send
// Name.
func applyCompoundNameSynthesis(schema *models.ObjectMetadata, data map[string]interface{}, allowOverwrite bool) {
	if data == nil {
		return
	}
	if !schemaHasField(schema, constants.FieldName) {
		return
	}

	if !allowOverwrite {
		if existing, ok := data[constants.FieldName]; ok && !isBlankValue(existing) {
			return
		}
	}

	compoundName, ok := schemautil.CompoundFieldValue(schema, constants.FieldName, data)
	if !ok {
		return
	}

	data[constants.FieldName] = compoundName
}

func schemaHasField(schema *models.ObjectMetadata, apiName string) bool {
	if schema == nil || apiName == "" {
		return false
	}
	for _, field := range schema.Fields {
		if field.APIName == apiName {
			return true
		}
	}
	return false
}

// fieldByKey looks up a field in schema.FieldsMap using the canonical lookup
// key the metadata loader writes: lower-cased API name (see
// infrastructure/persistence/metadata_schema.go Populate FieldsMap). SObject
// payload keys use the mixed-case API name, so raw map indexing would miss every
// non-lowercase field and fall back to the byte-identical string comparator.
func fieldByKey(schema *models.ObjectMetadata, key string) *models.FieldMetadata {
	if schema == nil || schema.FieldsMap == nil {
		return nil
	}
	return schema.FieldsMap[strings.ToLower(key)]
}

// isFieldNonUpdateable checks the persisted field capability from _System_Field.
func isFieldNonUpdateable(objectName string, schema *models.ObjectMetadata, fieldAPIName string) bool {
	for i := range schema.Fields {
		if schema.Fields[i].APIName == fieldAPIName {
			return !schema.Fields[i].Updateable
		}
	}
	return false
}

// isDerivedFieldUnderRecompute returns true when ctx carries the private
// derived-recompute marker (set only by PersistenceService.UpdateDerived) AND
// the field is a derived/aggregate type whose write path is the platform's
// derived-write substrate. Today this is RollupSummary only. Formulas remain
// read-only because they are computed at read time / on save by the formula
// engine, not by an external recompute pass.
func isDerivedFieldUnderRecompute(ctx context.Context, schema *models.ObjectMetadata, fieldAPIName string) bool {
	if !isDerivedRecomputeWrite(ctx) {
		return false
	}
	f := fieldByKey(schema, fieldAPIName)
	if f == nil {
		return false
	}
	return constants.NormalizeFieldType(string(f.Type)) == constants.FieldTypeRollupSummary
}
