package metadata

import (
	"sort"
	"strings"

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

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

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 {
		isRelationship := field.Type == constants.FieldTypeReference || field.Type == constants.FieldTypeMasterDetail || field.IsMasterDetail
		if !isRelationship || len(field.ReferenceTo) == 0 {
			continue
		}

		relationshipName := ""
		if field.RelationshipName != nil {
			relationshipName = *field.RelationshipName
		}
		if relationshipName == "" && strings.HasSuffix(field.APIName, "Id") {
			relationshipName = strings.TrimSuffix(field.APIName, "Id")
		}

		relationshipType := string(constants.FieldTypeReference)
		if field.IsMasterDetail || field.Type == constants.FieldTypeMasterDetail {
			relationshipType = string(constants.FieldTypeMasterDetail)
		}

		cascadeDelete := field.IsMasterDetail || field.Type == constants.FieldTypeMasterDetail
		restrictedDelete := false
		if field.DeleteRule != nil {
			cascadeDelete = cascadeDelete || strings.EqualFold(string(*field.DeleteRule), string(constants.DeleteRuleCascade))
			restrictedDelete = strings.EqualFold(string(*field.DeleteRule), string(constants.DeleteRuleRestrict))
		}

		for _, parentObjectAPIName := range field.ReferenceTo {
			key := relationshipCacheKey(schema.APIName, field.APIName, parentObjectAPIName)
			byKey[key] = &models.Relationship{
				ChildObjectAPIName:  schema.APIName,
				ParentObjectAPIName: parentObjectAPIName,
				FieldAPIName:        field.APIName,
				RelationshipName:    relationshipName,
				RelationshipType:    relationshipType,
				IsSharingParent:     field.IsMasterDetail || field.Type == constants.FieldTypeMasterDetail,
				CascadeDelete:       cascadeDelete,
				RestrictedDelete:    restrictedDelete,
			}
		}
	}

	return byKey
}

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 override.RelationshipName != "" {
			rel.RelationshipName = override.RelationshipName
		}
		if override.RelationshipType != "" {
			rel.RelationshipType = override.RelationshipType
		}
		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
}

func (ms *MetadataService) buildRelationshipSnapshot(schemas []*models.ObjectMetadata, schemaMap map[string]*models.ObjectMetadata, explicitRelationships []*models.Relationship) (map[string][]*models.Relationship, map[string][]*models.ObjectMetadata) {
	relationshipsByChild := make(map[string][]*models.Relationship, len(schemas))
	childrenByParent := make(map[string][]*models.ObjectMetadata)
	explicitByChild := make(map[string][]*models.Relationship)

	for _, rel := range explicitRelationships {
		if rel == nil {
			continue
		}
		childKey := strings.ToLower(rel.ChildObjectAPIName)
		explicitByChild[childKey] = append(explicitByChild[childKey], rel)
	}

	for _, schema := range schemas {
		if schema == nil {
			continue
		}

		base := buildFieldDerivedRelationships(schema)
		overlayRelationshipConfig(base, explicitByChild[strings.ToLower(schema.APIName)])
		childKey := strings.ToLower(schema.APIName)
		relationshipsByChild[childKey] = cloneRelationships(base)

		seenParents := make(map[string]bool)
		for _, rel := range relationshipsByChild[childKey] {
			parentKey := strings.ToLower(rel.ParentObjectAPIName)
			if seenParents[parentKey] {
				continue
			}
			seenParents[parentKey] = true

			if childSchema := schemaMap[childKey]; childSchema != nil {
				childrenByParent[parentKey] = append(childrenByParent[parentKey], childSchema)
			}
		}
	}

	return relationshipsByChild, childrenByParent
}
