package agentbuild

import (
	"context"

	vmoperatorv1beta1 "github.com/VictoriaMetrics/operator/api/operator/v1beta1"
	ec2v1alpha1 "github.com/crossplane-contrib/provider-aws/apis/ec2/v1alpha1"
	eksv1alpha1 "github.com/crossplane-contrib/provider-aws/apis/eks/manualv1alpha1"
	gkecontainerv1beta1 "github.com/crossplane-contrib/provider-gcp/apis/container/v1beta1"
	corev1 "k8s.io/api/core/v1"
	rbacv1 "k8s.io/api/rbac/v1"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"

	"github.com/tidbcloud/observability/agent-control-server/pkg/helmcli"
	"github.com/tidbcloud/observability/agent-control-server/pkg/k8sclient/k8scliv1"
)

// DeleteVMAgent deletes a VMAgent resource.
//
// It returns nil if the resource is not found or if the API server does not recognize the resource.
func DeleteVMAgent(ctx context.Context, cli client.Client, name, namespace string) error {
	err := cli.Delete(ctx, &vmoperatorv1beta1.VMAgent{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
	})
	if apierrors.IsNotFound(err) {
		return nil
	}
	if meta.IsNoMatchError(err) {
		return nil
	}
	return err
}

// DeleteVMPodScrape deletes a VMPodScrape resource.
//
// It returns nil if the resource is not found or if the API server does not recognize the resource.
func DeleteVMPodScrape(ctx context.Context, cli client.Client, name, namespace string) error {
	err := cli.Delete(ctx, &vmoperatorv1beta1.VMPodScrape{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
	})
	if apierrors.IsNotFound(err) {
		return nil
	}
	if meta.IsNoMatchError(err) {
		return nil
	}
	return err
}

// DeleteVMServiceScrape deletes a VMServiceScrape resource.
//
// It returns nil if the resource is not found or if the API server does not recognize the resource.
func DeleteVMServiceScrape(ctx context.Context, cli client.Client, name, namespace string) error {
	err := cli.Delete(ctx, &vmoperatorv1beta1.VMServiceScrape{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
	})
	if apierrors.IsNotFound(err) {
		return nil
	}
	if meta.IsNoMatchError(err) {
		return nil
	}
	return err
}

// DeleteRole deletes a Role resource.
//
// It returns nil if the resource is not found.
func DeleteRole(ctx context.Context, cli client.Client, name, namespace string) error {
	return client.IgnoreNotFound(cli.Delete(ctx, &rbacv1.Role{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
	}))
}

// DeleteRoleBinding deletes a RoleBinding resource.
//
// It returns nil if the resource is not found.
func DeleteRoleBinding(ctx context.Context, cli client.Client, name, namespace string) error {
	return client.IgnoreNotFound(cli.Delete(ctx, &rbacv1.RoleBinding{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
	}))
}

// DeletePVC deletes a PersistentVolumeClaim resource.
//
// It returns nil if the resource is not found.
func DeletePVC(ctx context.Context, cli client.Client, name, namespace string) error {
	return client.IgnoreNotFound(cli.Delete(ctx, &corev1.PersistentVolumeClaim{
		ObjectMeta: metav1.ObjectMeta{
			Name:      name,
			Namespace: namespace,
		},
	}))
}

// DeletePVCWithLabels deletes PersistentVolumeClaim resources matching the specified labels.
//
// It lists all PVCs with the given labels in the namespace and deletes them.
func DeletePVCWithLabels(ctx context.Context, cli client.Client, labels map[string]string, namespace string) error {
	var pvcList corev1.PersistentVolumeClaimList
	if err := cli.List(ctx, &pvcList, client.InNamespace(namespace), client.MatchingLabels(labels)); err != nil {
		return err
	}
	for _, pvc := range pvcList.Items {
		if err := cli.Delete(ctx, &pvc); client.IgnoreNotFound(err) != nil {
			return err
		}
	}
	return nil
}

// DeleteNodeGroup deletes a NodeGroup resource.
//
// It returns nil if the resource is not found.
func DeleteNodeGroup(ctx context.Context, cli client.Client, name string) error {
	return client.IgnoreNotFound(cli.Delete(ctx, &eksv1alpha1.NodeGroup{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}))
}

// DeleteNodeGroupWithLabels deletes NodeGroup resources matching the specified labels.
//
// It lists all NodeGroups with the given labels and deletes them, skipping any
// name listed in excludeNames and any NodeGroup already mid-deletion.
func DeleteNodeGroupWithLabels(ctx context.Context, cli client.Client, labels map[string]string, excludeNames ...string) ([]string, error) {
	exclude := make(map[string]struct{}, len(excludeNames))
	for _, n := range excludeNames {
		exclude[n] = struct{}{}
	}

	var nodegroups eksv1alpha1.NodeGroupList
	if err := cli.List(ctx, &nodegroups, client.MatchingLabels(labels)); err != nil {
		return nil, err
	}

	var deleted []string
	for i := range nodegroups.Items {
		ng := &nodegroups.Items[i]
		if _, skip := exclude[ng.Name]; skip {
			continue
		}
		if !ng.DeletionTimestamp.IsZero() {
			continue
		}
		if err := client.IgnoreNotFound(cli.Delete(ctx, ng)); err != nil {
			return nil, err
		}
		deleted = append(deleted, ng.Name)
	}
	return deleted, nil
}

// DeleteNodePool deletes a NodePool resource.
//
// It returns nil if the resource is not found.
func DeleteNodePool(ctx context.Context, cli client.Client, name string) error {
	return client.IgnoreNotFound(cli.Delete(ctx, &gkecontainerv1beta1.NodePool{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}))
}

// DeleteNodePoolWithLabels deletes NodePool resources matching the specified labels.
//
// It lists all NodePools with the given labels and deletes them.
func DeleteNodePoolWithLabels(ctx context.Context, cli client.Client, labels map[string]string) error {
	var nodepools gkecontainerv1beta1.NodePoolList
	if err := cli.List(ctx, &nodepools, client.MatchingLabels(labels)); err != nil {
		return err
	}
	for _, np := range nodepools.Items {
		if err := cli.Delete(ctx, &np); err != nil {
			return err
		}
	}
	return nil
}

// DeleteAwsVPCEndpoint deletes a VPCEndpoint resource.
//
// It returns nil if the resource is not found.
func DeleteAwsVPCEndpoint(ctx context.Context, cli client.Client, name string) error {
	return client.IgnoreNotFound(cli.Delete(ctx, &ec2v1alpha1.VPCEndpoint{
		ObjectMeta: metav1.ObjectMeta{
			Name: name,
		},
	}))
}

// DeleteHelm uninstalls a Helm release.
//
// It builds a Helm client and uninstalls the specified release.
func DeleteHelm(ctx context.Context, cliGetter k8scliv1.ClientGetter, name, namespace string) error {
	helm, err := helmcli.BuildHelmClient(ctx, cliGetter, namespace)
	if err != nil {
		return err
	}
	return helmcli.UninstallRelease(ctx, helm, name)
}

// DeleteObject deletes a generic Kubernetes object.
//
// It returns nil if the resource is not found.
func DeleteObject(ctx context.Context, cli client.Writer, obj client.Object) error {
	if err := client.IgnoreNotFound(cli.Delete(ctx, obj)); err != nil {
		return err
	}
	return nil
}
