package catalogreconcile

import (
	"context"
	"fmt"
	"strings"

	"github.com/nexuscrm/metadata/catalogsnapshot"
	"github.com/nexuscrm/shared/pkg/models"
)

// LayoutAuthoringMetadataReader supplies the stored Layout aggregate facts.
// Unlike caller-effective resolution, authoring preserves Role selectors and
// does not require record-type or related-list resolution authority.
type LayoutAuthoringMetadataReader interface {
	// Read the complete stored facts, including empty collections and legacy
	// Layout kinds, from the publication transaction. Rendering readers are
	// deliberately insufficient for this contract.
	ReadLayoutAuthoringAggregate(context.Context, string) (*models.LayoutAuthoringAggregate, error)
}

func appendLayoutAuthoringDefinitions(
	ctx context.Context,
	reader LayoutAuthoringMetadataReader,
	revision catalogsnapshot.CatalogRevision,
	objects []*models.ObjectMetadata,
	input *catalogsnapshot.CompileInput,
) error {
	return appendStoredLayoutDefinitions(ctx, reader, revision, objects, input, true, false)
}

// appendStoredLayoutDefinitions is the single Controlplane projection of
// stored Layout rows. Caller-effective resolution may impose additional
// security closure, but it must not create a second stored-fact reader.
func appendStoredLayoutDefinitions(
	ctx context.Context,
	reader LayoutAuthoringMetadataReader,
	revision catalogsnapshot.CatalogRevision,
	objects []*models.ObjectMetadata,
	input *catalogsnapshot.CompileInput,
	includeAuthoringAggregates bool,
	includeEffectiveResolution bool,
) error {
	if reader == nil || input == nil {
		return fmt.Errorf("stored Layout publication requires reader and compiler input")
	}

	for _, object := range objects {
		if object == nil {
			return fmt.Errorf("stored Layout object is nil")
		}
		objectName := strings.TrimSpace(object.APIName)
		aggregate, err := reader.ReadLayoutAuthoringAggregate(ctx, objectName)
		if err != nil {
			return fmt.Errorf("read stored Layout aggregate for %q: %w", objectName, err)
		}
		if aggregate == nil || strings.TrimSpace(aggregate.ObjectAPIName) != objectName {
			return fmt.Errorf("stored Layout aggregate for %q is invalid", objectName)
		}
		if includeAuthoringAggregates {
			input.LayoutAuthoringAggregates = append(
				input.LayoutAuthoringAggregates,
				catalogsnapshot.LayoutAuthoringAggregateInput{
					CatalogRevision: revision,
					Aggregate:       aggregate,
				},
			)
		}
		if !includeEffectiveResolution {
			continue
		}
		for index, layout := range aggregate.Layouts {
			if layout == nil {
				return fmt.Errorf("page layout %q[%d] is nil", objectName, index)
			}
			// Preserve the rendering eligibility of MetadataRepository.GetLayouts.
			// The complete aggregate above still retains legacy Create documents;
			// selecting authoring must not widen caller-effective resolution.
			if strings.EqualFold(layout.Type, "Create") {
				continue
			}
			input.PageLayouts = append(input.PageLayouts, catalogsnapshot.PageLayoutInput{
				CatalogRevision: revision,
				Layout:          layout,
			})
		}
		for index, assignment := range aggregate.Assignments {
			if assignment == nil {
				return fmt.Errorf("page-layout assignment %q[%d] is nil", objectName, index)
			}
			// Caller-effective resolution cannot interpret Role selectors without
			// exact routing-security authority. Stored authoring does not resolve
			// them; it preserves the selector as an authored fact.
			if assignment.RoleID != nil {
				return fmt.Errorf(
					"page-layout assignment %q[%d] requires exact routing-security authority",
					objectName,
					index,
				)
			}
			input.LayoutAssignments = append(input.LayoutAssignments, catalogsnapshot.LayoutAssignmentInput{
				CatalogRevision: revision,
				Assignment:      assignment,
			})
		}
	}
	return nil
}
