package packs

import (
	"fmt"
	"strings"
)

type sourceBackedObjectSpecBuildState struct {
	parsedByBusinessKey   map[string][]ObjectSpec
	indexByObjectAPIName  map[string]sourceBackedObjectSpecTarget
	packSpecs             []ObjectSpec
	overlaidByBusinessKey map[string]map[string]struct{}
}

func (p *SourceBackedPack) collectSourceBackedObjectSpecBuildState(ordered []ManifestComponent) (sourceBackedObjectSpecBuildState, error) {
	state := sourceBackedObjectSpecBuildState{
		parsedByBusinessKey:  make(map[string][]ObjectSpec),
		indexByObjectAPIName: make(map[string]sourceBackedObjectSpecTarget),
		packSpecs:            make([]ObjectSpec, 0),
	}
	objectSpecBusinessKeys := make([]string, 0)
	for _, component := range ordered {
		if component.Kind != ComponentKindObjectSpec {
			continue
		}
		payload, err := sourceBackedComponentPayload(p.source, component)
		if err != nil {
			return sourceBackedObjectSpecBuildState{}, fmt.Errorf("%s %s payload: %w", component.Kind, component.BusinessKey, err)
		}
		specs, err := parseObjectSpecsJSON(payload, componentPayloadLabel(component))
		if err != nil {
			return sourceBackedObjectSpecBuildState{}, fmt.Errorf("%s %s: %w", component.Kind, component.BusinessKey, err)
		}
		if err := rejectObjectSpecSearchAllDefaultFieldsPayload(payload, componentPayloadLabel(component)); err != nil {
			return sourceBackedObjectSpecBuildState{}, err
		}
		if err := validateSourceBackedObjectSpecAccessPolicySidecarSurface(specs, componentPayloadLabel(component)); err != nil {
			return sourceBackedObjectSpecBuildState{}, err
		}
		businessKey := strings.TrimSpace(component.BusinessKey)
		state.parsedByBusinessKey[businessKey] = specs
		objectSpecBusinessKeys = append(objectSpecBusinessKeys, businessKey)
		state.packSpecs = append(state.packSpecs, specs...)
		for _, spec := range specs {
			objectAPIName := strings.ToLower(strings.TrimSpace(spec.APIName))
			if objectAPIName == "" {
				continue
			}
			state.indexByObjectAPIName[objectAPIName] = sourceBackedObjectSpecTarget{
				businessKey:           strings.TrimSpace(component.BusinessKey),
				manageability:         component.Manageability,
				objectAPIName:         strings.TrimSpace(spec.APIName),
				hasRawChildCloneRules: len(spec.ChildCloneRules) > 0,
				hasRawRecordMutation:  spec.AccessPolicy != nil && spec.AccessPolicy.RecordMutation != nil,
				hasRawLifecycle:       spec.AccessPolicy != nil && spec.AccessPolicy.Lifecycle != nil,
			}
		}
	}
	overlaidObjectSpecs, err := overlaySourceBackedChildCloneRules(p.source, ordered, state.parsedByBusinessKey, state.indexByObjectAPIName)
	if err != nil {
		return sourceBackedObjectSpecBuildState{}, err
	}
	state.overlaidByBusinessKey = overlaidObjectSpecs
	state.packSpecs = state.packSpecs[:0]
	for _, businessKey := range objectSpecBusinessKeys {
		state.packSpecs = append(state.packSpecs, state.parsedByBusinessKey[businessKey]...)
	}
	if err := validateSourceBackedListFieldsSingleObjectSpecOwnership(state.parsedByBusinessKey); err != nil {
		return sourceBackedObjectSpecBuildState{}, err
	}
	if err := ValidateChildRelationshipDeclarations(state.packSpecs); err != nil {
		return sourceBackedObjectSpecBuildState{}, fmt.Errorf("childRelationshipName validation: %w", err)
	}
	return state, nil
}
