package analytics

import (
	"context"
	"fmt"
	"log"
	"strings"

	pkgErrors "github.com/nexuscrm/kernel/pkg/errors"
	"github.com/nexuscrm/kernel/pkg/recorddisplay"
	"github.com/nexuscrm/kernel/pkg/schemautil"
	"github.com/nexuscrm/kernel/pkg/utils"
	"github.com/nexuscrm/shared/pkg/constants"
	"github.com/nexuscrm/shared/pkg/models"
	"github.com/nexuscrm/shared/pkg/queryspec"
	"github.com/nexuscrm/shared/pkg/referenceidentity"
)

func buildQueryFields(schema *models.ObjectMetadata, req models.QueryRequest, visibleFields []string) []string {
	visible := make(map[string]bool, len(visibleFields))
	for _, field := range visibleFields {
		visible[field] = true
	}

	sourceFields := req.SelectFields
	if len(sourceFields) == 0 {
		sourceFields = visibleFields
	}

	queryFields := make([]string, 0, len(sourceFields)+4)
	added := make(map[string]bool)
	addField := func(field string) {
		if field == "" || added[field] || !visible[field] {
			return
		}
		queryFields = append(queryFields, field)
		added[field] = true
	}

	addField(constants.FieldID)

	for _, field := range sourceFields {
		if strings.HasSuffix(field, "_Name") {
			base := strings.TrimSuffix(field, "_Name")
			addField(base)
			continue
		}
		if refField := lookupFieldForDotNotation(schema, field); refField != nil {
			addField(refField.APIName)
			continue
		}
		if isVirtualCompoundField(schema, field) {
			for _, member := range schemautil.CompoundMemberNames(schema, field) {
				addField(member)
			}
			continue
		}
		addField(field)
	}

	return queryFields
}

func lookupFieldForDotNotation(schema *models.ObjectMetadata, field string) *models.FieldMetadata {
	field = strings.TrimSpace(field)
	if schema == nil || strings.Count(field, ".") != 1 {
		return nil
	}

	parts := strings.SplitN(field, ".", 2)
	parent := strings.TrimSpace(parts[0])
	child := strings.TrimSpace(parts[1])
	if parent == "" || child == "" {
		return nil
	}

	refField := models.FindField(schema, parent)
	if refField == nil {
		refField = findLookupByRelationshipName(schema, parent)
	}
	if !canHydrateDotNotationLookup(refField) {
		return nil
	}

	return refField
}

func canHydrateDotNotationLookup(field *models.FieldMetadata) bool {
	if field == nil || !constants.IsReferenceType(field.Type) {
		return false
	}
	if field.IsPolymorphic || len(field.ReferenceTo) != 1 {
		return false
	}
	return strings.TrimSpace(field.ReferenceTo[0]) != ""
}

