// Copyright 2025 Microsoft Corporation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package validation

import (
	"context"
	"fmt"
	"testing"
	"time"

	"k8s.io/apimachinery/pkg/api/operation"
	"k8s.io/utils/ptr"

	azcorearm "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm"

	"github.com/Azure/ARO-HCP/internal/api/coreapi"
	"github.com/Azure/ARO-HCP/internal/api/metadataapi"
	"github.com/Azure/ARO-HCP/internal/apitesting/coreapitesting"
	"github.com/Azure/ARO-HCP/internal/utils"
)

// testFeatureOptions creates validation options from feature names,
// matching production behavior by normalizing to lowercase.
func testFeatureOptions(names ...string) []string {
	features := make([]coreapi.Feature, len(names))
	for i, name := range names {
		features[i] = coreapi.Feature{
			Name:  ptr.To(name),
			State: ptr.To("Registered"),
		}
	}
	return AFECsToValidationOptions(features)
}

// Comprehensive tests for ValidateClusterCreate
// using a subscription without AllowNonStableChannel flags
func TestValidateClusterCreate(t *testing.T) {
	ctx := context.Background()

	tests := []struct {
		name         string
		cluster      *coreapi.HCPOpenShiftCluster
		opOptions    []string
		expectErrors []utils.ExpectedError
	}{
		{
			name:         "valid cluster - create",
			cluster:      createValidCluster(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid cluster with identity - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// The helper already sets up a valid identity, so just return it
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "OpenShift 5 rejected on create without experimental release features (no prior version id)",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "5.0"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "OpenShift v5 and above is not supported", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "OpenShift 5 allowed with experimental release features - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "5.0"
				return c
			}(),
			opOptions:    testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "invalid DNS prefix - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.DNS.BaseDomainPrefix = "Invalid-Name"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be a valid DNS RFC 1035 label", FieldPath: "customerProperties.dns.baseDomainPrefix"},
			},
		},
		{
			name: "invalid network type - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.NetworkType = "InvalidType"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Unsupported value", FieldPath: "customerProperties.network.networkType"},
			},
		},
		{
			name: "invalid Pod CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.PodCIDR = "invalid-cidr"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "invalid CIDR address", FieldPath: "customerProperties.network.podCidr"},
			},
		},
		{
			name: "invalid Service CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.ServiceCIDR = "300.0.0.0/16"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "invalid CIDR address", FieldPath: "customerProperties.network.serviceCidr"},
			},
		},
		{
			name: "invalid Machine CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.MachineCIDR = "2001:db8::/32"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "not IPv4", FieldPath: "customerProperties.network.machineCidr"},
			},
		},
		{
			name: "host prefix too small - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.HostPrefix = 22
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be greater than or equal to 23", FieldPath: "customerProperties.network.hostPrefix"},
			},
		},
		{
			name: "host prefix too large - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.HostPrefix = 27
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be less than or equal to 26", FieldPath: "customerProperties.network.hostPrefix"},
			},
		},
		{
			name: "invalid API visibility - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.Visibility = "InvalidVisibility"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Unsupported value", FieldPath: "customerProperties.api.visibility"},
			},
		},
		{
			name: "invalid authorized CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"invalid-cidr"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "empty authorized CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{""}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "empty list authorized CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must have at least 1 items", FieldPath: "customerProperties.api.authorizedCidrs"},
			},
		},
		{
			name: "authorized CIDR with leading whitespace - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{" 10.0.0.0/16"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must not contain extra whitespace", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "authorized CIDR with trailing whitespace - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/16 "}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must not contain extra whitespace", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "authorized CIDR with internal whitespace - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0. 0.0/16"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "valid IPv4 address in authorized CIDRs - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"192.168.1.1"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid CIDR ranges in authorized CIDRs - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "IPv6 address in authorized CIDRs - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"2001:db8::1"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not IPv4", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "IPv6 CIDR in authorized CIDRs - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"2001:db8::/32"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not IPv4", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "invalid CIDR prefix in authorized CIDRs - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/33"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "multiple validation errors in authorized CIDRs - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"", "invalid-cidr", " 10.0.0.0/16", "2001:db8::1"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[1]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[1]"},
				{Message: "must not contain extra whitespace", FieldPath: "customerProperties.api.authorizedCidrs[2]"},
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[2]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[2]"},
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[3]"},
				{Message: "not IPv4", FieldPath: "customerProperties.api.authorizedCidrs[3]"},
			},
		},
		{
			name: "501 unique authorized CIDR blocks - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = makeUniqueCIDRs(501)
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must have at most 500 items", FieldPath: "customerProperties.api.authorizedCidrs"},
			},
		},
		{
			name: "missing subnet ID - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.SubnetID = nil
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "customerProperties.platform.subnetId"},
			},
		},
		{
			name: "invalid outbound type - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.OutboundType = "InvalidType"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Unsupported value", FieldPath: "customerProperties.platform.outboundType"},
			},
		},
		{
			name: "missing network security group ID - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.NetworkSecurityGroupID = nil
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "customerProperties.platform.networkSecurityGroupId"},
			},
		},
		{
			name: "wrong NSG resource type - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.NetworkSecurityGroupID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "resource ID must reference an instance of type", FieldPath: "customerProperties.platform.networkSecurityGroupId"},
			},
		},
		{
			name: "node drain timeout too large - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.NodeDrainTimeoutMinutes = 10081
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be less than or equal to 10080", FieldPath: "customerProperties.nodeDrainTimeoutMinutes"},
			},
		},
		{
			name: "invalid etcd encryption key management mode - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = "InvalidMode"
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = nil
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Unsupported value", FieldPath: "customerProperties.etcd.dataEncryption.keyManagementMode"},
			},
		},
		{
			name: "customer managed without customer managed profile - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = nil
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be specified when", FieldPath: "customerProperties.etcd.dataEncryption.customerManaged"},
			},
		},
		{
			name: "missing kms encryption visibility fails validation - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						// Visibility is omitted (empty string) - EnsureDefaults will fill it in before validation
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "test-version",
						},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{
					Message:   "Required value",
					FieldPath: "customerProperties.etcd.dataEncryption.customerManaged.kms.visibility",
				},
				{
					Message:   "supported values: \"Private\", \"Public\"",
					FieldPath: "customerProperties.etcd.dataEncryption.customerManaged.kms.visibility",
				},
			},
		},
		{
			name: "invalid kms encryption visibility - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: "InvalidVisibility",
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "test-version",
						},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Unsupported value", FieldPath: "customerProperties.etcd.dataEncryption.customerManaged.kms.visibility"},
			},
		},
		{
			name: "valid kms encryption with public visibility - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: metadataapi.KeyVaultVisibilityPublic,
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "test-version",
						},
					},
				}
				return c
			}(),
		},
		{
			name: "valid kms encryption with private visibility - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: metadataapi.KeyVaultVisibilityPrivate,
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "test-version",
						},
					},
				}
				return c
			}(),
		},
		{
			name: "invalid cluster image registry state - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.ClusterImageRegistry.State = "InvalidState"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Unsupported value", FieldPath: "customerProperties.clusterImageRegistry.state"},
			},
		},
		{
			name: "missing user assigned identity name - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
					"": metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity")),
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[]"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities"},
			},
		},
		{
			name: "invalid user assigned identity resource type - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
					"test-operator": metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet")),
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "resource ID must reference an instance of type", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities"},
			},
		},
		{
			name: "missing identity type - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "identity.type"},
				{Message: "Unsupported value", FieldPath: "identity.state"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities"},
			},
		},
		{
			name: "invalid identity type - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: "InvalidType",
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Unsupported value", FieldPath: "identity.state"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities"},
			},
		},
		{
			name: "invalid user assigned identity resource type - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet": {},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "resource ID must reference an instance of type", FieldPath: "identity.userAssignedIdentities"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
			},
		},
		{
			name: "multiple validation errors - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.DNS.BaseDomainPrefix = "Invalid-Name"
				c.CustomerProperties.Network.NetworkType = "InvalidType"
				c.CustomerProperties.API.Visibility = "InvalidVisibility"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be a valid DNS RFC 1035 label", FieldPath: "customerProperties.dns.baseDomainPrefix"},
				{Message: "Unsupported value", FieldPath: "customerProperties.network.networkType"},
				{Message: "Unsupported value", FieldPath: "customerProperties.api.visibility"},
			},
		},
		// Tests for validateOperatorAuthenticationAgainstIdentities
		{
			name: "identity assigned but not used - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				unusedIdentityID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/unused-identity"
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						unusedIdentityID: {},
					},
				}
				// Don't reference the identity in operators
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{}
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ServiceManagedIdentity = nil
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities[/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/unused-identity]"},
			},
		},
		{
			name: "identity used but not assigned - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type:                   coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{},
				}
				// Reference an identity that's not assigned
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
					"test-operator": metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/unassigned-identity")),
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
			},
		},
		{
			name: "identity used multiple times - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				identityID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/shared-identity"
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						identityID: {},
					},
				}
				// Use the same identity in multiple places
				identityResourceID := metadataapi.Must(azcorearm.ParseResourceID(identityID))
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
					"operator1": identityResourceID,
					"operator2": identityResourceID,
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be unique within the cluster", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators"},
				{Message: "identity is used multiple times", FieldPath: "identity.userAssignedIdentities[/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/shared-identity]"},
			},
		},
		{
			name: "duplicate managed identity across data plane operators - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				sharedIdentityID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/shared-dataplane-identity"
				sharedIdentity := metadataapi.Must(azcorearm.ParseResourceID(sharedIdentityID))
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.DataPlaneOperators = map[string]*azcorearm.ResourceID{
					"dataplane-operator-1": sharedIdentity,
					"dataplane-operator-2": sharedIdentity,
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be unique within the cluster", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.dataPlaneOperators"},
			},
		},
		{
			name: "duplicate managed identity between control plane and service managed identity - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				identityID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/shared-identity"
				identityResourceID := metadataapi.Must(azcorearm.ParseResourceID(identityID))
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						identityID: {},
					},
				}
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
					"test-operator": identityResourceID,
				}
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ServiceManagedIdentity = identityResourceID
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be unique within the cluster", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.serviceManagedIdentity"},
				{Message: "identity is used multiple times", FieldPath: "identity.userAssignedIdentities[/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/shared-identity]"},
			},
		},
		{
			name: "data plane operator uses assigned identity - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				identityID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/dataplane-identity"
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						identityID: {},
					},
				}
				// Data plane operators cannot use assigned identities
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.DataPlaneOperators = map[string]*azcorearm.ResourceID{
					"dataplane-operator": metadataapi.Must(azcorearm.ParseResourceID(identityID)),
				}
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "cannot use identity assigned to this resource by .identities.userAssignedIdentities", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.dataPlaneOperators[dataplane-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities[/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/dataplane-identity]"},
			},
		},
		{
			name: "service managed identity used correctly - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				identityID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/service-identity"
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						identityID: {},
					},
				}
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{}
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ServiceManagedIdentity = metadataapi.Must(azcorearm.ParseResourceID(identityID))
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "case insensitive identity matching - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				lowerCaseID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourcegroups/some-resource-group/providers/microsoft.managedidentity/userassignedidentities/test-identity"
				upperCaseID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity"
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						lowerCaseID: {},
					},
				}
				// Reference with different casing should work
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
					"test-operator": metadataapi.Must(azcorearm.ParseResourceID(upperCaseID)),
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		// Tests for validateResourceIDsAgainstClusterID
		{
			name: "managed resource group same as cluster resource group - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// Managed resource group cannot be the same as the cluster's resource group
				c.CustomerProperties.Platform.ManagedResourceGroup = "some-resource-group"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must not be the same resource group name", FieldPath: "customerProperties.platform.subnetId"},
				{Message: "must not be the same resource group name", FieldPath: "customerProperties.platform.networkSecurityGroupId"},
				{Message: "must not be the same resource group name", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
				{Message: "must not be the same resource group name", FieldPath: "customerProperties.platform.managedResourceGroup"},
				{Message: "must not be the same resource group name", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
			},
		},
		{
			name: "subnet in different subscription - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// Subnet in different subscription should fail
				c.CustomerProperties.Platform.SubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/different-sub/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-subnet"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must belong to the same VNet as subnetId", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.subnetId"},
			},
		},
		{
			name: "network security group in different subscription - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.NetworkSecurityGroupID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/different-sub/resourceGroups/test-rg/providers/Microsoft.Network/networkSecurityGroups/test-nsg"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.networkSecurityGroupId"},
			},
		},
		{
			name: "network security group in managed resource group - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.NetworkSecurityGroupID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/managed-rg/providers/Microsoft.Network/networkSecurityGroups/test-nsg"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must not be the same resource group name", FieldPath: "customerProperties.platform.networkSecurityGroupId"},
			},
		},
		{
			name: "vnet integration subnet is optional - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// vnetIntegrationSubnetId is optional for backwards compatibility
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = nil
				return c
			}(),
		},
		{
			name: "valid vnet integration subnet - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// Valid VNet integration subnet in same subscription, different RG from managed RG
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/valid-vnet-integration-subnet"))
				return c
			}(),
		},
		{
			name: "vnet integration subnet in different subscription - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// VNet integration subnet in different subscription should fail
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/different-sub/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-vnet-integration-subnet"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must belong to the same VNet as subnetId", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
			},
		},
		{
			name: "vnet integration subnet in managed resource group - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// VNet integration subnet in managed resource group should fail
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/managed-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-vnet-integration-subnet"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must not be the same resource group name", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
				{Message: "must belong to the same VNet as subnetId", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
			},
		},
		{
			name: "vnet integration subnet in different VNet - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/virtualNetworks/other-vnet/subnets/test-vnet-integration-subnet"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must belong to the same VNet as subnetId", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
			},
		},
		{
			name: "control plane operator identity in wrong location - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// Identity in different subscription
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
					"test-operator": metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/different-sub/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity")),
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities"},
			},
		},
		{
			name: "data plane operator identity validation - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// Data plane operator identity validation
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.DataPlaneOperators = map[string]*azcorearm.ResourceID{
					"dataplane-operator": metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/different-sub/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/dataplane-identity")),
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.dataPlaneOperators[dataplane-operator]"},
			},
		},
		{
			name: "service managed identity validation - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// Service managed identity validation
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ServiceManagedIdentity = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/different-sub/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/service-identity"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.serviceManagedIdentity"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.serviceManagedIdentity"},
			},
		},
		// Tests for network CIDR overlap validation
		{
			name: "machine CIDR overlaps with service CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.MachineCIDR = "10.0.0.0/16"
				c.CustomerProperties.Network.ServiceCIDR = "10.0.1.0/24" // Overlaps with machine CIDR
				c.CustomerProperties.Network.PodCIDR = "10.128.0.0/14"   // No overlap
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "machine CIDR '10.0.0.0/16' and service CIDR '10.0.1.0/24' overlap", FieldPath: "customerProperties.network"},
			},
		},
		{
			name: "machine CIDR overlaps with pod CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.MachineCIDR = "10.0.0.0/16"
				c.CustomerProperties.Network.PodCIDR = "10.0.1.0/24"       // Overlaps with machine CIDR
				c.CustomerProperties.Network.ServiceCIDR = "172.30.0.0/16" // No overlap
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "machine CIDR '10.0.0.0/16' and pod CIDR '10.0.1.0/24' overlap", FieldPath: "customerProperties.network"},
			},
		},
		{
			name: "service CIDR overlaps with pod CIDR - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.MachineCIDR = "192.168.0.0/16" // No overlap
				c.CustomerProperties.Network.ServiceCIDR = "10.0.0.0/16"
				c.CustomerProperties.Network.PodCIDR = "10.0.1.0/24" // Overlaps with service CIDR
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "service CIDR '10.0.0.0/16' and pod CIDR '10.0.1.0/24' overlap", FieldPath: "customerProperties.network"},
			},
		},
		{
			name: "multiple CIDR overlaps - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// All CIDRs overlap with each other
				c.CustomerProperties.Network.MachineCIDR = "10.0.0.0/14"
				c.CustomerProperties.Network.ServiceCIDR = "10.0.0.0/16"
				c.CustomerProperties.Network.PodCIDR = "10.1.0.0/16"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "machine CIDR '10.0.0.0/14' and service CIDR '10.0.0.0/16' overlap", FieldPath: "customerProperties.network"},
				{Message: "machine CIDR '10.0.0.0/14' and pod CIDR '10.1.0.0/16' overlap", FieldPath: "customerProperties.network"},
			},
		},
		{
			name: "non-overlapping CIDRs - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// No overlaps between any CIDRs
				c.CustomerProperties.Network.MachineCIDR = "192.168.0.0/16"
				c.CustomerProperties.Network.ServiceCIDR = "172.30.0.0/16"
				c.CustomerProperties.Network.PodCIDR = "10.128.0.0/14"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "invalid machine CIDR format - no overlap check - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				// Invalid CIDR format - overlap check should not crash
				c.CustomerProperties.Network.MachineCIDR = "invalid-cidr"
				c.CustomerProperties.Network.ServiceCIDR = "172.30.0.0/16"
				c.CustomerProperties.Network.PodCIDR = "10.128.0.0/14"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "invalid CIDR address", FieldPath: "customerProperties.network.machineCidr"},
			},
		},
		// Resource naming validation tests (covering middleware_validatestatic_test.go patterns)
		{
			name: "invalid cluster resource name - special character",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ID.Name = "$"
				c.Name = "$"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be a valid DNS RFC 1035 label", FieldPath: "id"},
			},
		},
		{
			name: "invalid cluster resource name - starts with hyphen",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ID.Name = "-garbage"
				c.Name = "-garbage"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be a valid DNS RFC 1035 label", FieldPath: "id"},
			},
		},
		{
			name: "invalid cluster resource name - starts with number",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ID.Name = "1cluster"
				c.Name = "1cluster"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be a valid DNS RFC 1035 label", FieldPath: "id"},
			},
		},
		{
			name: "invalid cluster resource name - ends with hyphen",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ID.Name = "my-cluster-"
				c.Name = "my-cluster-"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be a valid DNS RFC 1035 label", FieldPath: "id"},
			},
		},
		{
			name: "invalid cluster resource name - too long",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				longName := "3a725v234c0Qd5bPfSYgk5okd2ps7UApyv8wtv810Y02ZvfAse0pgZemQ6dqE791QVKq6n6DAzU8bQTUOVCHwUOeq9fx92dpFebTgKEsx1Xl8Xrvs8NLehe3bj3h813B3j"
				c.ID.Name = longName
				c.Name = longName
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Too long", FieldPath: "id"},
				{Message: "must be a valid DNS RFC 1035 label", FieldPath: "id"},
			},
		},
		{
			name: "invalid cluster resource name - empty",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ID.Name = ""
				c.Name = ""
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "resource name is required", FieldPath: "trackedResource.resource.id"},
				{Message: "resource name is required", FieldPath: "id"},
			},
		},
		{
			name: "valid cluster resource name - minimum length",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ID.Name = "a"
				c.Name = "a"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid cluster resource name - with hyphens",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ID.Name = "my-cluster-1"
				c.Name = "my-cluster-1"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid cluster resource name - mixed case",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ID.Name = "MyCluster"
				c.Name = "MyCluster"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "invalid cluster create - MaxNodesTotal exceeds maximum",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Autoscaling.MaxNodesTotal = 501 // exceeds limit of 500
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be less than or equal to 500", FieldPath: "customerProperties.autoscaling.maxNodesTotal"},
			},
		},
		{
			name: "valid cluster create - MaxNodesTotal is zero",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Autoscaling.MaxNodesTotal = 0 // cluster limit of 500 still enforced at nodepool level
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "invalid cluster create - MaxNodesTotal is negative",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Autoscaling.MaxNodesTotal = -1
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be greater than or equal to 0", FieldPath: "customerProperties.autoscaling.maxNodesTotal"},
			},
		},
		{
			name: "valid cluster create - MaxNodesTotal at maximum",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Autoscaling.MaxNodesTotal = 500 // at the limit
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		// Managed resource group name validation tests
		{
			name: "invalid cluster - managed resource group name is missing",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = ""
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "customerProperties.platform.managedResourceGroup"},
			},
		},
		{
			name: "valid managed resource group name - alphanumeric",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "myResourceGroup123"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid managed resource group name - with hyphens and underscores",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "my-resource_group"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid managed resource group name - with parentheses",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "my-resource-group(test)"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid managed resource group name - with periods in middle",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "my.resource.group"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "invalid managed resource group name - ends with period",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "my-resource-group."
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "max 90 characters", FieldPath: "customerProperties.platform.managedResourceGroup"},
			},
		},
		{
			name: "invalid managed resource group name - too long (91 chars)",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "a123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "max 90 characters", FieldPath: "customerProperties.platform.managedResourceGroup"},
			},
		},
		{
			name: "invalid managed resource group name - invalid characters",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "my-resource-group$invalid"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "max 90 characters", FieldPath: "customerProperties.platform.managedResourceGroup"},
			},
		},
		{
			name: "valid managed resource group name - exactly 90 chars",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "a12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid managed resource group name - unicode letters",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.ManagedResourceGroup = "myRésourceGröup"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid 4.19 version with experimental flag - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.19"
				return c
			}(),
			opOptions:    testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "invalid 4.19 version without experimental flag - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.19"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be at least 4.20", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "unsupported ingress type Disabled - create",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Ingress.Type = metadataapi.IngressTypeDisabled
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "Unsupported value", FieldPath: "customerProperties.ingress.type"},
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			op := operation.Operation{Type: operation.Create, Options: tt.opOptions}
			errs := ValidateCluster(ctx, op, tt.cluster, nil, nil)
			utils.VerifyErrorsMatch(t, tt.expectErrors, errs)
		})
	}
}

