package sfcrmappservice

import (
	"errors"
	"reflect"
	"strings"

	catalogsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	permissionsemantic "github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/effectivepermissionsv1"
)

// Resolve shared metadata actions before selecting permissions or projecting a
// layout. Standard placements belong to the separate invocable/host action
// namespace. An inline action without an object definition remains layout-owned;
// once an object definition exists, the layout may not redefine its behavior.
func resolveLayoutActionDefinitions(layout *appPageLayout, view catalogsemantic.AppMetadataViewV3) error {
	needsDefinitions := false
	for _, placements := range [][]appLayoutAction{layout.HeaderActions, layout.QuickActions} {
		for _, placement := range placements {
			needsDefinitions = needsDefinitions || placement.Type != "Standard"
		}
	}
	if !needsDefinitions {
		return nil
	}
	actions, err := objectActionsFromView(view, layout.ObjectAPIName)
	if err != nil {
		var failure *permissionsemantic.Failure
		if !errors.As(err, &failure) || failure.Category != permissionsemantic.FailureMissing {
			return err
		}
	}
	definitions := make(map[string]effectiveObjectActionSpec, len(actions))
	for _, action := range actions {
		definitions[strings.ToLower(action.Name)] = action
	}
	for _, placements := range [][]appLayoutAction{layout.HeaderActions, layout.QuickActions} {
		for index := range placements {
			placement := &placements[index]
			if placement.Type == "Standard" {
				continue
			}
			definition, found := definitions[strings.ToLower(placement.Name)]
			if !found {
				if placement.Type == "QuickAction" {
					return unavailablePageLayout()
				}
				continue
			}
			if definition.Type == "Standard" || definition.Type == "QuickAction" || placement.Component != "" ||
				(placement.Type != "QuickAction" && placement.Type != definition.Type) ||
				(placement.TargetObject != "" && placement.TargetObject != quickActionTargetObject(definition)) ||
				!actionConfigAgrees(placement.Config, definition.Config) {
				return unavailablePageLayout()
			}
			placement.Type = definition.Type
			placement.TargetObject = definition.TargetObject
			placement.Config = definition.Config
			if placement.Label == "" {
				placement.Label = definition.Label
			}
			if placement.Icon == "" {
				placement.Icon = definition.Icon
			}
		}
	}
	return nil
}

// Old placements may repeat a subset of the definition. Accept matching copies,
// but never merge behavior from two authors or interpret a conflict as read-only.
func actionConfigAgrees(placement, definition map[string]any) bool {
	for key, value := range placement {
		canonical, exists := definition[key]
		if !exists {
			return false
		}
		if nested, ok := value.(map[string]any); ok {
			canonicalMap, ok := canonical.(map[string]any)
			if !ok || !actionConfigAgrees(nested, canonicalMap) {
				return false
			}
		} else if !reflect.DeepEqual(value, canonical) {
			return false
		}
	}
	return true
}