func validateQuerySelections(schema *models.ObjectMetadata, selectFields []string, targetSchema func(string) *models.ObjectMetadata) error {
	if schema == nil || len(selectFields) == 0 {
		return nil
	}

	for _, raw := range selectFields {
		field := strings.TrimSpace(raw)
		if !strings.Contains(field, ".") {
			if models.FindField(schema, field) != nil {
				continue
			}
			// Reference display-name aliases are hydrated from the schema-owned
			// reference, rather than persisted as fields of their own.
			if strings.HasSuffix(field, "_Name") {
				base := models.FindField(schema, strings.TrimSuffix(field, "_Name"))
				if base != nil && constants.IsReferenceType(base.Type) {
					continue
				}
			}
			return pkgErrors.NewValidationError("SelectFields", fmt.Sprintf("object %q has no field API name match for %q", schema.APIName, raw))
		}
		if strings.Count(field, ".") != 1 {
			return pkgErrors.NewValidationError("SelectFields", fmt.Sprintf("selection %q must name a field or a supported one-hop relationship field", raw))
		}

		parts := strings.SplitN(field, ".", 2)
		parent := strings.TrimSpace(parts[0])
		child := strings.TrimSpace(parts[1])
		if parent == "" || child == "" {
			return pkgErrors.NewValidationError("SelectFields", fmt.Sprintf("selection %q has an empty relationship or field name", raw))
		}

		refField := models.FindField(schema, parent)
		if refField == nil {
			refField = findLookupByRelationshipName(schema, parent)
		}
		if refField == nil || !constants.IsReferenceType(refField.Type) {
			return pkgErrors.NewValidationError("SelectFields", fmt.Sprintf("object %q has no reference relationship match for selection %q", schema.APIName, raw))
		}
		if !canHydrateDotNotationLookup(refField) {
			return pkgErrors.NewValidationError(
				"SelectFields",
				fmt.Sprintf("relationship field %q cannot be selected through dot notation %q because it is polymorphic, has multiple reference targets, or has no concrete reference target", refField.APIName, raw),
			)
		}

		targetAPIName := strings.TrimSpace(refField.ReferenceTo[0])
		var target *models.ObjectMetadata
		if targetSchema != nil {
			target = targetSchema(targetAPIName)
		}
		if target == nil {
			return pkgErrors.NewValidationError(
				"SelectFields",
				fmt.Sprintf("relationship field %q cannot validate dot notation %q because target object %q is unavailable", refField.APIName, raw, targetAPIName),
			)
		}
		childField := models.FindField(target, child)
		if childField == nil {
			return pkgErrors.NewValidationError(
				"SelectFields",
				fmt.Sprintf("relationship field %q cannot select child field %q through dot notation %q because %s has no field API name match", refField.APIName, child, raw, targetAPIName),
			)
		}
		if !canHydrateDotNotationChild(target, childField) {
			return pkgErrors.NewValidationError(
				"SelectFields",
				fmt.Sprintf("relationship field %q cannot select virtual or compound child field %q through dot notation %q; select its concrete member fields or the relationship Name field instead", refField.APIName, childField.APIName, raw),
			)
		}
	}

	return nil
}

func canHydrateDotNotationChild(schema *models.ObjectMetadata, field *models.FieldMetadata) bool {
	if field == nil {
		return false
	}
	if strings.EqualFold(field.APIName, constants.FieldName) {
		return true
	}
	if field.IsVirtual || field.IsCompound {
		return false
	}
	return len(schemautil.CompoundMemberNames(schema, field.APIName)) == 0
}

// Helper methods

func (qs *QueryService) hydrateVirtualFields(
	ctx context.Context,
	rows []models.SObject,
	schema *models.ObjectMetadata,
	visibleFields []string,
	currentUser *models.UserSession,
	additionalChildren map[string][]string,
) ([]models.SObject, error) {
	return qs.hydrateVirtualFieldsWithReferenceMode(
		ctx, rows, schema, visibleFields, currentUser, additionalChildren,
		queryspec.ReferenceHydrationDefault, nil,
	)
}

func (qs *QueryService) hydrateVirtualFieldsWithReferenceMode(
	ctx context.Context,
	rows []models.SObject,
	schema *models.ObjectMetadata,
	visibleFields []string,
	currentUser *models.UserSession,
	additionalChildren map[string][]string,
	mode queryspec.ReferenceHydrationMode,
	requestedFields []string,
) ([]models.SObject, error) {
	lookupFields := make([]models.FieldMetadata, 0)
	explicitLookups := explicitReferenceHydrationFields(schema, requestedFields)

	for _, field := range schema.Fields {
		if !utils.ContainsString(visibleFields, field.APIName) {
			continue
		}

		isLookup := constants.IsReferenceType(field.Type)

		if isLookup && lookupRefObject(field) != "" &&
			(mode != queryspec.ReferenceHydrationExplicitOnly || explicitLookups[strings.ToLower(field.APIName)]) {
			lookupFields = append(lookupFields, field)
		}
	}

	// Hydrate each row (static type conversion only; formula computation is handled by TiDB Views)
	for i := range rows {
		record := rows[i]
		record = schemautil.FromStorageRecord(schema, record, visibleFields)
		rows[i] = record
	}

	// Hydrate lookup field display names (batch query for efficiency)
	if len(lookupFields) > 0 {
		var err error
		rows, err = qs.hydrateLookupNames(ctx, rows, lookupFields, currentUser, additionalChildren)
		if err != nil {
			return nil, err
		}
	}

	return rows, nil
}

