package aks

import (
	"context"
	"fmt"

	"k8s.io/utils/pointer"
	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"

	"github.com/tidbcloud/eks-provider/controllers/cluster/task"
	"github.com/tidbcloud/eks-provider/controllers/cluster/task/utils/annotator"
	cloudv1 "github.com/tidbcloud/infra-api/api/v1"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/util"
)

// Guardian adds and removes the finalizer of the Cluster.
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 {
	// prevent reconciling objects from different regions
	if t.EnvProvider.GetRegion() != t.Config.Network.Region {
		return deploy.Panic(nil)
	}
	// ignore resource that doesn't belong to this region
	if !util.ContainsRegionLabel(t.Cluster) {
		// ensure the object contains the region label
		if !util.EnsureRegion(t.Cluster, t.EnvProvider.GetRegion()) {
			return deploy.Panic(nil).WithRequeuAfter(0)
		}
	}

	if t.Cluster.Status.Phase == "" {
		t.Cluster.Status.Phase = cloudv1.ClusterPhaseCreating
	}

	annotator.AnnotateCluster(t.Config.Override, t.Cluster)
	if !controllerutil.ContainsFinalizer(t.Cluster, task.Finalizer) {
		controllerutil.AddFinalizer(t.Cluster, task.Finalizer)
		// Require immediate requeue without running other tasks
		return deploy.Panic(nil).WithRequeuAfter(0)
	}

	switch phase := t.Cluster.Status.Phase; phase {
	// do nothing
	case cloudv1.ClusterPhasePending:
	case cloudv1.ClusterPhaseCreating, cloudv1.ClusterPhaseRunning,
		cloudv1.ClusterPhaseUnavailable, cloudv1.ClusterPhaseScaling,
		cloudv1.ClusterPhaseUpgrading, cloudv1.ClusterPhaseSuspend:
		if err := t.initClusterSpec(); err != nil {
			return deploy.Panic(err)
		}
	default:
		return deploy.Panic(fmt.Errorf("unexpected cluster phase during creation: %s", phase))
	}

	return deploy.Done()
}

func (t *Guardian) Delete(ctx context.Context) deploy.Result {
	switch phase := t.Cluster.Status.Phase; phase {
	case cloudv1.ClusterPhaseCreating, cloudv1.ClusterPhaseRunning, "":
		t.Cluster.Status.Phase = cloudv1.ClusterPhasePendingDelete
		// Require immediate requeue without running other tasks
		return deploy.Panic(nil).WithRequeuAfter(0)
	case cloudv1.ClusterPhasePendingDelete:
		t.Cluster.Status.Phase = cloudv1.ClusterPhaseDeleting
	case cloudv1.ClusterPhaseDeleting:
	default:
		return deploy.Panic(fmt.Errorf("unexpected cluster phase during deletion: %s", phase))
	}
	return deploy.Done()
}

func (t *Guardian) initClusterSpec() error {
	if t.AKSClusterSpec.ComponentPCAARN == nil {
		t.AKSClusterSpec.ComponentPCAARN = pointer.String(t.AcctInfoGetter.GetComponentPCAARN())
	}

	return nil
}
