package catalogreconcile

import (
	"context"
	"fmt"

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

// PageLayoutResolutionMetadataReader supplies the complete neutral closure
// needed by the caller-effective resolver. Implementations must read every
// fact through the transaction carried by ctx.
type PageLayoutResolutionMetadataReader interface {
	LayoutAuthoringMetadataReader
	RecordTypeMetadataReader
	GetRuntimeRelatedListRules(context.Context) ([]metadata.RelatedListRule, error)
}

func appendPageLayoutResolutionDefinitions(
	ctx context.Context,
	reader PageLayoutResolutionMetadataReader,
	revision catalogsnapshot.CatalogRevision,
	objects []*models.ObjectMetadata,
	input *catalogsnapshot.CompileInput,
	includeAuthoringAggregates bool,
) error {
	if reader == nil || input == nil {
		return fmt.Errorf("page-layout resolution publication requires reader and compiler input")
	}
	if err := appendStoredLayoutDefinitions(ctx, reader, revision, objects, input, includeAuthoringAggregates, true); err != nil {
		return err
	}

	if bulkReader, ok := reader.(BulkRecordTypeMetadataReader); ok {
		recordTypes, err := bulkReader.GetAllRecordTypes(ctx)
		if err != nil {
			return fmt.Errorf("read page-layout record types: %w", err)
		}
		if err := appendPageLayoutRecordTypes(revision, recordTypes, input); err != nil {
			return err
		}
	} else {
		for _, object := range objects {
			recordTypes, err := reader.GetRecordTypes(ctx, object.APIName)
			if err != nil {
				return fmt.Errorf("read page-layout record types for %q: %w", object.APIName, err)
			}
			if err := appendPageLayoutRecordTypes(revision, recordTypes, input); err != nil {
				return err
			}
		}
	}

	rules, err := reader.GetRuntimeRelatedListRules(ctx)
	if err != nil {
		return fmt.Errorf("read effective related-list resolutions: %w", err)
	}
	for _, rule := range rules {
		if rule.Resolution == nil {
			continue
		}
		resolution := rule.Resolution
		input.RelatedListResolutions = append(input.RelatedListResolutions, catalogsnapshot.RelatedListResolutionInput{
			CatalogRevision:    revision,
			ParentObjectID:     rule.ParentObjectAPI,
			DisplayObjectID:    rule.ChildObjectAPI,
			DisplayLookupField: rule.LookupField,
			Query: catalogsnapshot.PageLayoutResolvedQueryTarget{
				ObjectID:      resolution.Query.ObjectApiName,
				LookupField:   resolution.Query.LookupField,
				ParentFieldID: resolution.Query.ParentFieldApiName,
			},
			Detail: catalogsnapshot.PageLayoutResolvedDetailTarget{
				ObjectID: resolution.Detail.ObjectApiName,
			},
			ListTarget: catalogsnapshot.PageLayoutResolvedListTarget{
				ObjectID:      resolution.List.ObjectApiName,
				FilterField:   resolution.List.FilterField,
				ParentFieldID: resolution.List.ParentFieldApiName,
			},
			Create: catalogsnapshot.PageLayoutResolvedCreateTarget{
				Kind:            resolution.Create.Kind,
				ObjectID:        resolution.Create.ObjectApiName,
				PrefillMappings: cloneStringMap(resolution.Create.PrefillMappings),
			},
		})
	}
	return nil
}

func appendPageLayoutRecordTypes(
	revision catalogsnapshot.CatalogRevision,
	recordTypes []*models.RecordType,
	input *catalogsnapshot.CompileInput,
) error {
	for index, recordType := range recordTypes {
		if recordType == nil {
			return fmt.Errorf("page-layout record type[%d] is nil", index)
		}
		if !recordType.IsActive {
			continue
		}
		input.PageLayoutRecordTypes = append(input.PageLayoutRecordTypes, catalogsnapshot.PageLayoutRecordTypeInput{
			CatalogRevision: revision,
			ObjectAPIName:   recordType.ObjectAPIName,
			RecordTypeID:    recordType.ID,
		})
	}
	return nil
}

func cloneStringMap(values map[string]string) map[string]string {
	if len(values) == 0 {
		return nil
	}
	result := make(map[string]string, len(values))
	for key, value := range values {
		result[key] = value
	}
	return result
}