func explicitReferenceHydrationFields(schema *models.ObjectMetadata, selectFields []string) map[string]bool {
	result := make(map[string]bool)
	for _, raw := range selectFields {
		fieldName := strings.TrimSpace(raw)
		if fieldName == "" {
			continue
		}
		if refField := lookupFieldForDotNotation(schema, fieldName); refField != nil {
			result[strings.ToLower(refField.APIName)] = true
			continue
		}
		if strings.HasSuffix(strings.ToLower(fieldName), "_name") {
			base := strings.TrimSpace(fieldName[:len(fieldName)-len("_Name")])
			if field := models.FindField(schema, base); field != nil && constants.IsReferenceType(field.Type) {
				result[strings.ToLower(field.APIName)] = true
			}
		}
	}
	return result
}

func parseDotNotationChildren(schema *models.ObjectMetadata, selectFields []string) map[string][]string {
	if schema == nil || len(selectFields) == 0 {
		return nil
	}

	children := make(map[string][]string)
	seen := make(map[string]map[string]bool)
	for _, raw := range selectFields {
		field := strings.TrimSpace(raw)
		if strings.Count(field, ".") != 1 {
			continue
		}

		parts := strings.SplitN(field, ".", 2)
		parent := strings.TrimSpace(parts[0])
		child := strings.TrimSpace(parts[1])
		if parent == "" || child == "" || strings.EqualFold(child, constants.FieldName) {
			continue
		}

		refField := models.FindField(schema, parent)
		if refField == nil {
			refField = findLookupByRelationshipName(schema, parent)
		}
		if !canHydrateDotNotationLookup(refField) {
			continue
		}

		if seen[refField.APIName] == nil {
			seen[refField.APIName] = make(map[string]bool)
		}
		childKey := strings.ToLower(child)
		if seen[refField.APIName][childKey] {
			continue
		}
		seen[refField.APIName][childKey] = true
		children[refField.APIName] = append(children[refField.APIName], child)
	}

	if len(children) == 0 {
		return nil
	}
	return children
}

func projectExplicitRelationshipSelections(schema *models.ObjectMetadata, rows []models.SObject, selectFields []string) []models.SObject {
	if schema == nil || len(rows) == 0 || len(selectFields) == 0 {
		return rows
	}

	type relationshipProjection struct {
		field    models.FieldMetadata
		children map[string]string
	}

	directRequested := make(map[string]bool, len(selectFields))
	projections := make(map[string]*relationshipProjection)
	for _, raw := range selectFields {
		fieldName := strings.TrimSpace(raw)
		if fieldName == "" {
			continue
		}
		refField := lookupFieldForDotNotation(schema, fieldName)
		if refField == nil {
			directRequested[strings.ToLower(fieldName)] = true
			continue
		}

		parts := strings.SplitN(fieldName, ".", 2)
		child := strings.TrimSpace(parts[1])
		if child == "" {
			continue
		}

		relName := relationshipNameForField(*refField)
		projection := projections[relName]
		if projection == nil {
			projection = &relationshipProjection{
				field:    *refField,
				children: make(map[string]string),
			}
			projections[relName] = projection
		}
		projection.children[strings.ToLower(child)] = child
	}
	if len(projections) == 0 {
		return rows
	}

	for _, row := range rows {
		for relName, projection := range projections {
			existing := relationshipMap(row[relName])
			next := make(map[string]interface{}, len(projection.children)+1)
			if existing != nil {
				if attrs, ok := existing["attributes"]; ok {
					next["attributes"] = attrs
				}
			}

			addedChild := false
			for _, child := range projection.children {
				if existing != nil {
					if key, value, ok := relationshipValue(existing, child); ok {
						next[key] = value
						addedChild = true
						continue
					}
				}
				if strings.EqualFold(child, constants.FieldName) {
					if value, ok := row[projection.field.APIName+"_Name"]; ok {
						next[constants.FieldName] = value
						addedChild = true
					}
				}
			}

			if addedChild {
				if _, ok := next["attributes"]; !ok && len(projection.field.ReferenceTo) > 0 {
					models.StampObjectType(models.SObject(next), projection.field.ReferenceTo[0])
				}
				row[relName] = next
			} else if existing == nil {
				delete(row, relName)
			}

			fieldKey := strings.ToLower(projection.field.APIName)
			nameKey := strings.ToLower(projection.field.APIName + "_Name")
			if !directRequested[fieldKey] {
				delete(row, projection.field.APIName)
			}
			if !directRequested[fieldKey] && !directRequested[nameKey] {
				delete(row, projection.field.APIName+"_Name")
			}
		}
	}

	return rows
}

