package cluster

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/devops/api"
	k8supgradeapi "github.com/tidbcloud/devops/api/k8supgrade"
	entity "github.com/tidbcloud/devops/entity/k8supgrade"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	contutil "github.com/tidbcloud/devops/pkg/infra/utils/container"
	taskapi "github.com/tidbcloud/devops/pkg/service/k8supgrade/task/api"
)

// ListK8sUpgradeTasks
//
//	@Id				ListK8sUpgradeTasks
//	@Description	list k8s upgrade tasks
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			req	query		k8supgradeapi.ListTasksReq	true	"ListTasksReq"
//	@Success		200	{object}	k8supgradeapi.ListTasksResp
//	@Failure		500	{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks [get]
func (c *ClusterController) ListK8sUpgradeTasks(ctx *gin.Context) {
	req := &k8supgradeapi.ListTasksReq{}
	if err := ctx.ShouldBindQuery(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	list, total, err := c.k8sUpgradeTaskSvc.ListTasks(ctx, taskapi.ListTasksParam{
		CloudProviders: contutil.TransformSlice(req.CloudProvider, contutil.FromString[string]),
		EOSVersions:    contutil.TransformSlice(req.EOSVersion, contutil.FromString[string]),
		Status:         contutil.TransformSlice(req.Status, contutil.FromString[entity.TaskStatus]),
		Page:           int(req.Page),
		PerPage:        int(req.PerPage),
		SortBy:         taskapi.SortByType(req.SortBy),
		Order:          taskapi.OrderType(req.Order),
	})
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &k8supgradeapi.ListTasksResp{
		Total: total,
		Tasks: list,
	})
}

// GetK8sUpgradeTask
//
//	@Id				GetK8sUpgradeTask
//	@Description	get specific k8s upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id						path		string	true	"task id"
//	@Param			with_excluded_k8s_clusters	query		bool	false	"whether to include excluded k8s clusters in the response (default true)"
//	@Success		200							{object}	k8supgradeapi.GetTaskResp
//	@Failure		500							{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id} [get]
func (c *ClusterController) GetK8sUpgradeTask(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	withExcluded := true
	if v := ctx.Query("with_excluded_k8s_clusters"); v != "" {
		withExcluded, _ = strconv.ParseBool(v)
	}
	t, err := c.k8sUpgradeTaskSvc.GetTask(ctx, taskapi.GetTaskParam{
		TaskID:                  tid,
		WithExcludedK8sClusters: withExcluded,
	})
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &k8supgradeapi.GetTaskResp{
		Task: *t,
	})
}

