package commontasks

import (
	"context"
	"fmt"

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

	agentv2 "github.com/tidbcloud/observability/agent-control-server/api/v2"
	"github.com/tidbcloud/observability/agent-control-server/pkg/consts"
	"github.com/tidbcloud/observability/agent-control-server/pkg/deploy"
	"github.com/tidbcloud/observability/agent-control-server/pkg/reconciler"
)

type GuardianAgent interface {
	client.Object
	Paused() bool
	Phase() string
	SetPhase(phase string)
}

type GuardianTask struct {
	*reconciler.BaseTask[any]
	agent GuardianAgent
	cm    deploy.ClusterMeta
}

func NewGuardianTask(agent GuardianAgent, cm deploy.ClusterMeta) *GuardianTask {
	return &GuardianTask{
		BaseTask: &reconciler.BaseTask[any]{},
		agent:    agent,
		cm:       cm,
	}
}

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

func (t *GuardianTask) ClusterMeta() deploy.ClusterMeta {
	return t.cm
}

// Create returns an error or makes the o11y agent phase be one of:
//   - O11yAgentPhaseCreating
//   - O11yAgentPhaseNormal
func (t *GuardianTask) Create(ctx context.Context) deploy.Result {
	if !controllerutil.ContainsFinalizer(t.agent, consts.AnnotationKeyO11yAgentFinalizer) {
		controllerutil.AddFinalizer(t.agent, consts.AnnotationKeyO11yAgentFinalizer)
		// Require immediate requeue without running other tasks.
		return deploy.Panic(nil).WithRequeueAfter(0)
	}
	switch phase := t.agent.Phase(); phase {
	case "":
		t.agent.SetPhase(agentv2.O11yAgentPhaseCreating)
	case agentv2.O11yAgentPhaseCreating, agentv2.O11yAgentPhaseHibernated, agentv2.O11yAgentPhaseWakeUp, agentv2.O11yAgentPhaseNormal, agentv2.O11yAgentPhaseScaling, agentv2.O11yAgentPhasePanic:
	default:
		return deploy.Panic(fmt.Errorf("unexpected o11y agent phase during creation: %s", phase))
	}

	// Waking up
	if !t.agent.Paused() && t.agent.Phase() == agentv2.O11yAgentPhaseHibernated {
		t.agent.SetPhase(agentv2.O11yAgentPhaseWakeUp)
		// Require immediate requeue without running other tasks
		return deploy.Panic(nil).WithRequeueAfter(0)
	}
	return deploy.Done()
}

// Delete makes the o11y agent phase be one of:
//   - O11yAgentPhasePendingDelete
//   - O11yAgentPhaseDeleting
func (t *GuardianTask) Delete(ctx context.Context) deploy.Result {
	switch phase := t.agent.Phase(); phase {
	case agentv2.O11yAgentPhaseCreating, agentv2.O11yAgentPhaseNormal, agentv2.O11yAgentPhaseHibernated, agentv2.O11yAgentPhaseWakeUp, agentv2.O11yAgentPhasePanic, "":
		t.agent.SetPhase(agentv2.O11yAgentPhasePendingDelete)
		// Require immediate requeue without running other tasks.
		return deploy.Panic(nil).WithRequeueAfter(0)
	case agentv2.O11yAgentPhasePendingDelete:
		t.agent.SetPhase(agentv2.O11yAgentPhaseDeleting)
	case agentv2.O11yAgentPhaseDeleting:
	default:
		return deploy.Panic(fmt.Errorf("unexpected o11y agent phase during deletion: %s", phase))
	}
	return deploy.Done()
}