func relationshipValue(values map[string]interface{}, key string) (string, interface{}, bool) {
	if value, ok := values[key]; ok {
		return key, value, true
	}
	for existingKey, value := range values {
		if strings.EqualFold(existingKey, key) {
			return existingKey, value, true
		}
	}
	return "", nil, false
}

func relationshipMap(value interface{}) map[string]interface{} {
	switch typed := value.(type) {
	case map[string]interface{}:
		return typed
	case models.SObject:
		return map[string]interface{}(typed)
	default:
		return nil
	}
}

func projectSubqueryGroupingFields(rows []models.SObject, fkField string, selectFields, visibleFields []string) []models.SObject {
	if len(rows) == 0 || strings.TrimSpace(fkField) == "" {
		return rows
	}

	fieldKey := strings.ToLower(fkField)
	nameKey := strings.ToLower(fkField + "_Name")
	visible := make(map[string]bool, len(visibleFields))
	for _, field := range visibleFields {
		visible[strings.ToLower(strings.TrimSpace(field))] = true
	}
	directRequested := make(map[string]bool, len(selectFields))
	for _, field := range selectFields {
		field = strings.TrimSpace(field)
		if field == "" || strings.Contains(field, ".") {
			continue
		}
		key := strings.ToLower(field)
		if visible[key] {
			directRequested[key] = true
		}
	}

	for _, row := range rows {
		if !directRequested[fieldKey] {
			delete(row, fkField)
		}
		if !directRequested[fieldKey] && !directRequested[nameKey] {
			delete(row, fkField+"_Name")
		}
	}

	return rows
}

func projectVirtualCompoundFields(rows []models.SObject, schema *models.ObjectMetadata, requestedFields []string) []models.SObject {
	if len(rows) == 0 || schema == nil || len(requestedFields) == 0 {
		return rows
	}

	requested := make(map[string]struct{}, len(requestedFields))
	var selected []models.FieldMetadata
	for _, fieldName := range requestedFields {
		requested[strings.ToLower(fieldName)] = struct{}{}
		field := models.FindField(schema, fieldName)
		if field == nil || !isVirtualCompoundField(schema, field.APIName) {
			continue
		}
		selected = append(selected, *field)
	}
	if len(selected) == 0 {
		return rows
	}

	supportOnly := make(map[string]struct{})
	for _, field := range selected {
		for _, member := range schemautil.CompoundMemberNames(schema, field.APIName) {
			if _, explicitlyRequested := requested[strings.ToLower(member)]; !explicitlyRequested {
				supportOnly[member] = struct{}{}
			}
		}
	}

	for i := range rows {
		record := rows[i]
		for _, field := range selected {
			if value, ok := schemautil.CompoundFieldValue(schema, field.APIName, record); ok {
				record[field.APIName] = value
			} else {
				record[field.APIName] = nil
			}
		}
		for member := range supportOnly {
			delete(record, member)
		}
		rows[i] = record
	}

	return rows
}