// CreateK8sUpgradeTask
//
//	@Id				CreateK8sUpgradeTask
//	@Description	create a k8s upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			req	body		k8supgradeapi.CreateTaskReq	true	"request body"
//	@Success		200	{object}	k8supgradeapi.CreateTaskResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks [post]
func (c *ClusterController) CreateK8sUpgradeTask(ctx *gin.Context) {
	req := &k8supgradeapi.CreateTaskReq{}
	if err := ctx.ShouldBindJSON(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	if req.CloudProvider == "gcp" && req.MaxRestartTimes == 0 {
		ctrl.FailWithCode(ctx, fmt.Errorf("max restart times must be greater than 0 for gcp"), api.ErrCodeInvalidArgument)
		return
	}

	all, err := c.k8sUpgradeTaskSvc.ListSupportedEOSVersions(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	versions := all[req.CloudProvider]
	if len(versions) > 0 {
		ok := false
		for _, v := range versions {
			if strings.EqualFold(v, req.EOSVersion) {
				ok = true
				break
			}
		}
		if !ok {
			ctrl.FailWithCode(ctx, fmt.Errorf("unsupported eos_version %q for cloud_provider %q", req.EOSVersion, req.CloudProvider), api.ErrCodeInvalidArgument)
			return
		}
	}

	v, exist := ctx.Get(consts.CtxUserEmail)
	if !exist {
		ctrl.FailWithCode(ctx, fmt.Errorf("account not found in ctx"), api.ErrCodeInternal)
		return
	}
	email := v.(string)

	id, err := c.k8sUpgradeTaskSvc.CreateTask(ctx, taskapi.CreateTaskParam{
		Name:            req.Name,
		CloudProvider:   req.CloudProvider,
		EOSVersion:      req.EOSVersion,
		MaxRestartTimes: req.MaxRestartTimes,
		Deadline:        req.Deadline,
		Applicant:       email,
		SelectorType:    req.SelectorType,
		SelectorValue:   req.SelectorValue,
	})
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	resp := &k8supgradeapi.CreateTaskResp{ID: &id}
	ctrl.Success(ctx, resp)
}

// CloseK8sUpgradeTask
//
//	@Id				CloseK8sUpgradeTask
//	@Description	close a k8s upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id	path		string						true	"task id"
//	@Param			req		body		k8supgradeapi.CloseTaskReq	true	"request body"
//	@Success		200		{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/close [post]
func (c *ClusterController) CloseK8sUpgradeTask(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	//req := &k8supgradeapi.CloseTaskReq{}
	//if err := ctx.ShouldBindJSON(req); err != nil {
	//	ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
	//	return
	//}

	err = c.k8sUpgradeTaskSvc.CloseTask(ctx, tid)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

// UpdateK8sUpgradeTask
//
//	@Id				UpdateK8sUpgradeTask
//	@Description	update a k8s upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id	path		string						true	"task id"
//	@Param			req		body		k8supgradeapi.UpdateTaskReq	true	"request body"
//	@Success		200		{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id} [put]
func (c *ClusterController) UpdateK8sUpgradeTask(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	req := &k8supgradeapi.UpdateTaskReq{}
	if err := ctx.ShouldBindJSON(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := taskapi.UpdateTaskParam{TaskToUpdate: &entity.Task{Synced: false}}
	if req.Name != nil {
		param.TaskToUpdate.Name = *req.Name
	}
	if req.MaxRestartTimes != nil {
		param.TaskToUpdate.MaxRestartTimes = *req.MaxRestartTimes
	}
	if req.Deadline != nil {
		param.TaskToUpdate.Deadline = *req.Deadline
	}

	err = c.k8sUpgradeTaskSvc.UpdateTask(ctx, tid, param)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

// SubmitK8sUpgradeTask
//
//	@Id				SubmitK8sUpgradeTask
//	@Description	submit a k8s upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id	path		string	true	"task id"
//	@Success		200		{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/submit [post]
func (c *ClusterController) SubmitK8sUpgradeTask(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	if err := c.k8sUpgradeTaskSvc.SubmitTask(ctx, tid); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

// GetK8sUpgradeTaskCluster
//
//	@Id				GetK8sUpgradeTaskCluster
//	@Description	get specific k8s cluster of an upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id		path		string	true	"task id"
//	@Param			k8s_name	path		string	true	"k8s name"
//	@Success		200			{object}	k8supgradeapi.GetTaskK8sClusterResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/{k8s_name} [get]
func (c *ClusterController) GetK8sUpgradeTaskCluster(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	k8sName := ctx.Param("k8s_name")
	if len(k8sName) == 0 {
		ctrl.FailWithCode(ctx, fmt.Errorf("k8s cluster name is empty: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	cluster, err := c.k8sUpgradeTaskSvc.GetK8sCluster(ctx, taskapi.GetK8sClusterParam{
		TaskID:  tid,
		K8sName: k8sName,
	})
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &k8supgradeapi.GetTaskK8sClusterResp{
		K8sCluster: *cluster,
	})
}

// UpdateK8sUpgradeTaskCluster
//
//	@Id				UpdateK8sUpgradeTaskCluster
//	@Description	update specific k8s cluster of an upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id		path		string									true	"task id"
//	@Param			k8s_name	path		string									true	"k8s name"
//	@Param			req			body		k8supgradeapi.UpdateTaskK8sClusterReq	true	"request"
//	@Success		200			{object}	k8supgradeapi.UpdateTaskK8sClusterResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/{k8s_name} [put]
func (c *ClusterController) UpdateK8sUpgradeTaskCluster(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	k8sName := ctx.Param("k8s_name")
	if len(k8sName) == 0 {
		ctrl.FailWithCode(ctx, fmt.Errorf("k8s cluster name is empty: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	req := &k8supgradeapi.UpdateTaskK8sClusterReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	err = c.k8sUpgradeTaskSvc.UpdateK8sCluster(ctx, tid, k8sName, taskapi.UpdateK8sClusterParam{
		Cluster: req.ToK8sClusterEntity(),
	})
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &k8supgradeapi.UpdateTaskK8sClusterResp{})
}

// UpdateK8sUpgradeTaskClusterAutoGeneration
//
//	@Id				UpdateK8sUpgradeTaskClusterAutoGeneration
//	@Description	update the DisableAutoGeneration field of a specific k8s cluster of an upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id		path		string														true	"task id"
//	@Param			k8s_name	path		string														true	"k8s name"
//	@Param			req			body		k8supgradeapi.UpdateK8sUpgradeTaskClusterAutoGenerationReq	true	"request"
//	@Success		200			{object}	k8supgradeapi.UpdateK8sUpgradeTaskClusterAutoGenerationResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/{k8s_name}/autoGeneration [put]
func (c *ClusterController) UpdateK8sUpgradeTaskClusterAutoGeneration(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	k8sName := ctx.Param("k8s_name")
	if len(k8sName) == 0 {
		ctrl.FailWithCode(ctx, fmt.Errorf("k8s cluster name is empty: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	req := &k8supgradeapi.UpdateK8sUpgradeTaskClusterAutoGenerationReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	err = c.k8sUpgradeTaskSvc.UpdateK8sClusterAutoGeneration(ctx, tid, k8sName, req.DisableAutoGeneration)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &k8supgradeapi.UpdateK8sUpgradeTaskClusterAutoGenerationResp{})
}

// ExcludeK8sUpgradeTaskCluster
//
//	@Id				ExcludeK8sUpgradeTaskCluster
//	@Description	exclude a specific k8s cluster of an upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id		path		string	true	"task id"
//	@Param			k8s_name	path		string	true	"k8s name"
//	@Success		200			{object}	k8supgradeapi.ExcludeK8sUpgradeTaskClusterResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/{k8s_name}/exclude [put]
func (c *ClusterController) ExcludeK8sUpgradeTaskCluster(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	k8sName := ctx.Param("k8s_name")
	if len(k8sName) == 0 {
		ctrl.FailWithCode(ctx, fmt.Errorf("k8s cluster name is empty: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	err = c.k8sUpgradeTaskSvc.ExcludeK8sCluster(ctx, tid, k8sName)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &k8supgradeapi.ExcludeK8sUpgradeTaskClusterResp{})
}

// CancelUpgradeK8sCluster
//
//	@Id				CancelUpgradeK8sCluster
//	@Description	cancel the upgrade of a specific k8s cluster for a running task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id		path		string	true	"task id"
//	@Param			k8s_name	path		string	true	"k8s name"
//	@Success		200			{object}	k8supgradeapi.CancelUpgradeK8sClusterResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/{k8s_name}/cancel [put]
func (c *ClusterController) CancelUpgradeK8sCluster(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	k8sName := ctx.Param("k8s_name")
	if len(k8sName) == 0 {
		ctrl.FailWithCode(ctx, fmt.Errorf("k8s cluster name is empty: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	err = c.k8sUpgradeTaskSvc.CancelUpgradeK8sCluster(ctx, tid, k8sName)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &k8supgradeapi.CancelUpgradeK8sClusterResp{})
}

// DeleteK8sUpgradeTask
//
//	@Id				DeleteK8sUpgradeTask
//	@Description	delete a k8s upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id	path		string	true	"task id"
//	@Success		200		{object}	api.BaseResp
//	@Failure		500		{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id} [delete]
func (c *ClusterController) DeleteK8sUpgradeTask(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}

	err = c.k8sUpgradeTaskSvc.DeleteTask(ctx, tid)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &api.BaseResp{})
}

// GetK8sUpgradeTaskNotice
//
//	@Id				GetK8sUpgradeTaskNotice
//	@Description	get rendered notice content for a k8s upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id		path		string	true	"task id"
//	@Param			page_url	query		string	false	"page URL used when rendering notice template placeholders"
//	@Success		200			{object}	k8supgradeapi.RenderedNoticeResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/notice [get]
func (c *ClusterController) GetK8sUpgradeTaskNotice(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	pageURL := ctx.Query("page_url")
	content, err := c.k8sUpgradeTaskSvc.GetTaskNotice(ctx, tid, pageURL)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &k8supgradeapi.RenderedNoticeResp{Content: content})
}

// UpdateK8sUpgradeTaskNotice
//
//	@Id				UpdateK8sUpgradeTaskNotice
//	@Description	update notice template (title and content) for a k8s upgrade task
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			task_id	path		string							true	"task id"
//	@Param			req		body		k8supgradeapi.NoticeTemplateReq	true	"notice template"
//	@Success		200		{object}	api.BaseResp
//	@Failure		500		{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/notice [put]
func (c *ClusterController) UpdateK8sUpgradeTaskNotice(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	req := &k8supgradeapi.NoticeTemplateReq{}
	if err := ctx.ShouldBindJSON(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	email, _ := ctx.Get(consts.CtxUserEmail)
	sb, _ := email.(string)
	if err := c.k8sUpgradeTaskSvc.UpsertTaskNotice(ctx, tid, req.Title, req.Content, sb); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

// NotifyK8sUpgradeTask
//
//	@Id				NotifyK8sUpgradeTask
//	@Description	Notify Sales Owner / TAM for matching clusters in an upgrade task
//	@Tags			Cluster
//	@Accept			json
//	@Produce		json
//	@Param			task_id	path		int						true	"Task ID"
//	@Param			req		body		k8supgradeapi.NotifyReq	true	"request body"
//	@Success		200		{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/k8sUpgradeTasks/{task_id}/notify [post]
func (c *ClusterController) NotifyK8sUpgradeTask(ctx *gin.Context) {
	tid, err := strconv.ParseUint(ctx.Param("task_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("task id is not a int: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	req := &k8supgradeapi.NotifyReq{}
	if err := ctx.ShouldBindJSON(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	sentBy, _ := ctx.Get(consts.CtxUserEmail)
	sb, _ := sentBy.(string)
	if err := c.k8sUpgradeTaskSvc.NotifyTask(ctx, entity.NotifyParam{
		TaskID:        tid,
		Channels:      req.Channels,
		CustomMessage: req.CustomMessage,
		SentBy:        sb,
	}); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}
