package catalogreconcile

import (
	"context"
	"encoding/json"
	"fmt"
	"sort"
	"strings"

	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/shared/pkg/models"
	consumerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/catalogapp"
	"github.com/pingcap-inc/nexuscrm-controlplane/componentbehavior"
)

type EffectiveRelatedListRuleMetadataReader interface {
	GetRuntimeRelatedListRules(context.Context) ([]metadata.RelatedListRule, error)
}

type effectiveRelatedListPresentationDocument struct {
	Rules         []metadata.RelatedListRule    `json:"rules"`
	Relationships []effectiveRelatedListColumns `json:"relationships"`
}

type effectiveRelatedListColumns struct {
	ChildObjectAPIName  string  `json:"childObjectApiName"`
	ParentObjectAPIName string  `json:"parentObjectApiName"`
	FieldAPIName        string  `json:"fieldApiName"`
	RelatedListLabel    *string `json:"relatedListLabel,omitempty"`
	RelatedListFields   *string `json:"relatedListFields,omitempty"`
}

// Publish the installed declarations in the same transaction and revision as
// the effective object closure. No process-global rule registry participates.
func buildEffectiveRelatedListPresentationComponent(ctx context.Context, reader EffectiveRelatedListRuleMetadataReader, objectNames []string, relationships []*models.Relationship) (catalogapp.EffectiveMetadataComponent, error) {
	rules, err := reader.GetRuntimeRelatedListRules(ctx)
	if err != nil {
		return catalogapp.EffectiveMetadataComponent{}, fmt.Errorf("read effective related-list rules: %w", err)
	}
	if err := metadata.ValidatePackRelatedListRules(rules); err != nil {
		return catalogapp.EffectiveMetadataComponent{}, fmt.Errorf("validate effective related-list rules: %w", err)
	}
	objects := make(map[string]bool, len(objectNames))
	for _, name := range objectNames {
		objects[strings.ToLower(name)] = true
	}
	selected := make([]metadata.RelatedListRule, 0, len(rules))
	for _, rule := range rules {
		if objects[strings.ToLower(rule.ParentObjectAPI)] {
			selected = append(selected, rule)
		}
	}
	key := func(rule metadata.RelatedListRule) string {
		return strings.ToLower(rule.ParentObjectAPI + "\x00" + rule.ChildObjectAPI + "\x00" + rule.LookupField)
	}
	sort.Slice(selected, func(i, j int) bool { return key(selected[i]) < key(selected[j]) })
	columns := make([]effectiveRelatedListColumns, 0)
	columnKey := func(value effectiveRelatedListColumns) string {
		return strings.ToLower(value.ParentObjectAPIName + "\x00" + value.ChildObjectAPIName + "\x00" + value.FieldAPIName)
	}
	seen := make(map[string]bool)
	for _, relationship := range relationships {
		if relationship == nil {
			return catalogapp.EffectiveMetadataComponent{}, fmt.Errorf("nil effective relationship")
		}
		if !objects[strings.ToLower(relationship.ParentObjectAPIName)] || !objects[strings.ToLower(relationship.ChildObjectAPIName)] {
			continue
		}
		if relationship.RelatedListLabel == nil && relationship.RelatedListFields == nil {
			continue
		}
		column := effectiveRelatedListColumns{
			ChildObjectAPIName:  relationship.ChildObjectAPIName,
			ParentObjectAPIName: relationship.ParentObjectAPIName,
			FieldAPIName:        relationship.FieldAPIName,
			RelatedListLabel:    relationship.RelatedListLabel,
			RelatedListFields:   relationship.RelatedListFields,
		}
		identity := columnKey(column)
		if seen[identity] {
			return catalogapp.EffectiveMetadataComponent{}, fmt.Errorf("duplicate effective relationship presentation")
		}
		seen[identity] = true
		columns = append(columns, column)
	}
	sort.Slice(columns, func(i, j int) bool { return columnKey(columns[i]) < columnKey(columns[j]) })
	encoded, err := json.Marshal(effectiveRelatedListPresentationDocument{Rules: selected, Relationships: columns})
	if err != nil {
		return catalogapp.EffectiveMetadataComponent{}, err
	}
	behavior, err := componentbehavior.CanonicalJSONV1(encoded)
	if err != nil {
		return catalogapp.EffectiveMetadataComponent{}, err
	}
	return catalogapp.EffectiveMetadataComponent{
		ComponentKind:          consumerv1.EffectiveRelatedListPresentationComponentKind,
		ComponentFormatVersion: 1,
		BusinessKey:            consumerv1.EffectiveRelatedListPresentationComponentBusinessKey,
		ContentDigest:          consumerv1.ComputeEffectiveMetadataComponentDigest(behavior),
		CanonicalBehavior:      behavior,
	}, nil
}
