package catalogreconcile

import (
	"context"
	"fmt"
	"slices"
	"sort"
	"strings"

	"github.com/nexuscrm/metadata/catalogsnapshot"
	consumerv1 "github.com/pingcap-inc/nexuscrm-contract/pkg/catalog/consumerv1"
	"github.com/pingcap-inc/nexuscrm-contract/pkg/runtime/outboundintentv1"
	"github.com/pingcap-inc/nexuscrm-controlplane/catalogapp"
)

func validateOutboundMailRouteConfigs(config Config) error {
	seen := make(map[string]struct{}, len(config.OutboundMailRoutes))
	for index, route := range config.OutboundMailRoutes {
		path := fmt.Sprintf("Catalog reconcile outboundMailRoutes[%d]", index)
		for name, value := range map[string]string{
			"authorizedWorkloadId":      route.AuthorizedWorkloadID,
			"connectorOwnerKey":         route.ConnectorOwnerKey,
			"flowDeveloperName":         route.FlowDeveloperName,
			"flowActionName":            route.FlowActionName,
			"sourceObject":              route.SourceObject,
			"actionSlotId":              route.ActionSlotID,
			"destinationSlotId":         route.DestinationSlotID,
			"calloutProfileOwnerPack":   route.CalloutProfileOwnerPack,
			"calloutProfileBusinessKey": route.CalloutProfileBusinessKey,
		} {
			if value == "" || strings.TrimSpace(value) != value {
				return fmt.Errorf("%s %s is required and canonical", path, name)
			}
		}
		if !slices.Contains(config.AuthorizedWorkloadIDs, route.AuthorizedWorkloadID) {
			return fmt.Errorf("%s authorized workload is not declared", path)
		}
		bindingCount := 0
		for _, binding := range config.IntegrationBindings {
			if binding.AuthorizedWorkloadID == route.AuthorizedWorkloadID &&
				binding.Kind == consumerv1.BindingConnectorCalloutProfile &&
				binding.OwnerPack == route.CalloutProfileOwnerPack &&
				binding.BusinessKey == route.CalloutProfileBusinessKey {
				bindingCount++
			}
		}
		if bindingCount != 1 {
			return fmt.Errorf("%s requires exactly one selected callout-profile binding", path)
		}
		if err := catalogsnapshot.ValidateOutboundMailCompletionProjection(&route.MailCompletion); err != nil {
			return fmt.Errorf("%s mailCompletion: %w", path, err)
		}
		key := outboundMailRuntimeLookupKey(route)
		if _, duplicate := seen[key]; duplicate {
			return fmt.Errorf("%s duplicates a SEND_MAIL Runtime lookup", path)
		}
		seen[key] = struct{}{}
	}
	return nil
}

func outboundMailRuntimeLookupKey(route OutboundMailRouteConfig) string {
	return strings.Join([]string{
		strings.ToLower(route.ConnectorOwnerKey),
		strings.ToLower(route.FlowDeveloperName),
		strings.ToLower(route.FlowActionName),
		strings.ToLower(route.SourceObject),
	}, "\x00")
}

func canonicalOutboundMailRouteConfigs(values []OutboundMailRouteConfig) []OutboundMailRouteConfig {
	result := slices.Clone(values)
	sort.Slice(result, func(left, right int) bool {
		return outboundMailRouteConfigKey(result[left]) < outboundMailRouteConfigKey(result[right])
	})
	return result
}

func outboundMailRouteConfigKey(route OutboundMailRouteConfig) string {
	return strings.Join([]string{
		route.AuthorizedWorkloadID,
		route.ConnectorOwnerKey,
		route.FlowDeveloperName,
		route.FlowActionName,
		route.SourceObject,
		route.ActionSlotID,
		route.DestinationSlotID,
	}, "\x00")
}