// TestValidateClusterCreate_ManagedIdentitiesDataPlaneIdentityURLOptionalOperationOption tests that
// when ManagedIdentitiesDataPlaneIdentityURLOptionalOperationOption is set, the field becomes optional
func TestValidateClusterCreate_ManagedIdentitiesDataPlaneIdentityURLOptionalOperationOption(t *testing.T) {
	ctx := context.Background()

	tests := []struct {
		name         string
		cluster      *coreapi.HCPOpenShiftCluster
		expectErrors []utils.ExpectedError
	}{
		{
			name: "empty ManagedIdentitiesDataPlaneIdentityURL with option set - valid",
			cluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.ManagedIdentitiesDataPlaneIdentityURL = ""
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			op := operation.Operation{
				Type:    operation.Create,
				Options: []string{ManagedIdentitiesDataPlaneIdentityURLOptionalOperationOption},
			}
			errs := ValidateCluster(ctx, op, tt.cluster, nil, nil)
			utils.VerifyErrorsMatch(t, tt.expectErrors, errs)
		})
	}
}

// Comprehensive tests for ValidateClusterUpdate
func TestValidateClusterUpdate(t *testing.T) {
	ctx := context.Background()

	tests := []struct {
		name         string
		newCluster   *coreapi.HCPOpenShiftCluster
		oldCluster   *coreapi.HCPOpenShiftCluster
		opOptions    []string
		expectErrors []utils.ExpectedError
	}{
		{
			name:         "valid cluster update - no changes",
			newCluster:   createValidCluster(),
			oldCluster:   createValidCluster(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid cluster update - systemData",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				createdAt := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
				now := time.Now()
				c.SystemData = &coreapi.SystemData{
					CreatedBy:      "test-user",
					CreatedByType:  coreapi.CreatedByTypeUser,
					CreatedAt:      &createdAt,
					LastModifiedAt: &now,
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				createdAt := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
				earlier := time.Now().Add(-1 * time.Hour)
				c.SystemData = &coreapi.SystemData{
					CreatedBy:      "test-user",
					CreatedByType:  coreapi.CreatedByTypeUser,
					CreatedAt:      &createdAt,
					LastModifiedAt: &earlier,
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid cluster update - allow channel group change",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ChannelGroup = "stable"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ChannelGroup = "stable"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid cluster update - allow authorized CIDRs change",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/16", "192.168.1.0/24"}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/16"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "valid cluster update - allow autoscaling changes",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Autoscaling.MaxNodesTotal = 200
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Autoscaling.MaxNodesTotal = 100
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "invalid cluster update - MaxNodesTotal exceeds maximum",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Autoscaling.MaxNodesTotal = 501 // exceeds limit of 500
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be less than or equal to 500", FieldPath: "customerProperties.autoscaling.maxNodesTotal"},
			},
		},
		{
			name: "valid cluster update - allow node drain timeout change",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.NodeDrainTimeoutMinutes = 60
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.NodeDrainTimeoutMinutes = 30
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "identity cannot change",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity":  {},
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity2": {},
					},
				}
				c.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
					"test-operator":   metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity")),
					"test-operator-2": metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity2")),
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.platform"},
				{Message: "field is immutable", FieldPath: "customerProperties.platform.operatorsAuthentication"},
				{Message: "field is immutable", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities"},
				{Message: "field is immutable", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator-2]"},
			},
		},
		{
			name: "immutable provisioning state - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.ProvisioningState = coreapi.ProvisioningStateProvisioning
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.ProvisioningState = coreapi.ProvisioningStateSucceeded
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "serviceProviderProperties.provisioningState"},
			},
		},
		{
			name: "version ID can be changed - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.21"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			opOptions:    testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "version ChannelGroup can be changed - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ChannelGroup = "fast"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ChannelGroup = "stable"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "immutable base domain - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.DNS.BaseDomain = "new.example.com"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.DNS.BaseDomain = "example.com"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "serviceProviderProperties.dns.baseDomain"},
			},
		},
		{
			name: "immutable base domain prefix - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.DNS.BaseDomainPrefix = "newprefix"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.DNS.BaseDomainPrefix = "oldprefix"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.dns.baseDomainPrefix"},
			},
		},
		{
			name: "immutable network profile - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.PodCIDR = "10.200.0.0/14"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Network.PodCIDR = "10.128.0.0/14"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.network"},
				{Message: "field is immutable", FieldPath: "customerProperties.network.podCidr"},
			},
		},
		{
			name: "immutable console profile - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.Console.URL = "https://new-console.example.com"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.Console.URL = "https://console.example.com"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "serviceProviderProperties.console"},
				{Message: "field is immutable", FieldPath: "serviceProviderProperties.console.url"},
			},
		},
		{
			name: "immutable API URL - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.API.URL = "https://new-api.example.com"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.API.URL = "https://api.example.com"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "serviceProviderProperties.api.url"},
			},
		},
		{
			name: "immutable API visibility - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.Visibility = metadataapi.VisibilityPrivate
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.Visibility = metadataapi.VisibilityPublic
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.api.visibility"},
			},
		},
		{
			name: "immutable ingress type - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Ingress.Type = metadataapi.IngressTypePrivate
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Ingress.Type = metadataapi.IngressTypePublic
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.ingress.type"},
			},
		},
		{
			name: "immutable platform profile - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.SubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/new-subnet"))
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-vnet-integration-subnet"))
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.SubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-subnet"))
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-vnet-integration-subnet"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.platform"},
				{Message: "field is immutable", FieldPath: "customerProperties.platform.subnetId"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.subnetId"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
			},
		},
		{
			name: "immutable vnet integration subnet - update (change)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/new-vnet-integration-subnet"))
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/old-vnet-integration-subnet"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.platform"},
				{Message: "field is immutable", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
				{Message: "must belong to the same VNet as subnetId", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
				{Message: "must be in the same Azure subscription", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
			},
		},
		{
			name: "immutable vnet integration subnet - update (remove)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = nil
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/old-vnet-integration-subnet"))
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.platform"},
				{Message: "field is immutable", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
			},
		},
		{
			name: "valid vnet integration subnet unchanged - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/vnet-integration-subnet"))
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/vnet-integration-subnet"))
				return c
			}(),
		},
		{
			name: "vnet integration subnet remains nil - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = nil
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = nil
				return c
			}(),
		},
		{
			name: "cannot add vnet integration subnet on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/new-vnet-integration-subnet"))
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Platform.VnetIntegrationSubnetID = nil
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.platform"},
				{Message: "field is immutable", FieldPath: "customerProperties.platform.vnetIntegrationSubnetId"},
			},
		},
		{
			name: "immutable etcd profile - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = "Invalid"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.etcd.dataEncryption.keyManagementMode"},
				{Message: "\"Invalid\": supported values: \"CustomerManaged\"", FieldPath: "customerProperties.etcd.dataEncryption.keyManagementMode"},
				{Message: "may only be specified when `keyManagementMode` is \"CustomerManaged\"", FieldPath: "customerProperties.etcd.dataEncryption.customerManaged"},
			},
		},
		{
			name: "immutable kms visibility - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: metadataapi.KeyVaultVisibilityPrivate,
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "test-version",
						},
					},
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: metadataapi.KeyVaultVisibilityPublic,
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "test-version",
						},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.etcd.dataEncryption.customerManaged.kms.visibility"},
			},
		},
		{
			name: "mutable kms key version with v20260630preview - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: metadataapi.KeyVaultVisibilityPublic,
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "new-version",
						},
					},
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: metadataapi.KeyVaultVisibilityPublic,
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "old-version",
						},
					},
				}
				return c
			}(),
			opOptions:    []string{metadataapi.APIVersionOption(metadataapi.APIVersionV20260630Preview)},
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "immutable kms key version without v20260630preview - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: metadataapi.KeyVaultVisibilityPublic,
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "new-version",
						},
					},
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
				c.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
					EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
					Kms: &coreapi.KmsEncryptionProfile{
						Visibility: metadataapi.KeyVaultVisibilityPublic,
						ActiveKey: coreapi.KmsKey{
							Name:      "test-key",
							VaultName: "test-vault",
							Version:   "old-version",
						},
					},
				}
				return c
			}(),
			opOptions: []string{metadataapi.APIVersionOption(metadataapi.APIVersionV20251223Preview)},
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.etcd.dataEncryption.customerManaged.kms.activeKey.version"},
			},
		},
		{
			name: "immutable cluster image registry profile - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.ClusterImageRegistry.State = metadataapi.ClusterImageRegistryStateDisabled
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.ClusterImageRegistry.State = metadataapi.ClusterImageRegistryStateEnabled
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.clusterImageRegistry"},
				{Message: "field is immutable", FieldPath: "customerProperties.clusterImageRegistry.state"},
			},
		},
		{
			name: "immutable CryptoRestrictions - update (None to FIPS)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.CryptoRestrictions = metadataapi.CryptoRestrictionsFIPS
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.CryptoRestrictions = metadataapi.CryptoRestrictionsNone
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.cryptoRestrictions"},
			},
		},
		{
			name: "immutable CryptoRestrictions - update (FIPS to None)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.CryptoRestrictions = metadataapi.CryptoRestrictionsNone
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.CryptoRestrictions = metadataapi.CryptoRestrictionsFIPS
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.cryptoRestrictions"},
			},
		},
		{
			name: "CryptoRestrictions unchanged - update (both None)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.CryptoRestrictions = metadataapi.CryptoRestrictionsNone
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.CryptoRestrictions = metadataapi.CryptoRestrictionsNone
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "CryptoRestrictions unchanged - update (both FIPS)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.CryptoRestrictions = metadataapi.CryptoRestrictionsFIPS
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.CryptoRestrictions = metadataapi.CryptoRestrictionsFIPS
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "invalid new field value on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"invalid-cidr"}
				return c
			}(),
			oldCluster: createValidCluster(),
			expectErrors: []utils.ExpectedError{
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "empty authorized CIDR on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{""}
				return c
			}(),
			oldCluster: createValidCluster(),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "authorized CIDR with whitespace on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{" 10.0.0.0/16 "}
				return c
			}(),
			oldCluster: createValidCluster(),
			expectErrors: []utils.ExpectedError{
				{Message: "must not contain extra whitespace", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "not an IP", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "IPv6 in authorized CIDRs on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"2001:db8::1"}
				return c
			}(),
			oldCluster: createValidCluster(),
			expectErrors: []utils.ExpectedError{
				{Message: "not IPv4", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
				{Message: "invalid CIDR address", FieldPath: "customerProperties.api.authorizedCidrs[0]"},
			},
		},
		{
			name: "empty list authorized CIDR - create",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{}
				return c
			}(),
			oldCluster: createValidCluster(),
			expectErrors: []utils.ExpectedError{
				{Message: "must have at least 1 items", FieldPath: "customerProperties.api.authorizedCidrs"},
			},
		},
		{
			name: "too many authorized CIDRs on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = make([]string, 501)
				for i := range c.CustomerProperties.API.AuthorizedCIDRs {
					c.CustomerProperties.API.AuthorizedCIDRs[i] = "10.0.0.1"
				}
				return c
			}(),
			oldCluster: createValidCluster(),
			expectErrors: []utils.ExpectedError{
				{Message: "Too many", FieldPath: "customerProperties.api.authorizedCidrs"},
			},
		},
		{
			name: "501 unique authorized CIDR blocks on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = makeUniqueCIDRs(501)
				return c
			}(),
			oldCluster: createValidCluster(),
			expectErrors: []utils.ExpectedError{
				{Message: "must have at most 500 items", FieldPath: "customerProperties.api.authorizedCidrs"},
			},
		},
		{
			name: "add authorized CIDR on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/16", "192.168.1.0/24", "172.16.0.0/12"}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/16"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "remove authorized CIDR on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/16"}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/16", "192.168.1.0/24"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "clear all authorized CIDRs on update - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = nil
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.API.AuthorizedCIDRs = []string{"10.0.0.0/16", "192.168.1.0/24"}
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "immutable location - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Location = "westus2"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Location = "eastus"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "trackedResource.location"},
			},
		},
		{
			name: "immutable identity principal ID - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type:        coreapi.ManagedServiceIdentityTypeUserAssigned,
					PrincipalID: "new-principal-id",
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {},
					},
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type:        coreapi.ManagedServiceIdentityTypeUserAssigned,
					PrincipalID: "old-principal-id",
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "identity.principalId"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities[/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity]"},
			},
		},
		{
			name: "immutable identity tenant ID - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type:     coreapi.ManagedServiceIdentityTypeUserAssigned,
					TenantID: "new-tenant-id",
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {},
					},
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type:     coreapi.ManagedServiceIdentityTypeUserAssigned,
					TenantID: "old-tenant-id",
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "identity.tenantId"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities[/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity]"},
			},
		},
		{
			name: "immutable user assigned identity client ID - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {
							ClientID: metadataapi.Ptr("new-client-id"),
						},
					},
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {
							ClientID: metadataapi.Ptr("old-client-id"),
						},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "identity.userAssignedIdentities[/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity].clientId"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities[/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity]"},
			},
		},
		{
			name: "immutable user assigned identity principal ID - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {
							PrincipalID: metadataapi.Ptr("new-principal-id"),
						},
					},
				}
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.Identity = &coreapi.ManagedServiceIdentity{
					Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
					UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
						"/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity": {
							PrincipalID: metadataapi.Ptr("old-principal-id"),
						},
					},
				}
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "identity.userAssignedIdentities[/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity].principalId"},
				{Message: "identity is not assigned to this resource", FieldPath: "customerProperties.platform.operatorsAuthentication.userAssignedIdentities.controlPlaneOperators[test-operator]"},
				{Message: "identity is assigned to this resource but not used", FieldPath: "identity.userAssignedIdentities[/subscriptions/12345678-1234-1234-1234-123456789012/resourceGroups/test-rg/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity]"},
			},
		},
		{
			name: "multiple immutable field changes - update",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.21"
				c.CustomerProperties.Version.ChannelGroup = "fast"
				c.CustomerProperties.DNS.BaseDomainPrefix = "newprefix"
				c.CustomerProperties.API.Visibility = metadataapi.VisibilityPrivate
				c.ServiceProviderProperties.ManagedIdentitiesDataPlaneIdentityURL = "https://newhost.identity.azure.net"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				c.CustomerProperties.Version.ChannelGroup = "stable"
				c.CustomerProperties.DNS.BaseDomainPrefix = "oldprefix"
				c.CustomerProperties.API.Visibility = metadataapi.VisibilityPublic
				c.ServiceProviderProperties.ManagedIdentitiesDataPlaneIdentityURL = "https://oldhost.identity.azure.net"
				return c
			}(),
			// channelGroup and version.id are mutable; dns.baseDomainPrefix, api.visibility and managedIdentitiesDataPlaneIdentityURL are immutable without experimental flag
			expectErrors: []utils.ExpectedError{
				{Message: "field is immutable", FieldPath: "customerProperties.dns.baseDomainPrefix"},
				{Message: "field is immutable", FieldPath: "customerProperties.api.visibility"},
				{Message: "field is immutable", FieldPath: "serviceProviderProperties.managedIdentitiesDataPlaneIdentityURL"},
			},
		},
		// Test cases for version.id requirement validation on update
		// The new validation rules for version.id are:
		// - On create (oldObj == nil): version.id is always required
		// - On update with oldObj.ID set: version.id is required (cannot be cleared)
		// - On update with oldObj.ID empty: version.id is NOT required (legacy migration support)
		{
			name: "update: version.id can be empty when old cluster had no version.id (legacy migration)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = ""
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = ""
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "update: version.id cannot be cleared when old cluster had version.id",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = ""
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			opOptions: testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{
				{Message: "Required value", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "update: version.id can be added when old cluster had no version.id",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = ""
				return c
			}(),
			opOptions:    testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "update: version may not decrease from 4.21 to 4.20",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.21"
				return c
			}(),
			opOptions: testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{
				{Message: "may not decrease", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "update: version can increase from 4.20 to 4.21 using a case insensitive AFEC",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.21"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			opOptions:    testFeatureOptions("Microsoft.Redhatopenshift/ExperimentalReleaseFeatures"),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "update: version can stay the same",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "update: version can stay the same 4.19 version with experimental flag",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.19"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.19"
				return c
			}(),
			opOptions:    testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "update: version may not skip minor within same major (4.20 to 4.22)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.22"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			opOptions: testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{
				{Message: "only upgrade to the next minor is allowed", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "update: cross-major 4.22 to 5.0 rejected without experimental release features",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "5.0"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.22"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "OpenShift v5 and above is not supported", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "update: cross-major 4.20 to 5.0 rejected (4.20 not in 4→5 pairing map)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "5.0"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.20"
				return c
			}(),
			opOptions: testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{
				{Message: "cross-major upgrade from 4.20 is only allowed to", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "update: cross-major 4.22 to 5.0 allowed",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "5.0"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.22"
				return c
			}(),
			opOptions:    testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "update: cross-major 4.23 to 5.1 allowed",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "5.1"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.23"
				return c
			}(),
			opOptions:    testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{},
		},
		{
			name: "update: cross-major 4.22 to 5.1 rejected (wrong paired minor)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "5.1"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.22"
				return c
			}(),
			opOptions: testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{
				{Message: "cross-major upgrade from 4.22 is only allowed to 5.0", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "update: cross-major 4.23 to 5.0 rejected (wrong paired minor)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "5.0"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.23"
				return c
			}(),
			opOptions: testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{
				{Message: "cross-major upgrade from 4.23 is only allowed to 5.1", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "update: upgrade 4 to 6 major rejected (unsupported cross-major skew)",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "6.0"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.22"
				return c
			}(),
			opOptions: testFeatureOptions(metadataapi.FeatureExperimentalReleaseFeatures),
			expectErrors: []utils.ExpectedError{
				{Message: "skipping major versions is not allowed", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			name: "update: version must still be at least 4.20 even if old cluster had lower version without experimental flag",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.19"
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.CustomerProperties.Version.ID = "4.19"
				return c
			}(),
			expectErrors: []utils.ExpectedError{
				{Message: "must be at least 4.20", FieldPath: "customerProperties.version.id"},
			},
		},
		{
			// This test is to ensure that the ManagedIdentitiesDataPlaneIdentityURL is not required when the old value is
			// empty, which could happen if a previously existing cluster is being updated and the old value has not
			// been migrated yet.
			name: "not required ManagedIdentitiesDataPlaneIdentityURL when old value is empty",
			newCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.ManagedIdentitiesDataPlaneIdentityURL = ""
				return c
			}(),
			oldCluster: func() *coreapi.HCPOpenShiftCluster {
				c := createValidCluster()
				c.ServiceProviderProperties.ManagedIdentitiesDataPlaneIdentityURL = ""
				return c
			}(),
			expectErrors: []utils.ExpectedError{},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			op := operation.Operation{Type: operation.Update, Options: tt.opOptions}
			errs := ValidateCluster(ctx, op, tt.newCluster, tt.oldCluster, nil)
			utils.VerifyErrorsMatch(t, tt.expectErrors, errs)
		})
	}
}

