package network

import (
	"context"
	"fmt"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
	"sigs.k8s.io/controller-runtime/pkg/log"

	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/shared-provider/pkg/utils/namespace"
)

const (
	networkFinalizer = "network.tidbcloud.com/finalizer"
)

func (r *Reconciler) syncFinalizer(ctx context.Context, net *cloudv1.Network) error {
	if net.DeletionTimestamp == nil {
		return r.ensureFinalizer(ctx, net)
	}

	return r.tryToFinalize(ctx, net)
}

// TODO: change to a common util function.
func (r *Reconciler) ensureFinalizer(ctx context.Context, net *cloudv1.Network) error {
	logger := log.FromContext(ctx)

	if controllerutil.ContainsFinalizer(net, networkFinalizer) {
		return nil
	}

	logger.Info("Add finalizer")

	controllerutil.AddFinalizer(net, networkFinalizer)

	if err := r.InfraClient.Update(ctx, net); err != nil {
		return fmt.Errorf("add finalizer for network %s failed: %w", client.ObjectKeyFromObject(net), err)
	}

	return nil
}

func (r *Reconciler) tryToFinalize(ctx context.Context, net *cloudv1.Network) error {
	logger := log.FromContext(ctx)

	if !controllerutil.ContainsFinalizer(net, networkFinalizer) {
		return nil
	}

	ns := &corev1.Namespace{
		ObjectMeta: metav1.ObjectMeta{
			Name: namespace.GetOwnedNamespaceNameFromNetwork(net),
		},
	}

	if net.Status.Phase == cloudv1.NetworkPhaseDeleting {
		err := r.cleanUpImportResource(ctx, net)
		if err != nil {
			return err
		}

		if err = r.deleteBackupBucket(ctx, net); err != nil {
			return err
		}

		if err = r.deleteBackupRole(ctx, net); err != nil {
			return err
		}

		if err := r.Client.Delete(ctx, ns); err != nil {
			if !errors.IsNotFound(err) {
				return fmt.Errorf("delete namespace %s failed: %w", ns.Name, err)
			}

			logger.Info("Remove finalizer")

			controllerutil.RemoveFinalizer(net, networkFinalizer)

			if err := r.InfraClient.Update(ctx, net); err != nil {
				return fmt.Errorf("remove finalizer for network %s failed: %w", client.ObjectKeyFromObject(net), err)
			}
		}
	}

	return nil
}
