package pagelayoutresolutionv1

import (
	"context"
	"strings"

	common "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/commonv1"
)

func ValidateObjectListFieldsRequest(ctx context.Context, request ObjectListFieldsRequest) error {
	if err := common.ValidateRequestContext(ctx); err != nil {
		return err
	}
	if len(request.ObjectIDs) > MaxObjectSelectors {
		return exhausted(LimitRecordCount, MaxObjectSelectors)
	}
	if !validObjectListFieldsRequestShape(request) {
		return invalid(InvalidComponentOperation)
	}
	return nil
}

func validObjectListFieldsRequestShape(request ObjectListFieldsRequest) bool {
	if len(request.ObjectIDs) == 0 || len(request.ObjectIDs) > MaxObjectSelectors {
		return false
	}
	if request.MinCatalogRevision != nil && *request.MinCatalogRevision == 0 {
		return false
	}
	previous := ""
	seen := make(map[string]struct{}, len(request.ObjectIDs))
	for _, objectID := range request.ObjectIDs {
		if !common.ValidObjectIdentifier(objectID) || objectID <= previous {
			return false
		}
		key := strings.ToLower(objectID)
		if _, duplicate := seen[key]; duplicate {
			return false
		}
		seen[key] = struct{}{}
		previous = objectID
	}
	return true
}

func ValidateObjectListFieldsResponse(
	request ObjectListFieldsRequest,
	response ObjectListFieldsResponse,
) error {
	if !validObjectListFieldsRequestShape(request) ||
		len(response.Objects) != len(request.ObjectIDs) ||
		!common.ValidReceipts(response.Receipts, CapabilityName) ||
		(request.MinCatalogRevision != nil && response.Receipts.CatalogRevision < *request.MinCatalogRevision) {
		return internal()
	}
	remaining := MaxResolutionProjectionEntries
	for index, object := range response.Objects {
		if object.OrderedFieldIDs == nil || object.ObjectID != request.ObjectIDs[index] {
			return internal()
		}
		if len(object.OrderedFieldIDs) > MaxListFieldsPerObject {
			return exhausted(LimitFieldCount, MaxListFieldsPerObject)
		}
		if len(object.OrderedFieldIDs) > remaining {
			return exhausted(LimitFieldCount, MaxResolutionProjectionEntries)
		}
		remaining -= len(object.OrderedFieldIDs)
		seen := make(map[string]struct{}, len(object.OrderedFieldIDs))
		for _, fieldID := range object.OrderedFieldIDs {
			if !common.ValidFieldIdentifier(fieldID) {
				return internal()
			}
			key := strings.ToLower(fieldID)
			if _, duplicate := seen[key]; duplicate {
				return internal()
			}
			seen[key] = struct{}{}
		}
	}
	return nil
}
