package tidbtools

import (
	"fmt"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/devops/api"
	toolsdto "github.com/tidbcloud/devops/api/tidbtools"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
)

// ListRestores
//
//	@Id				TidbToolsControllerListRestores
//	@Description	list Dedicated or Premium restores
//	@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.ListRestoresResp
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/restores [get]
func (c *TidbToolsController) ListRestores(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}
	if !supportsBackupRestore(ci.clusterType, ci.servicePlan) {
		c.notImplemented(ctx, "ListRestores")
		return
	}
	page, pageSize, err := parsePaging(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	if ci.clusterType == logicalType {
		restoreID := strconv.FormatUint(clusterID, 10)
		resp, getErr := c.svc.GetPremiumRestoreStatus(ctx, ci.orgID, ci.projectID, clusterID)
		if getErr != nil {
			ctrl.Fail(ctx, getErr)
			return
		}
		if page > 1 {
			total := int64(0)
			if resp != nil {
				total = 1
			}
			ctrl.Success(ctx, &toolsdto.ListRestoresResp{Items: []*toolsdto.Restore{}, Total: total})
			return
		}
		ctrl.Success(ctx, toolsdto.ToListPremiumRestoresResp(restoreID, resp))
		return
	}
	resp, err := c.svc.ListRestores(ctx, ci.orgID, ci.projectID, clusterID, uint32(page), uint32(pageSize))
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}
	ctrl.Success(ctx, toolsdto.ToListRestoresResp(resp))
}

// GetRestoreProgress
//
//	@Id				TidbToolsControllerGetRestoreProgress
//	@Description	get Dedicated or Premium restore progress
//	@Tags			TidbToolsController
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Success		200			{object}	tidbtools.RestoreProgress
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/restores/progress [get]
func (c *TidbToolsController) GetRestoreProgress(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}
	if !supportsBackupRestore(ci.clusterType, ci.servicePlan) {
		c.notImplemented(ctx, "GetRestoreProgress")
		return
	}
	if ci.clusterType == logicalType {
		restoreID := strconv.FormatUint(clusterID, 10)
		resp, getErr := c.svc.GetPremiumRestore(ctx.Request.Context(), ci.orgID, ci.projectID, clusterID)
		if getErr != nil {
			ctrl.Fail(ctx, getErr)
			return
		}
		ctrl.Success(ctx, toolsdto.ToGetPremiumRestoreDiagnosticsResp(restoreID, resp))
		return
	}
	pb, err := c.svc.GetRestoreProgress(ctx.Request.Context(), ci.orgID, ci.projectID, clusterID)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}
	ctrl.Success(ctx, toolsdto.ToGetRestoreProgressResp(0, pb))
}

// GetRestore
//
//	@Id				TidbToolsControllerGetRestore
//	@Description	get a Dedicated restore by ID or the current Premium restore using its target cluster ID
//	@Tags			TidbToolsController
//	@Accept			json
//	@Produce		json
//	@Param			cluster_id	path		string	true	"cluster id"
//	@Param			restore_id	path		string	true	"restore ID; for Premium, the target cluster ID"
//	@Success		200			{object}	tidbtools.RestoreDetail
//	@Failure		default		{object}	api.BaseResp
//	@Router			/api/v1/devops/{cluster_id}/tidbtools/restores/{restore_id} [get]
func (c *TidbToolsController) GetRestore(ctx *gin.Context) {
	clusterID, ci, ok := c.requireClusterAccess(ctx)
	if !ok {
		return
	}
	if !supportsBackupRestore(ci.clusterType, ci.servicePlan) {
		c.notImplemented(ctx, "GetRestore")
		return
	}
	restoreIDStr := ctx.Param("restore_id")
	if ci.clusterType == logicalType {
		restoreID, err := strconv.ParseUint(restoreIDStr, 10, 64)
		if err != nil {
			ctrl.FailWithCode(ctx, fmt.Errorf("invalid restore_id %q: %w", restoreIDStr, err), api.ErrCodeInvalidArgument)
			return
		}
		// NextGen restore status is target-cluster scoped. The list endpoint
		// therefore exposes the target cluster ID as its only stable detail ID.
		if restoreID != clusterID {
			ctrl.FailWithCode(ctx, fmt.Errorf("restore %s not found", restoreIDStr), api.ErrCodeNotFound)
			return
		}
		resp, getErr := c.svc.GetPremiumRestoreStatus(ctx.Request.Context(), ci.orgID, ci.projectID, clusterID)
		if getErr != nil {
			ctrl.Fail(ctx, getErr)
			return
		}
		if resp == nil {
			ctrl.FailWithCode(ctx, fmt.Errorf("restore %s not found", restoreIDStr), api.ErrCodeNotFound)
			return
		}
		ctrl.Success(ctx, toolsdto.ToGetPremiumRestoreResp(strconv.FormatUint(clusterID, 10), restoreIDStr, resp))
		return
	}
	restoreID, err := strconv.ParseUint(restoreIDStr, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("invalid restore_id %q: %w", restoreIDStr, err), api.ErrCodeInvalidArgument)
		return
	}
	resp, err := c.svc.GetRestore(ctx.Request.Context(), ci.orgID, ci.projectID, clusterID, restoreID)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}
	ctrl.Success(ctx, toolsdto.ToGetRestoreResp(resp))
}