func isVirtualCompoundField(schema *models.ObjectMetadata, fieldName string) bool {
	field := models.FindField(schema, fieldName)
	return field != nil && field.IsVirtual && field.VirtualKind == constants.VirtualFieldCompound
}

// getLookupIdField determines the ID field to use for lookups from metadata
// Returns the SSoT ID field (constants.FieldID).
// Legacy support for ExternalId overriding has been removed to enforce strict FK integrity.
func getLookupIdField(schema *models.ObjectMetadata) string {
	return constants.FieldID
}

func getLookupDisplayFields(schema *models.ObjectMetadata) []string {
	fields := []string{constants.FieldID}
	fields = append(fields, recorddisplay.LookupFieldNames(schema)...)
	return fields
}

func (qs *QueryService) lookupDisplayFieldsForRead(ctx context.Context, objectName string, schema *models.ObjectMetadata, currentUser *models.UserSession) ([]string, error) {
	fields := getLookupDisplayFields(schema)
	visible := make([]string, 0, len(fields))
	for _, apiName := range fields {
		field := models.FindField(schema, apiName)
		if field == nil {
			visible = append(visible, apiName)
			continue
		}
		readable, err := qs.fieldReadableForQueryWithSchema(ctx, objectName, schema, *field, currentUser)
		if err != nil {
			return nil, err
		}
		if readable {
			visible = append(visible, apiName)
		}
	}
	return visible, nil
}

func extractLookupDisplayName(schema *models.ObjectMetadata, rec models.SObject) string {
	if schema == nil {
		return ""
	}

	return recorddisplay.LookupName(schema, rec)
}

func appendLookupDisplayField(fields []string, fieldName string) []string {
	if fieldName == "" || utils.ContainsString(fields, fieldName) {
		return fields
	}
	return append(fields, fieldName)
}

func appendRecordAccessInputFields(fields []string, schema *models.ObjectMetadata) []string {
	// CheckRecordAccess can evaluate ownership, folder/parent policies, target
	// record policies, and criteria-based sharing rules. Preserve the retired
	// REST hydrator's full-row RLS semantics while keeping response projection
	// limited to display/requested relationship fields.
	for _, field := range models.PersistableFields(schema) {
		fields = appendLookupDisplayField(fields, field.APIName)
	}
	return fields
}

// hydrateLookupNames resolves lookup field UUIDs to display names
// It batches queries per referenced object for efficiency.
// Records the user cannot access are silently omitted (names not populated).
// lookupRefObject returns the object a lookup field's display name resolves
// against, applying the polymorphic OwnerId→User convention shared by every
// hydrator (sobject_records, uiapi aggregator). Centralizing
// it here lets QueryService hydration match the REST hydrator exactly so the
// latter can be retired (issue #1585).
func lookupRefObject(field models.FieldMetadata) string {
	if field.IsPolymorphic {
		if field.APIName == constants.FieldOwnerID {
			return constants.TableUser
		}
		if len(field.ReferenceTo) == 1 && strings.TrimSpace(field.ReferenceTo[0]) != "" {
			return field.ReferenceTo[0]
		}
		return ""
	}
	if len(field.ReferenceTo) > 0 && strings.TrimSpace(field.ReferenceTo[0]) != "" {
		return field.ReferenceTo[0]
	}
	target, _ := constants.PlatformReferenceTargetForField(field.APIName)
	return target
}

