package layoutauthoringv2

import (
	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
	historysemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/queryv1"
	"strings"
)

// HistoryRelatedListProjection binds application presentation names to the
// existing record-history query result for Request.ObjectID. It is assembled by
// the calling application, never a claim that an arbitrary object schema exists.
// Runtime must validate the transaction-current parent, give physical objects precedence, and accept only these closed, read-only history columns. These
// request-local bindings also apply to preserved, cloned, and restored layouts.
// No binding grants record access or changes the stored Layout document.
type HistoryRelatedListProjection struct {
	ObjectID string                                   `json:"object_id"`
	Columns  map[string]historysemantic.HistoryColumn `json:"columns"`
}

const MaxHistoryRelatedListProjections = 32

func ValidateHistoryRelatedListProjections(parent string, projections []HistoryRelatedListProjection) error {
	if len(projections) > MaxHistoryRelatedListProjections {
		return &common.Failure{Category: common.FailureResourceExhausted, LimitKind: common.LimitOperationCount, Maximum: MaxHistoryRelatedListProjections}
	}
	objects := make(map[string]bool, len(projections))
	for _, projection := range projections {
		key := strings.ToLower(projection.ObjectID)
		if !common.ValidObjectIdentifier(projection.ObjectID) || strings.EqualFold(parent, projection.ObjectID) || objects[key] {
			return invalid(common.InvalidComponentCommandData)
		}
		objects[key] = true
		if len(projection.Columns) > historysemantic.HistoryResultColumnCount {
			return &common.Failure{Category: common.FailureResourceExhausted, LimitKind: common.LimitFieldCount, Maximum: historysemantic.HistoryResultColumnCount}
		}
		if len(projection.Columns) != historysemantic.HistoryResultColumnCount {
			return invalid(common.InvalidComponentCommandData)
		}
		names := make(map[string]bool, 8)
		members := make(map[historysemantic.HistoryColumn]bool, 8)
		for name, member := range projection.Columns {
			if !member.Valid() {
				return invalid(common.InvalidComponentCommandData)
			}

			folded := strings.ToLower(name)
			if !common.ValidObjectIdentifier(name) || names[folded] || members[member] {
				return invalid(common.InvalidComponentCommandData)
			}
			names[folded], members[member] = true, true
		}
	}
	return nil
}
