package bootstrapimpl

import (
	"strings"

	"github.com/nexuscrm/db/pkg/runtimeports"
	domainschema "github.com/nexuscrm/kernel/pkg/schema"
	"github.com/nexuscrm/shared/pkg/constants"
)

// The managed-relationship field-metadata cluster stays in bootstrap because the
// L0 assertion path (AssertManagedRelationshipConsistency in assertions_managed.go)
// reuses the same field-metadata derivation and overlay-row predicate that the
// relocated controlplane relationship reconcile uses. The type/builder/key/predicate
// are exported (with exported fields) so the relocated reconcile can reach them
// across the package boundary; logic is unchanged from the original in
// managed_relationships.go.

// ManagedRelationshipFieldMetadata captures the field-derived relationship shape
// used to decide whether an explicit _System_Relationship overlay row is needed.
// It is a type alias to runtimeports.ManagedRelationshipFieldMetadata so the
// SchemaProvisioner port can expose BuildManagedRelationshipFieldMetadata /
// NeedsOverlayRow to control-plane callers without any conversion: the bootstrap
// assertion path and the relocated controlplane reconcile share one type.
type ManagedRelationshipFieldMetadata = runtimeports.ManagedRelationshipFieldMetadata

// BuildManagedRelationshipFieldMetadata derives field-level relationship metadata
// (keyed by child|parent|field) from the supplied table definitions.
func BuildManagedRelationshipFieldMetadata(tableDefs []domainschema.TableDefinition) map[string]ManagedRelationshipFieldMetadata {
	metadata := make(map[string]ManagedRelationshipFieldMetadata)
	for _, def := range tableDefs {
		for _, col := range def.Columns {
			if len(col.ReferenceTo) == 0 {
				continue
			}
			for _, parent := range col.ReferenceTo {
				key := ManagedRelationshipKey(def.TableName, parent, col.Name)
				relationshipName := col.RelationshipName
				if relationshipName == "" && strings.HasSuffix(col.Name, "Id") {
					relationshipName = strings.TrimSuffix(col.Name, "Id")
				}
				relationshipType := string(constants.FieldTypeReference)
				if col.IsMasterDetail || strings.EqualFold(col.LogicalType, string(constants.FieldTypeMasterDetail)) {
					relationshipType = string(constants.FieldTypeMasterDetail)
				}
				metadata[key] = ManagedRelationshipFieldMetadata{
					RelationshipName: relationshipName,
					RelationshipType: relationshipType,
				}
			}
		}
	}
	return metadata
}

// ManagedRelationshipKey builds the lower-cased child|parent|field key.
func ManagedRelationshipKey(childObjectAPIName, parentObjectAPIName, fieldAPIName string) string {
	return strings.ToLower(childObjectAPIName) + "|" + strings.ToLower(parentObjectAPIName) + "|" + strings.ToLower(fieldAPIName)
}

// NeedsOverlayRow returns true when the managed definition specifies semantics
// that the field-derived resolver cannot produce on its own. Master-detail
// fields already yield IsSharingParent=true and CascadeDelete=true, so they
// never need an overlay row. Plain reference fields that the pack/substrate
// contract marks as sharing-parent or cascade-delete require an explicit
// _System_Relationship row so ResolveEffectiveRelationships can upgrade the
// field-derived edge.
func NeedsOverlayRow(meta ManagedRelationshipFieldMetadata, def domainschema.ManagedRelationshipSemantics) bool {
	isMasterDetail := meta.RelationshipType == string(constants.FieldTypeMasterDetail)
	if isMasterDetail {
		// Field-derived resolution already covers IsSharingParent and
		// CascadeDelete for master-detail fields. No overlay row needed.
		return false
	}
	// Plain reference field: check if the managed definition requires
	// semantics that differ from the field-derived defaults.
	if def.IsSharingParent != nil && *def.IsSharingParent {
		return true
	}
	if def.CascadeDelete != nil && *def.CascadeDelete {
		return true
	}
	if def.RestrictedDelete != nil && *def.RestrictedDelete {
		return true
	}
	return false
}
