package action

import (
	"context"
	"fmt"

	"github.com/go-logr/logr"

	baseapi "github.com/tidbcloud/devops/api"
	"github.com/tidbcloud/devops/entity/event"
	k8sent "github.com/tidbcloud/devops/entity/k8supgrade"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/errs"
	"github.com/tidbcloud/devops/pkg/infra/utils/pointer"
	apprsvcapi "github.com/tidbcloud/devops/pkg/service/approval/api"
	"github.com/tidbcloud/devops/pkg/service/k8supgrade/task/api"
	"github.com/tidbcloud/devops/pkg/service/k8supgrade/task/repo"
)

type CreateK8sUpgradeTaskParam struct {
}

type Objects struct {
	// Origin is the original instance object which is used to patch.
	//
	// NOTE: **DO NOT** modify it.
	Origin *k8sent.Task
	// Instance is the copied instance object that is used in action.
	Task *k8sent.Task
	// EventDetail contains the event detail of the instance.
	EventDetail *event.K8sUpgradeTaskEventDetail
}

type Components struct {
	Logger          logr.Logger
	Conf            config.K8sUpgradeServiceConfig
	Repo            repo.Repo
	ApprovalService apprsvcapi.ApprovalServiceAPI
	//CoreSvcCli      *coresvccli.CoreSvcCli
}

type Context struct {
	context.Context
	Components
	Objects
}

func (c *Context) PatchTask(ctx context.Context, patch ...repo.PatchTask) error {
	return c.Repo.PatchTask(ctx, c.Origin, patch...)
}

type K8sUpgradeTaskActor interface {
	// GetContext returns the context of the actor.
	GetContext() *Context
	// Create validates the param and creates a new k8s upgrade task.
	Create(param api.CreateTaskParam) error
	// SendApproval send an approval to approve this operation.
	SendApproval() error
	// ContinueIfApproved check the approval status and transition the phase.
	ContinueIfApproved() error
	// Cancel close the change and do nothing.
	Cancel() error
}

func NewK8sUpgradeTaskActor(ctx context.Context, comps Components, task *k8sent.Task) K8sUpgradeTaskActor {
	var a K8sUpgradeTaskActor
	var err error
	actx := &Context{
		Context:    ctx,
		Components: comps,
		Objects: Objects{
			Origin:      task,
			EventDetail: &event.K8sUpgradeTaskEventDetail{},
		},
	}

	if actx.Origin == nil {
		return &actorCreate{
			baseActor: &baseActor{
				Context:    actx,
				defaultErr: errs.APIErrorf(baseapi.ErrCodeFailedPrecond, "only support to create a new k8s upgrade task"),
			},
		}
	}

	actx.Task, err = pointer.DeepCopy(actx.Origin)
	if err != nil {
		return &baseActor{defaultErr: errs.APIErrorf(baseapi.ErrCodeFailedPrecond, "copy task failed: %s", err)}
	}

	status := actx.Task.Status
	base := &baseActor{
		Context: actx,
		defaultErr: errs.APIErrorf(baseapi.ErrCodeFailedPrecond,
			"unsupported action when status is %s", actx.Origin.Status,
		),
	}
	switch {
	case status == k8sent.TaskStatusPending:
		a = &actorCreate{baseActor: base}
	case status == k8sent.TaskStatusApproving:
		a = &actorApproving{baseActor: base}
	case status == k8sent.TaskStatusRunning:
		a = &actorRunning{baseActor: base}
	default:
		base.defaultErr = fmt.Errorf("unknown task status: %s", actx.Task.Status)
		a = base
	}
	return a
}

type baseActor struct {
	*Context

	defaultErr error
}

func (a *baseActor) GetContext() *Context {
	return a.Context
}

func (a *baseActor) Create(param api.CreateTaskParam) error {
	return a.defaultErr
}

func (a *baseActor) SendApproval() error {
	return a.defaultErr
}

func (a *baseActor) ContinueIfApproved() error {
	return a.defaultErr
}

func (a *baseActor) Cancel() error {
	return a.defaultErr
}

func (a *baseActor) cancel() error {
	ctx := a.Context

	// transition status to 'cancelled'
	err := a.PatchTask(ctx, PatchTaskToClose(k8sent.TaskStatusCancelled))
	return err
}
