package common

import (
	"context"
	"fmt"
	"time"

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

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

// Guardian adds and removes the finalizer of the o11yroute.
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())
	// Add finalizer
	if !controllerutil.ContainsFinalizer(t.O11yRoute, v1alpha1.Finalizer) {
		controllerutil.AddFinalizer(t.O11yRoute, v1alpha1.Finalizer)
		// Require immediate requeue without running other tasks
		return deploy.Panic(nil).WithRequeuAfter(0)
	}

	// Check o11yroute phase
	switch phase := t.O11yRoute.Status.Phase; phase {
	case "", v1alpha1.O11yRoutePhasePending, v1alpha1.O11yRoutePhaseFailed:
		if t.readyForCreate(ctx) {
			t.O11yRoute.Status.Phase = v1alpha1.O11yRoutePhaseCreating
			t.O11yRoute.Status.Reason = "DependenciesReady"
		} else {
			t.O11yRoute.Status.Phase = v1alpha1.O11yRoutePhasePending
			t.O11yRoute.Status.Reason = "WaitingForDependenciesCreation"
			return deploy.Panic(fmt.Errorf("APPBox not ready")).WithRequeuAfter(30 * time.Second)
		}
	case v1alpha1.O11yRoutePhaseCreating, v1alpha1.O11yRoutePhaseActive, v1alpha1.O11yRoutePhaseSuspended:
	default:
		return deploy.Panic(fmt.Errorf("unexpected o11yroute phase during creation: %s", phase)).WithRequeuAfter(30 * time.Second)
	}
	return deploy.Done()
}

func (t *Guardian) Delete(ctx context.Context) deploy.Result {
	if t.readyForDelete(ctx) {
		t.O11yRoute.Status.Reason = "DependenciesCleared"
		return deploy.Done()
	} else {
		t.O11yRoute.Status.Reason = "WaitingForDependenciesDeletion"
		return deploy.Panic(fmt.Errorf("WaitingForDependenciesDeletion")).WithRequeuAfter(30 * time.Second)
	}
}

func (t *Guardian) readyForCreate(_ context.Context) bool {
	return t.APPBox.Status.Phase == v1alpha1.APPBoxPhaseActive
}

func (t *Guardian) readyForDelete(_ context.Context) bool {
	if t.O11yRoute.Spec.APPRouteRef == nil {
		return true
	}
	// should delete o11y after tidb cluster
	return t.O11yRoute.Spec.APPRouteRef != nil && t.APPRoute == nil
}
