package packs

import (
	"encoding/json"
	"fmt"
	"reflect"

	kernelconnector "github.com/nexuscrm/kernel/pkg/connector"
	"github.com/nexuscrm/metadata"
	connector "github.com/pingcap-inc/nexuscrm-contract/pkg/connector"
)

func cloneConnectorInboundHandlerConfigDeclarations(
	declarations []ConnectorInboundHandlerConfigComponentPayload,
) []ConnectorInboundHandlerConfigComponentPayload {
	if declarations == nil {
		return nil
	}
	out := make([]ConnectorInboundHandlerConfigComponentPayload, len(declarations))
	for index, declaration := range declarations {
		out[index] = declaration
		out[index].Config = json.RawMessage(cloneSourceBytes(declaration.Config))
	}
	return out
}

func cloneConnectorPushContractDeclarations(
	declarations []connector.ConnectorPushDurableMetadataContract,
) []connector.ConnectorPushDurableMetadataContract {
	if declarations == nil {
		return nil
	}
	out := make([]connector.ConnectorPushDurableMetadataContract, len(declarations))
	for index, declaration := range declarations {
		out[index] = cloneConnectorPushContractDeclaration(declaration)
	}
	return out
}

func cloneConnectorPushHistoricalReconciliationBindings(
	bindings []kernelconnector.ConnectorPushHistoricalReconciliationBinding,
) []kernelconnector.ConnectorPushHistoricalReconciliationBinding {
	if bindings == nil {
		return nil
	}
	out := make([]kernelconnector.ConnectorPushHistoricalReconciliationBinding, len(bindings))
	for index, binding := range bindings {
		out[index] = binding
		out[index].Contract = cloneConnectorPushContractDeclaration(binding.Contract)
	}
	return out
}

func cloneConnectorPushContractDeclaration(
	declaration connector.ConnectorPushDurableMetadataContract,
) connector.ConnectorPushDurableMetadataContract {
	out := declaration
	out.PreviousActionSlotIDs = cloneSourceSlice(declaration.PreviousActionSlotIDs)
	out.DiagnosticErrorFieldPaths = cloneSourceSlice(declaration.DiagnosticErrorFieldPaths)
	if declaration.CreateConflictResolution != nil {
		resolution := *declaration.CreateConflictResolution
		resolution.StatusCodes = cloneSourceSlice(declaration.CreateConflictResolution.StatusCodes)
		if declaration.CreateConflictResolution.DiagnosticMatch != nil {
			resolution.DiagnosticMatch = make(map[string]string, len(declaration.CreateConflictResolution.DiagnosticMatch))
			for key, value := range declaration.CreateConflictResolution.DiagnosticMatch {
				resolution.DiagnosticMatch[key] = value
			}
		}
		out.CreateConflictResolution = &resolution
	}
	if declaration.Associations != nil {
		out.Associations = make([]connector.ConnectorPushAssociationContract, len(declaration.Associations))
		for index, association := range declaration.Associations {
			out.Associations[index] = cloneConnectorPushAssociationContract(association)
		}
	}
	return out
}

func cloneConnectorPushAssociationContract(
	association connector.ConnectorPushAssociationContract,
) connector.ConnectorPushAssociationContract {
	out := association
	out.TargetPreviousActionSlotIDs = cloneSourceSlice(association.TargetPreviousActionSlotIDs)
	out.InlinePhases = cloneSourceSlice(association.InlinePhases)
	out.CalloutPhases = cloneSourceSlice(association.CalloutPhases)
	out.PayloadTemplate = cloneSourceBackedJSONValue(association.PayloadTemplate)
	out.CalloutBodyTemplate = cloneSourceBackedJSONValue(association.CalloutBodyTemplate)
	if association.Junction != nil {
		junction := *association.Junction
		if association.Junction.Filters != nil {
			junction.Filters = make([]connector.ConnectorPushAssociationJunctionFilter, len(association.Junction.Filters))
			for index, filter := range association.Junction.Filters {
				junction.Filters[index] = filter
				junction.Filters[index].Equals = cloneSourceBackedJSONValue(filter.Equals)
			}
		}
		out.Junction = &junction
	}
	return out
}

// cloneSourceBackedJSONValue snapshots the JSON-shaped interface values
// produced by connector contract parsing. Unsupported mutable values fail
// closed instead of being returned as aliases.
func cloneSourceBackedJSONValue(value any) any {
	switch typed := value.(type) {
	case nil, bool, float64, string, json.Number:
		return typed
	case json.RawMessage:
		return json.RawMessage(cloneSourceBytes(typed))
	case []any:
		out := make([]any, len(typed))
		for index, item := range typed {
			out[index] = cloneSourceBackedJSONValue(item)
		}
		return out
	case map[string]any:
		out := make(map[string]any, len(typed))
		for key, item := range typed {
			out[key] = cloneSourceBackedJSONValue(item)
		}
		return out
	default:
		kind := reflect.TypeOf(value).Kind()
		switch kind {
		case reflect.Bool,
			reflect.Float32, reflect.Float64,
			reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.String,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
			return value
		default:
			panic(fmt.Sprintf("packs: unsupported connector JSON value %T", value))
		}
	}
}

func cloneRuntimeRelatedListRules(rules []metadata.RelatedListRule) []metadata.RelatedListRule {
	if rules == nil {
		return nil
	}
	out := make([]metadata.RelatedListRule, len(rules))
	for index, rule := range rules {
		out[index] = rule
		out[index].Fields = cloneSourceSlice(rule.Fields)
		if rule.Resolution != nil {
			resolution := *rule.Resolution
			if rule.Resolution.Create.PrefillMappings != nil {
				resolution.Create.PrefillMappings = make(map[string]string, len(rule.Resolution.Create.PrefillMappings))
				for key, value := range rule.Resolution.Create.PrefillMappings {
					resolution.Create.PrefillMappings[key] = value
				}
			}
			out[index].Resolution = &resolution
		}
	}
	return out
}