func appendOutboundMailRoutes(
	ctx context.Context,
	reader MetadataReader,
	revision catalogsnapshot.CatalogRevision,
	definitions []OutboundMailRouteConfig,
	bindings []catalogapp.IntegrationBindingSelection,
	input *catalogsnapshot.CompileInput,
) error {
	if len(definitions) == 0 {
		return nil
	}
	connectorReader, ok := reader.(ConnectorExecutionMetadataReader)
	if !ok {
		return fmt.Errorf("outbound mail route requires the connector execution metadata reader")
	}
	definitions = canonicalOutboundMailRouteConfigs(definitions)
	selectedBindings := make([]catalogapp.IntegrationBindingSelection, len(definitions))
	profilesToValidate := make([]catalogapp.IntegrationBindingSelection, 0, len(definitions))
	validatedProfiles := make(map[string]struct{}, len(definitions))
	for index, definition := range definitions {
		var selected []catalogapp.IntegrationBindingSelection
		for _, candidate := range bindings {
			binding := candidate.Binding
			if candidate.AuthorizedWorkloadID != definition.AuthorizedWorkloadID ||
				binding.Kind != consumerv1.BindingConnectorCalloutProfile ||
				binding.OwnerPack != definition.CalloutProfileOwnerPack ||
				binding.BusinessKey != definition.CalloutProfileBusinessKey {
				continue
			}
			selected = append(selected, candidate)
		}
		if len(selected) != 1 {
			return fmt.Errorf(
				"outbound mail route %s/%s/%s requires one exact callout profile, got %d",
				definition.FlowDeveloperName, definition.FlowActionName, definition.SourceObject, len(selected),
			)
		}
		selectedBindings[index] = selected[0]
		validationKey := selected[0].AuthorizedWorkloadID + "\x00" +
			integrationBindingComponentIdentity(selected[0].Binding)
		if _, validated := validatedProfiles[validationKey]; !validated {
			profilesToValidate = append(profilesToValidate, selected[0])
			validatedProfiles[validationKey] = struct{}{}
		}
	}
	if _, err := resolveConnectorFinalizationProfiles(ctx, connectorReader, profilesToValidate); err != nil {
		return fmt.Errorf("validate outbound mail callout profile authority: %w", err)
	}
	for index, definition := range definitions {
		binding := selectedBindings[index].Binding
		input.OutboundIntentRoutes = append(input.OutboundIntentRoutes, catalogsnapshot.OutboundIntentRouteInput{
			CatalogRevision: revision,
			Route: catalogsnapshot.OutboundIntentRoute{
				AuthorizedWorkloadID: definition.AuthorizedWorkloadID,
				ConnectorOwnerKey:    definition.ConnectorOwnerKey,
				FlowDeveloperName:    definition.FlowDeveloperName, FlowActionName: definition.FlowActionName,
				SourceObject: definition.SourceObject, ActionSlotID: definition.ActionSlotID,
				DestinationSlotID: definition.DestinationSlotID, Operation: outboundintentv1.OperationSendMail,
				Bindings: []catalogsnapshot.IntegrationBindingReference{{
					Kind: binding.Kind, OwnerPack: binding.OwnerPack,
					PackInstallationID: binding.PackInstallationID, PackPayloadDigest: binding.PackPayloadDigest,
					BusinessKey: binding.BusinessKey, DefinitionVersion: binding.DefinitionVersion,
				}},
				MailCompletion: cloneMailCompletion(definition.MailCompletion),
			},
		})
	}
	sort.Slice(input.OutboundIntentRoutes, func(left, right int) bool {
		l, r := input.OutboundIntentRoutes[left].Route, input.OutboundIntentRoutes[right].Route
		return outboundIntentRouteSortKey(l) < outboundIntentRouteSortKey(r)
	})
	return nil
}

func outboundIntentRouteSortKey(route catalogsnapshot.OutboundIntentRoute) string {
	return strings.Join([]string{
		string(route.Operation),
		route.AuthorizedWorkloadID,
		route.ConnectorOwnerKey,
		route.FlowDeveloperName,
		route.FlowActionName,
		route.SourceObject,
		route.ActionSlotID,
		route.DestinationSlotID,
	}, "\x00")
}

func cloneMailCompletion(value catalogsnapshot.OutboundMailCompletionProjection) *catalogsnapshot.OutboundMailCompletionProjection {
	copy := value
	return &copy
}