func (qs *QueryService) hydrateLookupNames(
	ctx context.Context,
	rows []models.SObject,
	lookupFields []models.FieldMetadata,
	currentUser *models.UserSession,
	additionalChildren map[string][]string,
) ([]models.SObject, error) {
	if len(rows) == 0 || len(lookupFields) == 0 {
		return rows, nil
	}

	// For each lookup field, collect all unique IDs and their reference object
	// Map: referenceObject -> Set of IDs
	refObjectIDs := make(map[string]map[string]bool)

	for _, field := range lookupFields {
		refObject := lookupRefObject(field) // honors polymorphic OwnerId→User
		if _, exists := refObjectIDs[refObject]; !exists {
			refObjectIDs[refObject] = make(map[string]bool)
		}

		for _, row := range rows {
			if val, ok := row[field.APIName]; ok && val != nil {
				if id, ok := val.(string); ok && id != "" {
					refObjectIDs[refObject][id] = true
				}
			}
		}
	}

	type hydratedLookupRecord struct {
		name   string
		record models.SObject
	}

	// For each reference object, fetch the names and requested child fields of all IDs.
	// Map: referenceObject -> ID -> hydrated data
	hydratedByObject := make(map[string]map[string]hydratedLookupRecord)
	visibleChildrenByLookup := make(map[string][]string)

	for refObject, idSet := range refObjectIDs {
		if len(idSet) == 0 {
			continue
		}

		// Get the schema for the referenced object to find the name and ID fields
		refSchema := qs.metadata.GetSchema(ctx, refObject)
		if refSchema == nil {
			continue
		}

		// Determine ID field from metadata (not hardcoded)
		idField := getLookupIdField(refSchema)

		fetchFields, err := qs.lookupDisplayFieldsForRead(ctx, refSchema.APIName, refSchema, currentUser)
		if err != nil {
			return nil, err
		}
		addFetchField := func(fieldName string) {
			if fieldName == "" || utils.ContainsString(fetchFields, fieldName) {
				return
			}
			fetchFields = append(fetchFields, fieldName)
		}

		bypassUserAccess := scopedReadBypassesUserAccess(ctx, refSchema.APIName)
		bypassFieldVisibility := scopedReadBypassesFieldVisibility(ctx, refSchema.APIName)
		requiresRecordAccessCheck := !bypassUserAccess && currentUser != nil && qs.permissions != nil
		for _, field := range lookupFields {
			if lookupRefObject(field) != refObject {
				continue
			}
			for _, requestedChild := range additionalChildren[field.APIName] {
				childField := models.FindField(refSchema, requestedChild)
				if childField == nil || strings.EqualFold(childField.APIName, constants.FieldName) {
					continue
				}
				if bypassFieldVisibility {
					if qs.fieldReadBlockedWithSchema(ctx, refSchema.APIName, refSchema, *childField) {
						continue
					}
				} else {
					if qs.permissions == nil {
						continue
					}
					visible, err := qs.checkFieldVisibilityWithSchema(ctx, refSchema.APIName, refSchema, childField, currentUser)
					if err != nil {
						return nil, err
					}
					if !visible {
						continue
					}
				}
				addFetchField(childField.APIName)
				if !utils.ContainsString(visibleChildrenByLookup[field.APIName], childField.APIName) {
					visibleChildrenByLookup[field.APIName] = append(visibleChildrenByLookup[field.APIName], childField.APIName)
				}
			}
		}
		if requiresRecordAccessCheck {
			fetchFields = appendRecordAccessInputFields(fetchFields, refSchema)
		}

		// Collect IDs into slice
		ids := make([]string, 0, len(idSet))
		for id := range idSet {
			ids = append(ids, id)
		}

		physicalIDs := ids
		var immutableResults []models.SObject
		if authority, bound := referenceidentity.FromContext(ctx); bound {
			resolution, resolveErr := authority.ResolveReferenceIdentities(ctx, refObject, ids, fetchFields)
			if resolveErr != nil {
				return nil, fmt.Errorf("resolve %s lookup identities: %w", refObject, resolveErr)
			}
			physicalIDs = make([]string, 0, len(ids))
			immutableResults = make([]models.SObject, 0, len(ids))
			for _, id := range ids {
				identity, ok := resolution.Identities[id]
				if !ok || identity.Disposition == referenceidentity.Unowned {
					physicalIDs = append(physicalIDs, id)
					continue
				}
				switch identity.Disposition {
				case referenceidentity.Immutable:
					resolvedID, _ := identity.Record[idField].(string)
					if identity.Record == nil || strings.TrimSpace(resolvedID) != id {
						return nil, fmt.Errorf("reference identity authority returned an invalid immutable record for %s/%s", refObject, id)
					}
					immutableResults = append(immutableResults, identity.Record)
				case referenceidentity.OwnedMissing:
					return nil, fmt.Errorf("authoritative %s lookup identity %q does not exist", refObject, id)
				default:
					return nil, fmt.Errorf("reference identity authority returned an invalid disposition for %s/%s", refObject, id)
				}
			}
		}

		var physicalResults []models.SObject
		if len(physicalIDs) != 0 {
			physicalResults, err = qs.repo.GetRecordsByIDs(ctx, refSchema, physicalIDs, fetchFields)
			if err != nil {
				log.Printf("⚠️ Failed to hydrate lookup names for %s: %v", refObject, err)
				continue
			}
		}

		// Build ID -> display name map using the same idField.
		// Apply record-level access check (RLS) to omit names for records the user cannot read.
		// SystemContext and metadata-owned scoped reads bypass this check.
		if requiresRecordAccessCheck && len(physicalResults) != 0 {
			qs.permissions.PrimeReadAccessInputCache(ctx, refSchema.APIName, physicalResults, currentUser)
		}
		for _, rec := range physicalResults {
			if requiresRecordAccessCheck && !qs.permissions.CheckRecordAccess(ctx, refSchema, rec, constants.PermRead, currentUser) {
				continue // silently omit — lookup name will not be populated
			}
			if id, ok := rec[idField].(string); ok {
				if hydratedByObject[refObject] == nil {
					hydratedByObject[refObject] = make(map[string]hydratedLookupRecord)
				}
				hydrated := hydratedLookupRecord{record: rec}
				if display := extractLookupDisplayName(refSchema, rec); display != "" {
					hydrated.name = display
				}
				hydratedByObject[refObject][id] = hydrated
			}
		}
		for _, rec := range immutableResults {
			id, _ := rec[idField].(string)
			if id == "" {
				continue
			}
			if hydratedByObject[refObject] == nil {
				hydratedByObject[refObject] = make(map[string]hydratedLookupRecord)
			}
			hydratedByObject[refObject][id] = hydratedLookupRecord{
				name:   extractLookupDisplayName(refSchema, rec),
				record: rec,
			}
		}
	}

	// Populate _Name fields on each row
	for i, row := range rows {
		for _, field := range lookupFields {
			if val, ok := row[field.APIName]; ok && val != nil {
				if id, ok := val.(string); ok {
					refObject := lookupRefObject(field)
					if refObject == "" {
						continue
					}
					hydrated, found := hydratedByObject[refObject][id]
					if !found {
						continue
					}
					if hydrated.name != "" {
						row[field.APIName+"_Name"] = hydrated.name
					}

					children := visibleChildrenByLookup[field.APIName]
					if len(children) == 0 {
						continue
					}
					nested := map[string]interface{}{
						constants.FieldID: id,
					}
					models.StampObjectType(models.SObject(nested), refObject)
					if hydrated.name != "" {
						nested[constants.FieldName] = hydrated.name
					}
					for _, child := range children {
						if childValue, ok := hydrated.record[child]; ok {
							nested[child] = childValue
						}
					}
					row[relationshipNameForField(field)] = nested
				}
			}
		}
		rows[i] = row
	}

	return rows, nil
}

// Calculate returns the record as-is; formula computation is handled by TiDB Views.
func (qs *QueryService) Calculate(
	ctx context.Context,
	objectName string,
	record models.SObject,
	user *models.UserSession,
) (models.SObject, error) {
	schema := qs.metadata.GetSchema(ctx, objectName)
	if schema == nil {
		return nil, pkgErrors.NewNotFoundError("Object", objectName)
	}

	record = schemautil.NormalizeSObject(schema, record)

	return record, nil
}
