package runtimeassembly

import (
	"context"
	"errors"

	"github.com/nexuscrm/events"
	"github.com/nexuscrm/kernel/pkg/ports"
	"github.com/nexuscrm/metadata"
	"github.com/nexuscrm/shared/pkg/models"
)

// MetadataAdminReadRuntime is the narrow repository-backed read graph used by
// metadata administration surfaces. It deliberately exposes only the neutral
// ContractService and has no schema manager, event listener, worker, or
// mutation owner.
type MetadataAdminReadRuntime struct {
	Reads *metadataAdminReadProjection
}

// metadataAdminReadProjection keeps the cache-backed object index coherent
// with the repository-backed layout reads. Administration traffic is low
// volume, so each object-index read refreshes the private MetadataService
// before projecting its cache instead of serving a process-lifetime snapshot.
type metadataAdminReadProjection struct {
	reads    *metadata.ContractService
	metadata *metadata.MetadataService
}

func NewMetadataAdminReadRuntime(repository ports.MetadataRepository) (*MetadataAdminReadRuntime, error) {
	if repository == nil {
		return nil, errors.New("metadata admin-read runtime: metadata repository is required")
	}
	service := metadata.NewMetadataService(repository, nil, &events.NoOpEventBus{})
	reads := metadata.NewContractService(service, nil)
	return &MetadataAdminReadRuntime{
		Reads: &metadataAdminReadProjection{reads: reads, metadata: service},
	}, nil
}

// Prepare proves the current MetadataService can load its complete object
// cache before Runtime advertises the capability. ListObjects is cache-backed;
// without this startup fence a repository failure could look like an empty
// administration index.
func (runtime *MetadataAdminReadRuntime) Prepare(ctx context.Context) error {
	if runtime == nil || runtime.Reads == nil || runtime.Reads.metadata == nil || runtime.Reads.reads == nil {
		return errors.New("metadata admin-read runtime: graph is incomplete")
	}
	return runtime.Reads.metadata.RefreshCache(ctx)
}

func (projection *metadataAdminReadProjection) ListObjects(ctx context.Context) ([]models.ObjectMetadata, error) {
	if err := projection.metadata.RefreshCache(ctx); err != nil {
		return nil, err
	}
	return projection.reads.ListObjects(ctx)
}

func (projection *metadataAdminReadProjection) ListLayouts(ctx context.Context, objectName string) ([]*models.PageLayout, error) {
	return projection.reads.ListLayouts(ctx, objectName)
}

func (projection *metadataAdminReadProjection) ListLayoutAssignments(ctx context.Context, objectName string) ([]*models.LayoutAssignment, error) {
	return projection.reads.ListLayoutAssignments(ctx, objectName)
}

func (projection *metadataAdminReadProjection) GetLayoutForAuthoring(ctx context.Context, layoutID string) (*models.PageLayout, error) {
	return projection.reads.GetLayoutForAuthoring(ctx, layoutID)
}

func (projection *metadataAdminReadProjection) ListValidationRules(ctx context.Context, objectName string) ([]*models.ValidationRule, error) {
	return projection.metadata.GetValidationRulesStrict(ctx, objectName)
}
