package k8supgradeplan

import (
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/go-logr/logr"
	"go.uber.org/dig"

	"github.com/tidbcloud/devops/api"
	planapi "github.com/tidbcloud/devops/api/k8supgradeplan"
	planent "github.com/tidbcloud/devops/entity/k8supgradeplan"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	plansvcapi "github.com/tidbcloud/devops/pkg/service/k8supgradeplan/api"
)

type ControllerParam struct {
	dig.In

	Logger  logr.Logger
	PlanSvc plansvcapi.API
}

type Controller struct {
	logger  logr.Logger
	planSvc plansvcapi.API
}

func NewController(p ControllerParam) ctrl.Controller {
	return &Controller{
		logger:  p.Logger.WithName("K8sUpgradePlanController"),
		planSvc: p.PlanSvc,
	}
}

func (c *Controller) Name() string {
	return "K8sUpgradePlanController"
}

func (c *Controller) RegisterAPI(suite *ctrl.Suite) error {
	g := suite.Router.Group("/k8sUpgradePlans")
	g.GET("", c.ListPlans)
	g.POST("", c.CreatePlan)
	g.GET("/:plan_id", c.GetPlan)
	g.PUT("/:plan_id", c.UpdatePlan)
	g.POST("/:plan_id/schedule", c.SchedulePlan)
	g.POST("/:plan_id/cancel", c.CancelPlan)
	g.POST("/:plan_id/triggerNow", c.TriggerNow)
	return nil
}

// ListPlans
//
//	@Id				K8sUpgradePlanControllerListPlans
//	@Description	list k8s upgrade plans
//	@Tags			K8sUpgradePlanService
//	@Accept			json
//	@Produce		json
//	@Param			req	query		planapi.ListPlansReq	true	"ListPlansReq"
//	@Success		200	{object}	planapi.ListPlansResp
//	@Failure		500	{object}	api.BaseResp
//	@Router			/api/v1/devops/k8sUpgradePlans [get]
func (c *Controller) ListPlans(ctx *gin.Context) {
	req := &planapi.ListPlansReq{}
	if err := ctx.ShouldBindQuery(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	st := make([]planent.PlanStatus, 0, len(req.Statuses))
	for _, s := range req.Statuses {
		st = append(st, planent.PlanStatus(s))
	}
	items, total, err := c.planSvc.ListPlans(ctx, plansvcapi.ListPlansParam{
		CloudProviders: req.CloudProviders,
		EOSVersions:    req.EOSVersions,
		Statuses:       st,
		Page:           int(req.Page),
		PerPage:        int(req.PerPage),
	})
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &planapi.ListPlansResp{Total: total, Items: items})
}

// CreatePlan
//
//	@Id				K8sUpgradePlanControllerCreatePlan
//	@Description	create a k8s upgrade plan
//	@Tags			K8sUpgradePlanService
//	@Accept			json
//	@Produce		json
//	@Param			req	body		planapi.CreatePlanReq	true	"CreatePlanReq"
//	@Success		200	{object}	planapi.CreatePlanResp
//	@Failure		500	{object}	api.BaseResp
//	@Router			/api/v1/devops/k8sUpgradePlans [post]
func (c *Controller) CreatePlan(ctx *gin.Context) {
	req := &planapi.CreatePlanReq{}
	if err := ctx.ShouldBindJSON(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	createdBy := ""
	if v, ok := ctx.Get(consts.CtxUserEmail); ok {
		createdBy, _ = v.(string)
	}
	id, err := c.planSvc.CreatePlan(ctx, plansvcapi.CreatePlanParam{
		Name: req.Name, CloudProvider: req.CloudProvider, EOSVersion: req.EOSVersion,
		EOSDate: req.EOSDate, Deadline: req.Deadline, TriggerTime: req.TriggerTime,
		MaxRestartTimes: req.MaxRestartTimes, SelectorType: req.SelectorType,
		SelectorValue: req.SelectorValue, CreatedBy: createdBy,
	})
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &planapi.CreatePlanResp{ID: id})
}

