package task

import (
	"context"
	"time"

	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/types"

	"github.com/tidbcloud/eks-provider/api/v1alpha1"
	"github.com/tidbcloud/infra-provider/pkg/deploy"
	"github.com/tidbcloud/infra-provider/pkg/k8s"
	"github.com/tidbcloud/infra-provider/pkg/util"

	"github.com/tidbcloud/chaos-provider/controllers/chaos/utils"
)

type ChaosWorkflowNodeTask struct {
	*BaseTask
}

func NewChaosWorkflowNodeTask(ctx *Context) *ChaosWorkflowNodeTask {
	return &ChaosWorkflowNodeTask{
		BaseTask: &BaseTask{Context: ctx},
	}
}

func (t *ChaosWorkflowNodeTask) Name() string {
	return "Chaos WorkflowNode"
}

func (t *ChaosWorkflowNodeTask) Create(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}

	// Get ChaosWorkflowNodes by ChaosWorkflow
	chaosWorkflowNodes, err := utils.FetchEntryNode(ctx, cli, *t.ChaosWorkflow)
	if err != nil {
		return deploy.Panic(err).WithRequeuAfter(0)
	}

	for _, node := range chaosWorkflowNodes {
		if err := t.Local.Create(ctx, &node); err != nil && !apierrors.IsAlreadyExists(err) {
			return deploy.Error(util.WrapError("create chaos workflow node", err))
		}
	}

	// Get ResourceVersion by tmpNodes
	var nodeList []v1alpha1.ChaosWorkflowNode
	var tmpNode v1alpha1.ChaosWorkflowNode
	for _, node := range chaosWorkflowNodes {
		if err := t.Local.Get(ctx, types.NamespacedName{Name: node.Name, Namespace: node.Namespace}, &tmpNode); err != nil {
			return deploy.Error(util.WrapError("get chaos workflow node", err))
		}
		tmpNode.ObjectMeta.OwnerReferences = []metav1.OwnerReference{
			{
				APIVersion: t.ChaosWorkflow.APIVersion,
				Kind:       t.ChaosWorkflow.Kind,
				Name:       t.ChaosWorkflow.Name,
				UID:        t.ChaosWorkflow.UID,
			},
		}
		nodeList = append(nodeList, tmpNode)
	}
	t.ChaosWorkflowNodes = &nodeList

	return deploy.Done()
}

func (t *ChaosWorkflowNodeTask) PostCreate(ctx context.Context) deploy.Result {
	cli, err := t.Remote.GetClient(ctx)
	if err != nil {
		return deploy.Panic(err)
	}

	// Get ChaosWorkflowNodes by ChaosWorkflow
	chaosWorkflowNodes, err := utils.FetchEntryNode(ctx, cli, *t.ChaosWorkflow)
	finishedCount := 0
	if err != nil {
		return deploy.Panic(err).WithRequeuAfter(0)
	}
	for i, remoteNode := range chaosWorkflowNodes {
		for j, localNode := range *t.ChaosWorkflowNodes {
			if remoteNode.Name == localNode.Name {
				(*t.ChaosWorkflowNodes)[j].Status = chaosWorkflowNodes[i].Status
				if v1alpha1.ChaosWorkflowNodeFinished(localNode.Status) {
					finishedCount++
				}
			}
		}
	}
	if finishedCount == len(*t.ChaosWorkflowNodes) {
		return deploy.Done()
	}

	return deploy.Error(k8s.RequeueOnlyError).WithRequeuAfter(10 * time.Second)
}
