package common

import (
	"context"
	"fmt"
	"time"

	"k8s.io/apimachinery/pkg/fields"
	"sigs.k8s.io/controller-runtime/pkg/client"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"github.com/tidbcloud/infra-provider/regional-server/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/controllers/vpc/task"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/deploy"
	"github.com/tidbcloud/infra-provider/regional-server/pkg/util"
)

// Guardian adds and removes the finalizer of the VPC.
type Guardian struct {
	*task.BaseTask
}

func NewGuardian(ctx *task.Context) *Guardian {
	return &Guardian{BaseTask: &task.BaseTask{Context: ctx}}
}

func (t *Guardian) Name() string {
	return "Guardian"
}

func (t *Guardian) Create(ctx context.Context) deploy.Result {
	t.BeginAt = util.Pointer(time.Now())
	// TODO: check region
	// Add finalizer
	if !controllerutil.ContainsFinalizer(t.VPC, v1alpha1.Finalizer) {
		controllerutil.AddFinalizer(t.VPC, v1alpha1.Finalizer)
		// Require immediate requeue without running other tasks
		return deploy.Panic(nil).WithRequeuAfter(0)
	}

	// Check vpc phase
	switch phase := t.VPC.Status.Phase; phase {
	case "", v1alpha1.VPCPhasePending, v1alpha1.VPCPhaseReadyForCreate, v1alpha1.VPCPhaseFailed:
		t.VPC.Status.Phase = v1alpha1.VPCPhaseCreating
	case v1alpha1.VPCPhaseCreating, v1alpha1.VPCPhaseActive, v1alpha1.VPCPhaseReadyForDelete:
	default:
		return deploy.Panic(fmt.Errorf("unexpected vpc phase during creation: %s", phase))
	}

	if err := t.VPCRW.UpdateStatus(ctx, t.VPC); err != nil {
		return deploy.Panic(err).WithRequeuAfter(0)
	}

	return deploy.Done()
}

func (t *Guardian) Delete(ctx context.Context) deploy.Result {
	if err := t.readyToDelete(ctx); err != nil {
		t.VPC.Status.Reason = err.Error()
		return deploy.Panic(err).WithRequeuAfter(30 * time.Second)
	}
	return deploy.Done()
}

// Check if the dependent objects are deleted
func (t *Guardian) readyToDelete(ctx context.Context) error {
	relatedAppBoxList := &v1alpha1.APPBoxList{}
	listOpts := &client.ListOptions{
		FieldSelector: fields.OneTermEqualSelector(controllers.VPCNameField, t.VPC.Name),
		Namespace:     t.VPC.Namespace,
	}
	if err := t.APPBoxR.List(ctx, relatedAppBoxList, listOpts); err != nil {
		return err
	}
	if len(relatedAppBoxList.Items) != 0 {
		return fmt.Errorf("clusters to be deleted: %d", len(relatedAppBoxList.Items))
	}

	relatedInternetList := &v1alpha1.InternetList{}
	if err := t.InternetR.List(ctx, relatedInternetList, listOpts); err != nil {
		return err
	}
	if len(relatedInternetList.Items) != 0 {
		return fmt.Errorf("internets to be deleted: %d", len(relatedInternetList.Items))
	}
	relatedPrivateLinkServiceList := &v1alpha1.PrivateLinkServiceList{}
	if err := t.PrivateLinkServiceR.List(ctx, relatedPrivateLinkServiceList, listOpts); err != nil {
		return err
	}
	if len(relatedPrivateLinkServiceList.Items) != 0 {
		return fmt.Errorf("privatelinkservices to be deleted: %d", len(relatedPrivateLinkServiceList.Items))
	}
	relatedPrivateLinkEndpointList := &v1alpha1.PrivateLinkEndpointList{}
	if err := t.PrivateLinkEndpointR.List(ctx, relatedPrivateLinkEndpointList, listOpts); err != nil {
		return err
	}
	if len(relatedPrivateLinkEndpointList.Items) != 0 {
		return fmt.Errorf("privatelinkendpoints to be deleted: %d", len(relatedPrivateLinkEndpointList.Items))
	}
	// TODO: VPCPeering does not have VPCRef, instead it reference to dataplane CR
	// relatedVPCPeeringList := &v1alpha1.VPCPeeringList{}
	// if err := t.VPCPeeringR.List(ctx, relatedVPCPeeringList, listOpts); err != nil {
	// 	return err
	// }
	// if len(relatedVPCPeeringList.Items) != 0 {
	// 	return fmt.Errorf("vpcpeerings to be deleted: %d", len(relatedVPCPeeringList.Items))
	// }
	return nil
}
