// 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"
	"fmt"
	"time"

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

	"golang.org/x/sync/errgroup"

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

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

	It("should be able to create an HCP cluster then delete it by deleting the customer resource group",
		labels.RequireNothing,
		labels.Critical,
		labels.Positive,
		labels.MIContainers(1),
		func(ctx context.Context) {
			const (
				customerNetworkSecurityGroupName = "customer-nsg"
				customerVnetName                 = "customer-vnet"
				customerVnetSubnetName           = "customer-vnet-subnet1"
				customerClusterName              = "cx-rg-hcp-cluster"
				customerNodePool1Name            = "np-1"
				customerNodePool2Name            = "np-2"
			)
			tc := framework.NewTestContext()

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

			By("creating the customer resource group")
			resourceGroup, err := tc.NewResourceGroup(ctx, "cx-rg-cluster", tc.Location())
			Expect(err).NotTo(HaveOccurred(), "failed to create customer resource group")

			By("creating cluster parameters")
			clusterParams := framework.NewDefaultClusterParams20240610()
			clusterParams.ClusterName = customerClusterName
			managedResourceGroupName := framework.SuffixName(*resourceGroup.Name, "-managed", 64)
			clusterParams.ManagedResourceGroupName = managedResourceGroupName

			By("creating customer resources")
			clusterParams, err = tc.CreateClusterCustomerResources20240610(ctx,
				resourceGroup,
				clusterParams,
				map[string]interface{}{
					"customerNsgName":        customerNetworkSecurityGroupName,
					"customerVnetName":       customerVnetName,
					"customerVnetSubnetName": customerVnetSubnetName,
				},
				TestArtifactsFS,
				framework.RBACScopeResource,
			)
			Expect(err).NotTo(HaveOccurred(), "failed to create customer resources for cluster %q", customerClusterName)

			By("creating the HCP cluster")
			err = tc.CreateHCPClusterFromParam20240610(
				ctx,
				GinkgoLogr,
				*resourceGroup.Name,
				clusterParams,
				framework.ClusterCreationTimeout,
			)
			Expect(err).NotTo(HaveOccurred(), "failed to create HCP cluster %q", customerClusterName)

			By("getting credentials")
			adminRESTConfig, err := tc.GetAdminRESTConfigForHCPCluster20240610(
				ctx,
				tc.Get20240610ClientFactoryOrDie(ctx).NewHcpOpenShiftClustersClient(),
				*resourceGroup.Name,
				customerClusterName,
				framework.GetAdminRESTConfigTimeout,
			)
			Expect(err).NotTo(HaveOccurred(), "failed to get admin REST config for cluster %q", customerClusterName)

			By("ensuring the cluster is viable")
			err = verifiers.VerifyHCPCluster(ctx, adminRESTConfig)
			Expect(err).NotTo(HaveOccurred(), "failed to verify HCP cluster %q is viable", customerClusterName)

			By("creating both nodepools in parallel")
			nodePoolParams1 := framework.NewDefaultNodePoolParams20240610()
			nodePoolParams1.NodePoolName = customerNodePool1Name
			nodePoolParams1.Replicas = int32(2)

			nodePoolParams2 := framework.NewDefaultNodePoolParams20240610()
			nodePoolParams2.NodePoolName = customerNodePool2Name
			nodePoolParams2.Replicas = int32(1)

			errCh := make(chan error, 2)
			group, groupCtx := errgroup.WithContext(ctx)
			for _, nodePoolParams := range []framework.NodePoolParams20240610{nodePoolParams1, nodePoolParams2} {
				group.Go(func() error {
					createErr := tc.CreateNodePoolFromParam20240610(
						groupCtx,
						GinkgoLogr,
						*resourceGroup.Name,
						managedResourceGroupName,
						customerClusterName,
						nodePoolParams,
						framework.NodePoolCreationTimeout,
					)
					if createErr != nil {
						errCh <- fmt.Errorf("nodepool %s: %w", nodePoolParams.NodePoolName, createErr)
					}
					return createErr
				})
			}
			err = group.Wait()
			close(errCh)
			for createErr := range errCh {
				GinkgoLogr.Error(createErr, "nodepool creation failed")
			}
			Expect(err).NotTo(HaveOccurred(), "failed to create node pools in parallel for cluster %q", customerClusterName)

			By("deleting customer resource group to trigger cluster deletion")
			rgClient := tc.GetARMResourcesClientFactoryOrDie(ctx).NewResourceGroupsClient()
			networkClient, err := tc.GetARMNetworkClientFactory(ctx)
			Expect(err).NotTo(HaveOccurred(), "failed to create ARM network client factory")
			err = framework.DeleteResourceGroup(ctx, rgClient, networkClient, *resourceGroup.Name, false, 60*time.Minute)
			Expect(err).NotTo(HaveOccurred(), "failed to delete customer resource group %q", *resourceGroup.Name)

			By("verifying customer resource group is deleted (404)")
			_, err = rgClient.Get(ctx, *resourceGroup.Name, nil)
			Expect(err).To(HaveOccurred(), "expected customer resource group %q to be deleted (404)", *resourceGroup.Name)
			Expect(err.Error()).To(ContainSubstring("ResourceGroupNotFound"), "customer resource group %q should return ResourceGroupNotFound after deletion", *resourceGroup.Name)

			By("verifying managed resource group is deleted (404)")
			Eventually(func() error {
				_, err = rgClient.Get(ctx, managedResourceGroupName, nil)
				return err
			}, 10*time.Minute, 30*time.Second).ShouldNot(Succeed())
			Expect(err.Error()).To(ContainSubstring("ResourceGroupNotFound"), "managed resource group %q should return ResourceGroupNotFound after deletion", managedResourceGroupName)
		})
})