// GetPlan
//
//	@Id				K8sUpgradePlanControllerGetPlan
//	@Description	get a k8s upgrade plan
//	@Tags			K8sUpgradePlanService
//	@Accept			json
//	@Produce		json
//	@Param			plan_id	path		int	true	"plan id"
//	@Success		200		{object}	planapi.GetPlanResp
//	@Failure		500		{object}	api.BaseResp
//	@Router			/api/v1/devops/k8sUpgradePlans/{plan_id} [get]
func (c *Controller) GetPlan(ctx *gin.Context) {
	id, err := parseID(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	plan, err := c.planSvc.GetPlan(ctx, id)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, &planapi.GetPlanResp{PlannedUpgrade: *plan})
}

// UpdatePlan
//
//	@Id				K8sUpgradePlanControllerUpdatePlan
//	@Description	update a k8s upgrade plan
//	@Tags			K8sUpgradePlanService
//	@Accept			json
//	@Produce		json
//	@Param			plan_id	path		int						true	"plan id"
//	@Param			req		body		planapi.UpdatePlanReq	true	"UpdatePlanReq"
//	@Success		200		{object}	api.BaseResp
//	@Failure		500		{object}	api.BaseResp
//	@Router			/api/v1/devops/k8sUpgradePlans/{plan_id} [put]
func (c *Controller) UpdatePlan(ctx *gin.Context) {
	id, err := parseID(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	req := &planapi.UpdatePlanReq{}
	if err := ctx.ShouldBindJSON(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	if err := c.planSvc.UpdatePlan(ctx, id, plansvcapi.UpdatePlanParam{
		Name: req.Name, Deadline: req.Deadline, TriggerTime: req.TriggerTime,
		MaxRestartTimes: req.MaxRestartTimes, SelectorType: req.SelectorType,
		SelectorValue: req.SelectorValue,
	}); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

// SchedulePlan
//
//	@Id				K8sUpgradePlanControllerSchedulePlan
//	@Description	schedule a k8s upgrade plan
//	@Tags			K8sUpgradePlanService
//	@Accept			json
//	@Produce		json
//	@Param			plan_id	path		int						true	"plan id"
//	@Param			req		body		planapi.SchedulePlanReq	true	"SchedulePlanReq"
//	@Success		200		{object}	api.BaseResp
//	@Failure		500		{object}	api.BaseResp
//	@Router			/api/v1/devops/k8sUpgradePlans/{plan_id}/schedule [post]
func (c *Controller) SchedulePlan(ctx *gin.Context) {
	id, err := parseID(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	req := &planapi.SchedulePlanReq{}
	if err := ctx.ShouldBindJSON(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	if err := c.planSvc.SchedulePlan(ctx, id, req.TriggerTime); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

// CancelPlan
//
//	@Id				K8sUpgradePlanControllerCancelPlan
//	@Description	cancel a k8s upgrade plan
//	@Tags			K8sUpgradePlanService
//	@Accept			json
//	@Produce		json
//	@Param			plan_id	path		int	true	"plan id"
//	@Success		200		{object}	api.BaseResp
//	@Failure		500		{object}	api.BaseResp
//	@Router			/api/v1/devops/k8sUpgradePlans/{plan_id}/cancel [post]
func (c *Controller) CancelPlan(ctx *gin.Context) {
	id, err := parseID(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	if err := c.planSvc.CancelPlan(ctx, id); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

// TriggerNow
//
//	@Id				K8sUpgradePlanControllerTriggerNow
//	@Description	trigger a k8s upgrade plan immediately
//	@Tags			K8sUpgradePlanService
//	@Accept			json
//	@Produce		json
//	@Param			plan_id	path		int	true	"plan id"
//	@Success		200		{object}	api.BaseResp
//	@Failure		500		{object}	api.BaseResp
//	@Router			/api/v1/devops/k8sUpgradePlans/{plan_id}/triggerNow [post]
func (c *Controller) TriggerNow(ctx *gin.Context) {
	id, err := parseID(ctx)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	if err := c.planSvc.TriggerNow(ctx, id); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

func parseID(ctx *gin.Context) (uint64, error) {
	return strconv.ParseUint(ctx.Param("plan_id"), 10, 64)
}
