package action

import (
	"fmt"
	"strconv"

	apprent "github.com/tidbcloud/devops/entity/approval"
	k8sent "github.com/tidbcloud/devops/entity/k8supgrade"
	apprapi "github.com/tidbcloud/devops/pkg/service/approval/api"
)

const (
	ApprovalParamTaskID              = "task_id"
	ApprovalParamVersionToBeUpgraded = "version_to_be_upgraded"
	ApprovalParamCloudProvider       = "cloud_provider"
	ApprovalParamMaxRestartTimes     = "max_restart_times"
)

type actorCreate struct {
	*baseActor
}

func (a *actorCreate) SendApproval() error {
	ctx := a.Context

	createApprParam, err := a.buildParamToCreateApproval()
	if err != nil {
		return fmt.Errorf("build param to create approval failed: %w", err)
	}

	apprID, err := a.ApprovalService.CreateApproval(ctx, createApprParam)
	if err != nil {
		return fmt.Errorf("create approval failed: %w", err)
	}

	a.Logger.Info("create approval", "approve id", *apprID, "param", createApprParam)
	err = a.PatchTask(ctx,
		func(cur *k8sent.Task) error {
			cur.ApprovalID = apprID
			cur.Status = k8sent.TaskStatusApproving
			return nil
		},
	)
	if err != nil {
		return fmt.Errorf("approval sent but transition status to approving failed: %w", err)
	}

	return nil

}

func (a *actorCreate) Cancel() error {
	return a.cancel()
}

func (a *actorCreate) buildParamToCreateApproval() (apprapi.CreateApprovalParam, error) {
	//ctx := a.Context
	task := a.Task

	param := apprapi.CreateApprovalParam{
		CreateApprovalParamBase: apprapi.CreateApprovalParamBase{
			Name:       "Upgrade K8S", // TODO
			Applicant:  task.Applicant,
			DetailLink: DetailLink(a.Conf.DetailLinkBaseURL, task.ID),
			Parameters: map[string]string{
				ApprovalParamTaskID:              strconv.FormatUint(task.ID, 10),
				ApprovalParamVersionToBeUpgraded: task.VersionToBeUpgraded,
				ApprovalParamCloudProvider:       task.CloudProvider,
				ApprovalParamMaxRestartTimes:     strconv.FormatUint(uint64(task.MaxRestartTimes), 10),
				// others are injected later.
			},
		},
		Nodes:   nil,
		CCTasks: nil,
	}

	// For dev/staging env, we don't need approval for automated test.
	//if a.BaseConf.Env == consts.EnvironmentDev || a.BaseConf.Env == consts.EnvStaging {
	//	return param, nil
	//}

	if len(a.Conf.K8sUpgradeTaskApprovers) != 0 {
		nodeParam, err := BuildApprovers(a.Conf.K8sUpgradeTaskApprovers)
		if err != nil {
			return param, fmt.Errorf("build approvers failed: %w", err)
		}

		param.Nodes = append(param.Nodes, &apprent.ApproverNode{
			Method:   apprent.ApproveMethodAny,
			Approver: nodeParam,
		})
	}

	if (len(a.Conf.K8sUpgradeTaskCCEmails) != 0) || (a.Conf.K8sUpgradeTaskCCUserGroup != nil) {
		nodeParam, err := BuildApprovalCC(a.Conf.K8sUpgradeTaskCCEmails, a.Conf.K8sUpgradeTaskCCUserGroup, task.Regions)
		if err != nil {
			return param, fmt.Errorf("build cc approvers failed: %w", err)
		}

		param.CCTasks = nodeParam
	}

	return param, nil
}

func BuildApprovers(apprs []string) (*apprent.Approver, error) {
	result := &apprent.Approver{}

	if len(apprs) != 0 {
		result.Emails = append(result.Emails, apprs...)
	}

	return result, nil
}

func BuildApprovalCC(emails []string, userGroup map[string]string, regions map[string]int64) (*apprent.Approver, error) {
	result := &apprent.Approver{}

	if len(emails) != 0 {
		result.Emails = append(result.Emails, emails...)
	}

	for k, v := range regions {
		if v > 0 {
			result.UserGroups = append(result.UserGroups, userGroup[k])
		}
	}

	return result, nil
}
