package cluster

import (
	"fmt"
	"strconv"
	"time"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/devops/api"
	clusterapi "github.com/tidbcloud/devops/api/cluster"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	cmxapi "github.com/tidbcloud/devops/pkg/service/clustermaintenance/api"
)

// ListMaintenances
//
//	@Id				ListMaintenances
//	@Description	list maintenances
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			req	query		clusterapi.ListMaintenancesReq	false	"request param"
//	@Success		200	{object}	clusterapi.ListMaintenancesResp
//	@Router			/api/v1/devops/cluster/maintenances [get]
func (c *ClusterController) ListMaintenances(ctx *gin.Context) {
	req := &clusterapi.ListMaintenancesReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	projectIDs := make([]uint64, 0)
	for _, projectIDStr := range req.ProjectIDs {
		projectIDU64, err := strconv.ParseUint(projectIDStr, 10, 64)
		if err != nil {
			ctrl.Fail(ctx, err)
		}
		projectIDs = append(projectIDs, projectIDU64)
	}
	param := cmxapi.ListMaintenanceParam{
		GetMaintenanceParam: cmxapi.GetMaintenanceParam{
			WithExecutions: false,
		},
		ProjectIDs: projectIDs,
		Statues:    req.Statues,

		Page:    int64(req.Page),
		PerPage: int64(req.PerPage),
	}
	maintenances, total, err := c.maintenanceSvc.ListMaintenances(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &clusterapi.ListMaintenancesResp{Maintenances: maintenances, Total: total}
	ctrl.Success(ctx, resp)
}

// GetMaintenance
//
//	@Id				GetMaintenance
//	@Description	get a maintenances
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			maintenance_id	path		int								true	"maintenance id"
//	@Param			req				query		clusterapi.GetMaintenanceReq	false	"request param"
//	@Success		200				{object}	clusterapi.GetMaintenanceResp
//	@Router			/api/v1/devops/cluster/maintenances/{maintenance_id} [get]
func (c *ClusterController) GetMaintenance(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("maintenance_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	req := &clusterapi.GetMaintenanceReq{}
	err = ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := cmxapi.GetMaintenanceParam{
		WithExecutions: req.WithExecutions,
	}
	maintenance, err := c.maintenanceSvc.GetMaintenance(ctx, id, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &clusterapi.GetMaintenanceResp{Maintenance: maintenance}
	ctrl.Success(ctx, resp)
}

// CreateMaintenance
//
//	@Id				CreateMaintenance
//	@Description	create a maintenances
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			req	body		clusterapi.CreateMaintenanceReq	false	"request param"
//	@Success		200	{object}	clusterapi.CreateMaintenanceResp
//	@Router			/api/v1/devops/cluster/maintenances [post]
func (c *ClusterController) CreateMaintenance(ctx *gin.Context) {
	req := &clusterapi.CreateMaintenanceReq{}
	err := ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	// parse the project id and cluster id.
	projectID, err := strconv.ParseUint(req.ProjectID, 10, 64)
	if err != nil {
		ctrl.Fail(ctx, err)
	}

	clusterIDs := make([]uint64, 0)
	for _, v := range req.ClusterIDs {
		clusterID, err := strconv.ParseUint(v, 10, 64)
		if err != nil {
			ctrl.Fail(ctx, err)
		}
		clusterIDs = append(clusterIDs, clusterID)
	}

	param := cmxapi.CreateMaintenanceParam{
		ProjectID:   projectID,
		Description: req.Description,
		Deadline:    time.Unix(req.Deadline, 0),
		ClusterIDs:  clusterIDs,
	}
	id, err := c.maintenanceSvc.CreateMaintenance(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &clusterapi.CreateMaintenanceResp{ID: id}
	ctrl.Success(ctx, resp)
}

// CloseMaintenance
//
//	@Id				CloseMaintenance
//	@Description	close a maintenances
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			maintenance_id	path		int	true	"maintenance id"
//	@Success		200				{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/maintenances/{maintenance_id}/close [post]
func (c *ClusterController) CloseMaintenance(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("maintenance_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	err = c.maintenanceSvc.CloseMaintenance(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// StartMaintenanceExecution
//
//	@Id				StartMaintenanceExecution
//	@Description	start a maintenance executions
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			maintenance_id	path		int	true	"maintenance id"
//	@Param			execution_id	path		int	true	"execution id"
//	@Success		200				{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/maintenances/{maintenance_id}/executions/{execution_id}/start [post]
func (c *ClusterController) StartMaintenanceExecution(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("maintenance_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}
	execID, err := strconv.ParseUint(ctx.Param("execution_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	err = c.maintenanceSvc.StartMaintenanceExecution(ctx, id, execID)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// FinishMaintenanceExecution
//
//	@Id				FinishMaintenanceExecution
//	@Description	finish a maintenance executions
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			maintenance_id	path		int	true	"maintenance id"
//	@Param			execution_id	path		int	true	"execution id"
//	@Success		200				{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/maintenances/{maintenance_id}/executions/{execution_id}/finish [post]
func (c *ClusterController) FinishMaintenanceExecution(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("maintenance_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}
	execID, err := strconv.ParseUint(ctx.Param("execution_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	err = c.maintenanceSvc.FinishMaintenanceExecution(ctx, id, execID)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// CancelMaintenanceExecution
//
//	@Id				CancelMaintenanceExecution
//	@Description	cancel a maintenance executions
//	@Tags			ClusterService
//	@Accept			json
//	@Produce		json
//	@Param			maintenance_id	path		int	true	"maintenance id"
//	@Param			execution_id	path		int	true	"execution id"
//	@Success		200				{object}	api.BaseResp
//	@Router			/api/v1/devops/cluster/maintenances/{maintenance_id}/executions/{execution_id}/cancel [post]
func (c *ClusterController) CancelMaintenanceExecution(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("maintenance_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}
	execID, err := strconv.ParseUint(ctx.Param("execution_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	err = c.maintenanceSvc.CancelMaintenanceExecution(ctx, id, execID)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}
