package action

import (
	"fmt"
	"time"

	apiv1 "github.com/tidbcloud/cluster-service-ng/api/cluster-service-ng/v1beta"
	"github.com/tidbcloud/cluster-service-ng/domain/entity"
	"github.com/tidbcloud/cluster-service-ng/domain/workflow"
	"github.com/tidbcloud/cluster-service-ng/pkg/runtime/actions"
	"github.com/tidbcloud/cluster-service-ng/pkg/storage/repo"
	"github.com/tidbcloud/cluster-service-ng/pkg/utils"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.uber.org/zap"
)

type PreCreateTiDBClusterAction struct {
	sharedPoolRepo repo.SharedPoolRepo
}

const PreCreateTiDBClusterActionName = "pre-create-tidbcluster"

func NewPreCreateTiDBClusterAction(
	sharedPoolRepo repo.SharedPoolRepo,
) actions.Action {
	return &PreCreateTiDBClusterAction{
		sharedPoolRepo: sharedPoolRepo,
	}
}

func (a *PreCreateTiDBClusterAction) Name() string {
	return PreCreateTiDBClusterActionName
}

func (a *PreCreateTiDBClusterAction) Invoke(args actions.ActionArgs) error {
	// generate context
	ctx := utils.GenerateContext(args)
	tilog.Info(ctx, "pre create tidb cluster action invoked")

	sharedPoolId, err := args.GetString(workflow.SharedPoolIdKey)
	if err != nil {
		return fmt.Errorf("failed to get shared pool id: %w", err)
	}

	// check shared pool status
	sharedPool, err := a.sharedPoolRepo.Load(ctx, sharedPoolId, false)
	if err != nil {
		tilog.Error(ctx, "failed to load shared pool", zap.String("sharedPoolId", sharedPoolId), zap.Error(err))
		return fmt.Errorf("failed to load shared pool: %w", err)
	}

	if sharedPool.GetPhase() == apiv1.SharedPool_DELETING {
		return actions.NewTerminalError(fmt.Errorf("shared pool %s is deleting", sharedPool.GetId()))
	}
	if sharedPool.PauseResumeState == entity.PauseResumeStateResuming {
		return fmt.Errorf("shared pool %s is waiting for resume to complete", sharedPool.GetId())
	}

	if sharedPool.GetPhase() == apiv1.SharedPool_UNAVAILABLE || sharedPool.GetPhase() == apiv1.SharedPool_CREATING {
		tilog.Error(ctx, "shared pool is unavailable or creating", zap.String("sharedPoolId", sharedPool.GetId()), zap.String("phase", sharedPool.GetPhase().String()))
		// Optional: wait a bit before returning an error
		// TODO: remove this sleep in production code
		time.Sleep(20 * time.Second)

		return fmt.Errorf("shared pool %s is not available", sharedPool.GetId())
	}

	return nil
}