func makeUniqueCIDRs(n int) []string {
	cidrs := make([]string, n)
	for i := range cidrs {
		octet3 := i / 256
		octet4 := i % 256
		cidrs[i] = fmt.Sprintf("10.0.%d.%d", octet3, octet4)
	}
	return cidrs
}

// Helper function to create a valid cluster for testing
func createValidCluster() *coreapi.HCPOpenShiftCluster {
	cluster := coreapi.NewDefaultHCPOpenShiftCluster(
		metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.RedHatOpenShift/hcpOpenShiftClusters/noop-updat")),
		coreapitesting.TestLocation,
	)

	// Set required fields that are not in the default
	cluster.Location = "eastus"                    // Required for TrackedResource validation
	cluster.CustomerProperties.Version.ID = "4.20" // Use MAJOR.MINOR format, must be at least 4.20
	cluster.CustomerProperties.DNS.BaseDomainPrefix = "testcluster"
	cluster.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
	cluster.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
		EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
		Kms: &coreapi.KmsEncryptionProfile{
			Visibility: metadataapi.KeyVaultVisibilityPublic,
			ActiveKey: coreapi.KmsKey{
				Name:      coreapitesting.TestKMSKeyName,
				VaultName: coreapitesting.TestKMSKeyVaultName,
				Version:   coreapitesting.TestKMSKeyVersion,
			},
		},
	}
	// Use different resource group for subnet to ensure same subscription validation
	cluster.CustomerProperties.Platform.SubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-subnet"))
	cluster.CustomerProperties.Platform.VnetIntegrationSubnetID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/virtualNetworks/test-vnet/subnets/test-vnet-integration-subnet"))
	cluster.CustomerProperties.Platform.NetworkSecurityGroupID = metadataapi.Must(azcorearm.ParseResourceID("/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.Network/networkSecurityGroups/test-nsg"))
	cluster.CustomerProperties.Platform.ManagedResourceGroup = "managed-rg" // Different from cluster resource group

	// Set ClusterUID for valid testing (generated by admission on CREATE; immutable thereafter)
	cluster.ServiceProviderProperties.ClusterUID = "00000000-0000-0000-0000-000000000000"

	// Set up user assigned identities for valid testing with matching subscription and location
	identityID := "/subscriptions/0465bc32-c654-41b8-8d87-9815d7abe8f6/resourceGroups/some-resource-group/providers/Microsoft.ManagedIdentity/userAssignedIdentities/test-identity"
	cluster.CustomerProperties.Platform.OperatorsAuthentication.UserAssignedIdentities.ControlPlaneOperators = map[string]*azcorearm.ResourceID{
		"test-operator": metadataapi.Must(azcorearm.ParseResourceID(identityID)),
	}

	// Add the identity to the cluster's identity section so it's properly assigned
	cluster.Identity = &coreapi.ManagedServiceIdentity{
		Type: coreapi.ManagedServiceIdentityTypeUserAssigned,
		UserAssignedIdentities: map[string]*coreapi.UserAssignedIdentity{
			identityID: {},
		},
	}

	// Add required systemData fields
	createdAt := time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC)
	cluster.SystemData = &coreapi.SystemData{
		CreatedBy:     "test-user",
		CreatedByType: coreapi.CreatedByTypeUser,
		CreatedAt:     &createdAt,
	}

	cluster.ServiceProviderProperties.ManagedIdentitiesDataPlaneIdentityURL = coreapitesting.TestManagedIdentitiesDataPlaneIdentityURL

	// PlatformManaged etcd encryption is not currently supported; require CustomerManaged for a valid cluster.
	cluster.CustomerProperties.Etcd.DataEncryption.KeyManagementMode = metadataapi.EtcdDataEncryptionKeyManagementModeTypeCustomerManaged
	cluster.CustomerProperties.Etcd.DataEncryption.CustomerManaged = &coreapi.CustomerManagedEncryptionProfile{
		EncryptionType: metadataapi.CustomerManagedEncryptionTypeKMS,
		Kms: &coreapi.KmsEncryptionProfile{
			Visibility: metadataapi.KeyVaultVisibilityPublic,
			ActiveKey: coreapi.KmsKey{
				Name:      "test-key",
				VaultName: "test-vault",
				Version:   "test-version",
			},
		},
	}

	return cluster
}
