package catalogsnapshot

import (
	"fmt"

	"github.com/nexuscrm/kernel/pkg/schemautil"
	"github.com/nexuscrm/shared/pkg/fieldcapabilities"
	"github.com/nexuscrm/shared/pkg/models"
)

// CreateRequiredInputFields returns the static create requirements of the
// effective object closure, after the same write-protection and server-default
// derivation used by record mutation. Each canonical object name has a non-nil
// list. Returned values are independent of the immutable source and require no
// live reads.
func (m EffectiveMetadata) CreateRequiredInputFields() (map[string][]string, error) {
	if len(m.bytes) == 0 {
		return nil, fmt.Errorf("effective metadata is unavailable")
	}
	var doc catalogDocument
	if err := decodeStrict(m.bytes, &doc); err != nil {
		return nil, fmt.Errorf("decode effective metadata create requirements: %w", err)
	}
	objects := make(map[string]*models.ObjectMetadata, len(doc.Objects))
	for _, record := range doc.Objects {
		object, err := reconstructCatalogObject(record)
		if err != nil {
			return nil, err
		}
		objects[foldIdentity(object.APIName)] = object
	}
	objects, err := createInputObjects(objects, doc.Mutation)
	if err != nil {
		return nil, err
	}
	result := make(map[string][]string, len(objects))
	for _, object := range objects {
		result[object.APIName] = schemautil.RequiredCreateInputFields(object)
	}
	return result, nil
}

// createInputObjects preserves authored Catalog bytes while exposing the same
// effective field facts read by runtimeRequiredCreateFields. Only field slices
// are copied: capability and default derivation change no nested schema metadata.
func createInputObjects(source map[string]*models.ObjectMetadata, mutation *mutationDefinitionDocument) (map[string]*models.ObjectMetadata, error) {
	objects := make(map[string]*models.ObjectMetadata, len(source))
	for key, object := range source {
		copy := *object
		copy.Fields = append([]models.FieldMetadata(nil), object.Fields...)
		copy.FieldsMap = nil
		for i := range copy.Fields {
			field := &copy.Fields[i]
			field.Createable, field.Updateable = fieldcapabilities.ApplyWriteProtectionPolicy(
				field.Createable, field.Updateable, field.WriteProtectionPolicy,
			)
		}
		objects[key] = &copy
	}
	err := applyAutoNumberDefaultedFacts(mutation, mutation, func(name string) bool {
		return objects[foldIdentity(name)] != nil
	}, func(rule AutoNumberRule, defaulted bool) error {
		object := objects[foldIdentity(rule.ObjectAPIName)]
		if object == nil {
			return fmt.Errorf("autonumber rule %q references absent object %q", rule.FieldID, rule.ObjectAPIName)
		}
		for i := range object.Fields {
			field := &object.Fields[i]
			if foldIdentity(field.APIName) == foldIdentity(rule.FieldAPIName) && foldIdentity(field.ID) == foldIdentity(rule.FieldID) {
				field.DefaultedOnCreate = defaulted
				return nil
			}
		}
		return fmt.Errorf("autonumber rule %q does not identify %s.%s", rule.FieldID, rule.ObjectAPIName, rule.FieldAPIName)
	})
	return objects, err
}
