package tidbtools

import (
	"fmt"
	"strconv"

	"github.com/gin-gonic/gin"
	ccproto "github.com/tidbcloud/config-center/proto/config_center"
	"github.com/tidbcloud/devops/api"
	toolsdto "github.com/tidbcloud/devops/api/tidbtools"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
)

// ListDMJobs
//
//	@Id				TidbToolsControllerListDMJobs
//	@Description	list DM jobs of a cluster
//	@Tags			TidbToolsController
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Param			page		query		int		false	"page, default 1"
//	@Param			page_size	query		int		false	"page size, default 20, max 200"
//	@Success		200			{object}	tidbtools.ListDMJobsResp
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/dm/jobs [get]
func (c *TidbToolsController) ListDMJobs(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}
	page, pageSize, err := parsePaging(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	switch {
	case ci.clusterType == dedicatedType:
		resp, err := c.svc.ListDMJobs(ctx, ci.orgID, ci.projectID, clusterID, page, pageSize)
		if err != nil {
			ctrl.Fail(ctx, err)
			return
		}
		ctrl.Success(ctx, toolsdto.ToListDMJobsResp(resp))
	case isPremium(ci.clusterType):
		resp, err := c.svc.ListPremiumDMJobs(ctx, strconv.FormatUint(clusterID, 10), pageSize)
		if err != nil {
			ctrl.Fail(ctx, err)
			return
		}
		ctrl.Success(ctx, toolsdto.ToPremiumListDMJobsResp(resp))
	default:
		c.notImplemented(ctx, "ListDMJobs")
	}
}

// GetDMOverview
//
//	@Id				TidbToolsControllerGetDMOverview
//	@Description	get DM version/overview for a cluster
//	@Tags			TidbToolsController
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Success		200			{object}	tidbtools.DMOverview
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/dm/overview [get]
func (c *TidbToolsController) GetDMOverview(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}

	overview := &toolsdto.DMOverview{}

	if c.cfgCenterCli != nil && ci.clusterType == dedicatedType {
		metaResp, err := c.cfgCenterCli.ConfigAPI.GetClusterMeta(ctx.Request.Context(), &ccproto.GetClusterMetaRequest{
			ClusterType:   ccproto.ClusterType_DEDICATED_TIDB,
			ClusterId:     strconv.FormatUint(clusterID, 10),
			MainComponent: ccproto.DEDICATED_TIDB_COMPONENT_DM,
		})
		if err != nil {
			ctrl.Fail(ctx, fmt.Errorf("get DM cluster meta: %w", err))
			return
		}
		for _, comp := range metaResp.GetComponents() {
			if comp.GetComponentName() == ccproto.DEDICATED_TIDB_COMPONENT_DM {
				overview.Version = comp.GetEffectiveImageTag()
				overview.VersionSource = "config-center"
				break
			}
		}
	}

	ctrl.Success(ctx, overview)
}

// GetDMJob
//
//	@Id				TidbToolsControllerGetDMJob
//	@Description	get a DM job by id
//	@Tags			TidbToolsController
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Param			job_id		path		string	true	"job id"
//	@Success		200			{object}	tidbtools.DMJobDetail
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/dm/jobs/{job_id} [get]
func (c *TidbToolsController) GetDMJob(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}
	jobIDStr := ctx.Param("job_id")

	switch {
	case ci.clusterType == dedicatedType:
		jobID, err := strconv.ParseUint(jobIDStr, 10, 64)
		if err != nil {
			ctrl.FailWithCode(ctx, fmt.Errorf("invalid job_id %q: %w", jobIDStr, err), api.ErrCodeInvalidArgument)
			return
		}
		resp, err := c.svc.GetDMJob(ctx, ci.orgID, ci.projectID, clusterID, jobID)
		if err != nil {
			ctrl.Fail(ctx, err)
			return
		}
		ctrl.Success(ctx, toolsdto.ToGetDMJobResp(resp))
	case isPremium(ci.clusterType):
		resp, err := c.svc.GetPremiumDMJob(ctx, strconv.FormatUint(clusterID, 10), jobIDStr)
		if err != nil {
			ctrl.Fail(ctx, err)
			return
		}
		ctrl.Success(ctx, toolsdto.ToPremiumGetDMJobResp(resp))
	default:
		c.notImplemented(ctx, "GetDMJob")
	}
}

// ListDMPrechecks
//
//	@Id				TidbToolsControllerListDMPrechecks
//	@Description	list DM prechecks of a cluster
//	@Tags			TidbToolsController
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Param			page		query		int		false	"page, default 1"
//	@Param			page_size	query		int		false	"page size, default 20, max 200"
//	@Success		200			{object}	tidbtools.ListDMPrechecksResp
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/dm/prechecks [get]
func (c *TidbToolsController) ListDMPrechecks(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}
	page, pageSize, err := parsePaging(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	switch {
	case ci.clusterType == dedicatedType:
		resp, err := c.svc.ListDMPrechecks(ctx, ci.orgID, ci.projectID, clusterID, page, pageSize)
		if err != nil {
			ctrl.Fail(ctx, err)
			return
		}
		ctrl.Success(ctx, toolsdto.ToListDMPrechecksResp(resp))
	case isPremium(ci.clusterType):
		resp, err := c.svc.ListPremiumDMPrechecks(ctx, strconv.FormatUint(clusterID, 10), pageSize)
		if err != nil {
			ctrl.Fail(ctx, err)
			return
		}
		ctrl.Success(ctx, toolsdto.ToPremiumListDMPrechecksResp(resp))
	default:
		c.notImplemented(ctx, "ListDMPrechecks")
	}
}

// GetDMPrecheck
//
//	@Id				TidbToolsControllerGetDMPrecheck
//	@Description	get a DM precheck by id
//	@Tags			TidbToolsController
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Param			precheck_id	path		string	true	"precheck id"
//	@Success		200			{object}	tidbtools.DMPrecheckDetail
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/dm/prechecks/{precheck_id} [get]
func (c *TidbToolsController) GetDMPrecheck(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}
	precheckIDStr := ctx.Param("precheck_id")

	switch {
	case ci.clusterType == dedicatedType:
		precheckID, err := strconv.ParseUint(precheckIDStr, 10, 64)
		if err != nil {
			ctrl.FailWithCode(ctx, fmt.Errorf("invalid precheck_id %q: %w", precheckIDStr, err), api.ErrCodeInvalidArgument)
			return
		}
		resp, err := c.svc.GetDMPrecheck(ctx, ci.orgID, ci.projectID, clusterID, precheckID)
		if err != nil {
			ctrl.Fail(ctx, err)
			return
		}
		ctrl.Success(ctx, toolsdto.ToGetDMPrecheckResp(precheckID, resp))
	case isPremium(ci.clusterType):
		resp, err := c.svc.GetPremiumDMPrecheck(ctx, strconv.FormatUint(clusterID, 10), precheckIDStr)
		if err != nil {
			ctrl.Fail(ctx, err)
			return
		}
		ctrl.Success(ctx, toolsdto.ToPremiumGetDMPrecheckResp(precheckIDStr, resp))
	default:
		c.notImplemented(ctx, "GetDMPrecheck")
	}
}
