// 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 e2e

import (
	"context"

	. "github.com/onsi/ginkgo/v2"
	. "github.com/onsi/gomega"

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

	"github.com/Azure/ARO-HCP/test/util/framework"
	"github.com/Azure/ARO-HCP/test/util/labels"
)

var _ = Describe("Customer", func() {
	BeforeEach(func() {
		// do nothing.  per test initialization usually ages better than shared.
	})

	It("should be able to create several HCP clusters in their customer resource group, but not in the same managed resource group",
		labels.RequireNothing,
		labels.AroRpApiCompatible,
		labels.Critical,
		labels.Positive,
		labels.MIContainers(3),
		func(ctx context.Context) {
			const (
				customerNetworkSecurityGroupName = "customer-nsg-name"
				customerVnetName                 = "customer-vnet-name"
				customerVnetSubnetName           = "customer-vnet-subnet1"
				customerClusterName              = "basic-hcp-cluster"

				customerNetworkSecurityGroupName2 = "customer-nsg2-name"
				customerVnetName2                 = "customer-vnet2-name"
				customerVnetSubnetName2           = "customer-vnet-subnet2"
				customerClusterName2              = "basic-hcp-cluster2"

				customerNetworkSecurityGroupName3 = "customer-nsg3-name"
				customerVnetName3                 = "customer-vnet3-name"
				customerVnetSubnetName3           = "customer-vnet-subnet3"
				customerClusterName3              = "basic-hcp-cluster3"
			)
			tc := framework.NewTestContext()

			if tc.UsePooledIdentities() {
				err := tc.AssignIdentityContainers(ctx, 3, framework.IdentityContainerAssignmentRetryInterval)
				Expect(err).NotTo(HaveOccurred(), "failed to assign pooled identity containers")
			}

			By("creating a shared customer resource group")
			customerResourceGroup, err := tc.NewResourceGroup(ctx, "customer-rg", tc.Location())
			Expect(err).NotTo(HaveOccurred(), "failed to create shared customer resource group")

			By("creating customer infrastructure and managed identities for first cluster")
			clusterParams1 := framework.NewDefaultClusterParams20240610()
			clusterParams1.ClusterName = customerClusterName
			clusterParams1.ManagedResourceGroupName = framework.SuffixName(*customerResourceGroup.Name, "-managed", 64)
			clusterParams1, err = tc.CreateClusterCustomerResources20240610(ctx, customerResourceGroup, clusterParams1,
				map[string]interface{}{
					"customerNsgName":        customerNetworkSecurityGroupName,
					"customerVnetName":       customerVnetName,
					"customerVnetSubnetName": customerVnetSubnetName,
				},
				TestArtifactsFS,
				framework.RBACScopeResourceGroup,
			)
			Expect(err).NotTo(HaveOccurred(), "failed to create customer resources for first cluster %q", customerClusterName)

			By("creating customer infrastructure and managed identities for second cluster")
			clusterParams2 := framework.NewDefaultClusterParams20240610()
			clusterParams2.ClusterName = customerClusterName2
			clusterParams2.ManagedResourceGroupName = framework.SuffixName(*customerResourceGroup.Name, "-managed-2", 64)
			clusterParams2, err = tc.CreateClusterCustomerResources20240610(ctx, customerResourceGroup, clusterParams2,
				map[string]interface{}{
					"customerNsgName":        customerNetworkSecurityGroupName2,
					"customerVnetName":       customerVnetName2,
					"customerVnetSubnetName": customerVnetSubnetName2,
				},
				TestArtifactsFS,
				framework.RBACScopeResourceGroup,
			)
			Expect(err).NotTo(HaveOccurred(), "failed to create customer resources for second cluster %q", customerClusterName2)

			By("starting creation of both clusters in parallel")
			clusterClient := tc.Get20240610ClientFactoryOrDie(ctx).NewHcpOpenShiftClustersClient()

			// Start first cluster creation
			poller1, err := framework.BeginCreateHCPCluster20240610(
				ctx,
				GinkgoLogr,
				clusterClient,
				*customerResourceGroup.Name,
				clusterParams1.ClusterName,
				clusterParams1,
				tc.Location(),
			)
			Expect(err).NotTo(HaveOccurred(), "failed to begin creation of first cluster %q", customerClusterName)

			// Start second cluster creation
			poller2, err := framework.BeginCreateHCPCluster20240610(
				ctx,
				GinkgoLogr,
				clusterClient,
				*customerResourceGroup.Name,
				clusterParams2.ClusterName,
				clusterParams2,
				tc.Location(),
			)
			Expect(err).NotTo(HaveOccurred(), "failed to begin creation of second cluster %q", customerClusterName2)

			By("waiting for first cluster to complete creation")
			pollCtx, pollCancel := context.WithTimeout(ctx, framework.ClusterCreationTimeout)
			defer pollCancel()
			_, err = poller1.PollUntilDone(pollCtx, &runtime.PollUntilDoneOptions{
				Frequency: framework.StandardPollInterval,
			})
			Expect(err).NotTo(HaveOccurred(), "failed to wait for first cluster %q to complete creation (timeout '%f' minutes)", customerClusterName, framework.ClusterCreationTimeout.Minutes())

			By("waiting for second cluster to complete creation")

			_, err = poller2.PollUntilDone(pollCtx, &runtime.PollUntilDoneOptions{
				Frequency: framework.StandardPollInterval,
			})
			Expect(err).NotTo(HaveOccurred(), "failed to wait for second cluster %q to complete creation (timeout '%f' minutes)", customerClusterName2, framework.ClusterCreationTimeout.Minutes())

			// Third cluster (should fail)
			By("creating customer infrastructure and managed identities for third cluster")
			clusterParams3 := framework.NewDefaultClusterParams20240610()
			clusterParams3.ClusterName = customerClusterName3
			clusterParams3.ManagedResourceGroupName = clusterParams2.ManagedResourceGroupName // Reuse cluster2's managed RG
			clusterParams3, err = tc.CreateClusterCustomerResources20240610(ctx, customerResourceGroup, clusterParams3,
				map[string]interface{}{
					"customerNsgName":        customerNetworkSecurityGroupName3,
					"customerVnetName":       customerVnetName3,
					"customerVnetSubnetName": customerVnetSubnetName3,
				},
				TestArtifactsFS,
				framework.RBACScopeResourceGroup,
			)
			Expect(err).NotTo(HaveOccurred(), "failed to create customer resources for third cluster %q", customerClusterName3)

			By("attempting to create a third cluster using the same managed resource group as the second cluster")
			err = tc.CreateHCPClusterFromParam20240610(ctx, GinkgoLogr, *customerResourceGroup.Name, clusterParams3, framework.ClusterCreationTimeout)
			Expect(err).To(HaveOccurred(), "expected error when creating third cluster %q with duplicate managed resource group", customerClusterName3)
			Expect(err).To(MatchError(MatchRegexp("please provide a unique managed resource group name")), "error should indicate duplicate managed resource group name")

			By("verifying that the managed resource group still exists")
			_, err = tc.GetARMResourcesClientFactoryOrDie(ctx).NewResourceGroupsClient().Get(ctx, clusterParams2.ManagedResourceGroupName, nil)
			Expect(err).NotTo(HaveOccurred(), "failed to verify managed resource group %q still exists", clusterParams2.ManagedResourceGroupName)

			By("testing resource group-level cluster listing")
			pager := clusterClient.NewListByResourceGroupPager(*customerResourceGroup.Name, nil)
			var foundClusters []string
			for pager.More() {
				clusterList, err := pager.NextPage(ctx)
				Expect(err).NotTo(HaveOccurred(), "failed to list clusters in resource group %q", *customerResourceGroup.Name)
				for _, cluster := range clusterList.Value {
					foundClusters = append(foundClusters, *cluster.Name)
				}
			}
			Expect(foundClusters).To(HaveLen(2), "Expected exactly two clusters listed in resource group %s", *customerResourceGroup.Name)
			Expect(foundClusters).To(ContainElements(customerClusterName, customerClusterName2), "listed clusters should contain both %s and %s", customerClusterName, customerClusterName2)
		})
})
