package metadatarel

import (
	"sort"
	"strings"

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

type DeleteSemantics struct {
	Cascade    bool
	Restricted bool
	SetNull    bool
	Rule       constants.DeleteRule
}

func relationshipCacheKey(childObjectAPIName, fieldAPIName, parentObjectAPIName string) string {
	return strings.ToLower(childObjectAPIName) + "|" + strings.ToLower(fieldAPIName) + "|" + strings.ToLower(parentObjectAPIName)
}

func restrictDeleteSemantics() DeleteSemantics {
	return DeleteSemantics{Restricted: true, Rule: constants.DeleteRuleRestrict}
}

func cascadeDeleteSemantics() DeleteSemantics {
	return DeleteSemantics{Cascade: true, Rule: constants.DeleteRuleCascade}
}

func setNullDeleteSemantics() DeleteSemantics {
	return DeleteSemantics{SetNull: true, Rule: constants.DeleteRuleSetNull}
}

// ResolveDeleteSemantics is the single fail-closed delete-rule resolver for a
// field-derived relationship edge and its optional effective relationship row.
// Effective rows are overlays: explicit true booleans can select cascade or
// restrict, but a silent row falls back to the backing field instead of erasing
// field-owned semantics.
func ResolveDeleteSemantics(field models.FieldMetadata, rel *models.Relationship) DeleteSemantics {
	sem := models.ResolveFieldRelationshipSemantics(&field, models.LifecyclePersistedRead)
	if !sem.IsRelationship {
		return DeleteSemantics{}
	}

	if rel != nil {
		if rel.RestrictedDelete {
			return restrictDeleteSemantics()
		}
		if rel.CascadeDelete {
			return cascadeDeleteSemantics()
		}
	}

	if sem.IsMasterDetail {
		return cascadeDeleteSemantics()
	}
	if field.DeleteRule != nil {
		switch constants.NormalizeDeleteRule(*field.DeleteRule) {
		case constants.DeleteRuleCascade:
			return cascadeDeleteSemantics()
		case constants.DeleteRuleSetNull:
			return setNullDeleteSemantics()
		case constants.DeleteRuleRestrict:
			return restrictDeleteSemantics()
		}
	}
	return restrictDeleteSemantics()
}

// ResolveFieldDeleteSemantics folds all effective relationship rows for a
// field into the field-level describe semantics. A field only advertises
// cascade when every target cascades; any restricting target makes the field
// restricted.
func ResolveFieldDeleteSemantics(field models.FieldMetadata, relationships []*models.Relationship) DeleteSemantics {
	if len(relationships) == 0 {
		return ResolveDeleteSemantics(field, nil)
	}

	allCascade := true
	sawSetNull := false
	for _, rel := range relationships {
		sem := ResolveDeleteSemantics(field, rel)
		if sem.Restricted {
			return restrictDeleteSemantics()
		}
		if sem.SetNull {
			sawSetNull = true
		}
		if !sem.Cascade {
			allCascade = false
		}
	}
	if allCascade {
		return cascadeDeleteSemantics()
	}
	if sawSetNull {
		return setNullDeleteSemantics()
	}
	return restrictDeleteSemantics()
}

func FieldReferencesParent(field models.FieldMetadata, parentObjectAPIName string) bool {
	parentObjectAPIName = strings.TrimSpace(parentObjectAPIName)
	if parentObjectAPIName == "" {
		return false
	}

	sem := models.ResolveFieldRelationshipSemantics(&field, models.LifecyclePersistedRead)
	if !sem.IsRelationship {
		return false
	}
	for _, ref := range sem.ReferenceTargets {
		if strings.EqualFold(strings.TrimSpace(ref), parentObjectAPIName) {
			return true
		}
	}
	return false
}

// ResolveEffectiveRelationships is the single resolver for a child object's
// effective relationship topology. Base lookup/master-detail edges derive from
// live field metadata; explicit _System_Relationship rows are overlay-only and
// only apply when they refer to a backing field-derived edge.
func ResolveEffectiveRelationships(schema *models.ObjectMetadata, explicitRows []*models.Relationship) []*models.Relationship {
	base := buildFieldDerivedRelationships(schema)
	overlayRelationshipConfig(base, explicitRows)
	return cloneRelationships(base)
}

func buildFieldDerivedRelationships(schema *models.ObjectMetadata) map[string]*models.Relationship {
	byKey := make(map[string]*models.Relationship)
	if schema == nil {
		return byKey
	}

	for _, field := range schema.Fields {
		sem := models.ResolveFieldRelationshipSemantics(&field, models.LifecyclePersistedRead)
		if !sem.IsRelationship || len(sem.ReferenceTargets) == 0 {
			continue
		}

		relationshipName := relationshipNameForField(field)

		relationshipType := string(sem.RelationshipKind)
		if sem.IsMasterDetail {
			relationshipType = string(constants.FieldTypeMasterDetail)
		}

		deleteSemantics := ResolveDeleteSemantics(field, nil)

		for _, parentObjectAPIName := range sem.ReferenceTargets {
			key := relationshipCacheKey(schema.APIName, field.APIName, parentObjectAPIName)
			byKey[key] = &models.Relationship{
				ChildObjectAPIName:  schema.APIName,
				ParentObjectAPIName: parentObjectAPIName,
				FieldAPIName:        field.APIName,
				RelationshipName:    relationshipName,
				RelationshipType:    relationshipType,
				IsSharingParent:     sem.IsMasterDetail,
				CascadeDelete:       deleteSemantics.Cascade,
				RestrictedDelete:    deleteSemantics.Restricted,
			}
		}
	}

	return byKey
}

func relationshipNameForField(field models.FieldMetadata) string {
	if field.RelationshipName != nil {
		if rel := strings.TrimSpace(*field.RelationshipName); rel != "" {
			return rel
		}
	}
	return deriveRelationshipName(field.APIName)
}

func deriveRelationshipName(apiName string) string {
	switch {
	case strings.HasSuffix(apiName, "__c"):
		return strings.TrimSuffix(apiName, "__c") + "__r"
	case strings.HasSuffix(apiName, "Id"):
		return strings.TrimSuffix(apiName, "Id")
	default:
		return apiName
	}
}

func overlayRelationshipConfig(base map[string]*models.Relationship, overrides []*models.Relationship) {
	for _, override := range overrides {
		if override == nil {
			continue
		}

		key := relationshipCacheKey(override.ChildObjectAPIName, override.FieldAPIName, override.ParentObjectAPIName)
		rel, ok := base[key]
		if !ok {
			continue
		}

		if strings.TrimSpace(override.RelationshipName) != "" {
			rel.RelationshipName = override.RelationshipName
		}
		// Field-owned topology stays authoritative. Explicit rows can decorate an
		// edge, but they cannot retype a lookup/master-detail relationship.
		if override.IsSharingParent {
			rel.IsSharingParent = true
		}
		if override.CascadeDelete {
			rel.CascadeDelete = true
			rel.RestrictedDelete = false
		}
		if override.RestrictedDelete {
			rel.RestrictedDelete = true
			rel.CascadeDelete = false
		}
		if override.RelatedListLabel != nil {
			rel.RelatedListLabel = override.RelatedListLabel
		}
		if override.RelatedListFields != nil {
			rel.RelatedListFields = override.RelatedListFields
		}
	}
}

func cloneRelationships(base map[string]*models.Relationship) []*models.Relationship {
	rels := make([]*models.Relationship, 0, len(base))
	for _, rel := range base {
		if rel == nil {
			continue
		}
		copyRel := *rel
		rels = append(rels, &copyRel)
	}
	sort.Slice(rels, func(i, j int) bool {
		if rels[i].FieldAPIName != rels[j].FieldAPIName {
			return rels[i].FieldAPIName < rels[j].FieldAPIName
		}
		return rels[i].ParentObjectAPIName < rels[j].ParentObjectAPIName
	})
	return rels
}
