package dummyworkflow

import (
	"context"
	"errors"
	"strconv"
	"time"

	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/pkgv2/log"
	"go.uber.org/zap"
)

const (
	ActionNameDummySleep        = "dummy-sleep-action"
	ActionNameDummySleepAsync   = "dummy-sleep-async-action"
	ActionNameDummyAlwaysFailed = "dummy-always-failed-action"
)

func BuildActionLib() *actions.ActionLibrary {
	actionLib := actions.NewActionLibrary()
	must(actionLib.Register(NewDummySleepAction(10*time.Second, 0)))
	must(actionLib.Register(NewDummySleepAction(15*time.Second, 1)))
	must(actionLib.Register(NewDummySleepAction(10*time.Second, 2)))
	must(actionLib.Register(NewDummySleepAction(15*time.Second, 3)))
	must(actionLib.Register(actions.CreateActionFromAsync(NewDummySleepAsyncAction(30*time.Second, 0), time.Second*10, time.Second*50)))
	must(actionLib.Register(actions.CreateActionFromAsync(NewDummySleepAsyncAction(30*time.Second, 1), time.Second*10, time.Second*50)))
	must(actionLib.Register(NewDummyAlwaysAction()))
	return actionLib
}

type DummySleepAction struct {
	dur   time.Duration
	index int
}

func NewDummySleepAction(dur time.Duration, index int) *DummySleepAction {
	return &DummySleepAction{dur: dur, index: index}
}

func (a *DummySleepAction) Name() string {
	return ActionNameDummySleep + "-" + strconv.Itoa(a.index)
}

func (a *DummySleepAction) Invoke(args actions.ActionArgs) error {
	ctx := context.TODO()
	log.Info(ctx, "dummy sleep action is invoked")
	for k, v := range args {
		log.Info(ctx, "dummy sleep action args", zap.String(k, v.String()))
	}
	time.Sleep(a.dur)
	return nil
}

type DummySleepAsyncAction struct {
	startUnix int64
	dur       time.Duration
	index     int
}

func NewDummySleepAsyncAction(dur time.Duration, index int) *DummySleepAsyncAction {
	return &DummySleepAsyncAction{dur: dur, index: index}
}

func (a *DummySleepAsyncAction) Name() string {
	return ActionNameDummySleepAsync + "-" + strconv.Itoa(a.index)
}

func (a *DummySleepAsyncAction) Start(args actions.ActionArgs) error {
	ctx := context.TODO()
	log.Info(ctx, "dummy sleep async action starts")
	for k, v := range args {
		log.Info(ctx, "dummy sleep async action args", zap.String(k, v.String()))
	}
	a.startUnix = time.Now().Local().Unix()
	return nil
}

func (a *DummySleepAsyncAction) CheckFinish(args actions.ActionArgs) (bool, error) {
	ctx := context.TODO()
	currUnix := time.Now().Local().Unix()
	finished := currUnix-a.startUnix > int64(a.dur.Seconds())
	log.Info(ctx, "dummy sleep async action is checked", zap.Bool("finished", finished))
	for k, v := range args {
		log.Info(ctx, "dummy sleep async action args", zap.String(k, v.String()))
	}
	return finished, nil
}

type DummyAlwaysFailedAction struct{}

func NewDummyAlwaysAction() *DummyAlwaysFailedAction {
	return &DummyAlwaysFailedAction{}
}

func (a *DummyAlwaysFailedAction) Name() string {
	return ActionNameDummyAlwaysFailed
}

func (a *DummyAlwaysFailedAction) Invoke(args actions.ActionArgs) error {
	log.Info(context.TODO(), "dummy always failed action is invoked")
	return errors.New("dummy always failed action must failed")
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}
