package cloudconfig

import (
	"fmt"
	"strconv"

	"github.com/tidbcloud/devops/api"
	ccapi "github.com/tidbcloud/devops/api/cloudconfig"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	ccsvcapi "github.com/tidbcloud/devops/pkg/service/cloudconfig/api"

	"github.com/gin-gonic/gin"
)

// ListTemplates
//
//	@Id				CloudConfigControllerListTemplates
//	@Description	list templates
//	@Tags			CloudConfigController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		ccapi.ListTemplatesReq	false	"request param"
//	@Success		200	{object}	ccapi.ListTemplatesResp
//	@Router			/api/v1/devops/cloudconfig/template/templates [get]
func (c *CloudConfigController) ListTemplates(ctx *gin.Context) {
	req := &ccapi.ListTemplatesReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := ccsvcapi.ListTemplateParam{
		IDIn:         req.IDIn,
		NameIn:       req.NameIn,
		TemplateType: req.TemplateType,
		Version:      req.Version,
		TenantID:     req.TenantID,
	}

	tmpls, err := c.svc.ListTemplate(ctx, req.ClusterType, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ccapi.ListTemplatesResp{Templates: tmpls}
	ctrl.Success(ctx, resp)
}

// CreateTemplate
//
//	@Id				CloudConfigControllerCreateTemplate
//	@Description	create a template
//	@Tags			CloudConfigController
//	@Accept			json
//	@Produce		json
//	@Param			req		body		ccapi.CreateTemplateReq	true	"request param"
//	@Success		200		{object}	ccapi.CreateTemplateResp
//	@Failure		default	{object}	api.BaseResp
//	@Router			/api/v1/devops/cloudconfig/template/templates [post]
func (c *CloudConfigController) CreateTemplate(ctx *gin.Context) {
	req := &ccapi.CreateTemplateReq{}
	err := ctx.ShouldBind(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := ccsvcapi.CreateTemplateParam{
		AllowAssignID:   req.AllowAssignID,
		DenyPropagation: req.DenyPropagation,
	}
	tmpl, err := c.svc.CreateTemplate(ctx, req.Template, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ccapi.CreateTemplateResp{ID: tmpl.ID}
	ctrl.Success(ctx, resp)
}

// GetTemplate
//
//	@Id				CloudConfigControllerGetTemplate
//	@Description	get a template
//	@Tags			CloudConfigController
//	@Accept			json
//	@Produce		json
//	@Param			template_id	path		string					true	"template_id"
//	@Param			req			query		ccapi.GetTemplateReq	false	"request param"
//	@Success		200			{object}	ccapi.GetTemplateResp
//	@Router			/api/v1/devops/cloudconfig/template/templates/{template_id} [get]
func (c *CloudConfigController) GetTemplate(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("template_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
	}

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

	param := ccsvcapi.GetTemplateParam{
		WithDefaultCases: req.WithDefaultCases,
	}
	tmpl, err := c.svc.GetTemplate(ctx, req.ClusterType, id, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ccapi.GetTemplateResp{Template: tmpl}
	ctrl.Success(ctx, resp)
}

// DiffTemplate
//
//	@Id				CloudConfigControllerDiffTemplate
//	@Description	diff a template
//	@Tags			CloudConfigController
//	@Accept			json
//	@Produce		json
//	@Param			template_id	path		string					true	"template_id"
//	@Param			req			query		ccapi.DiffTemplateReq	false	"request param"
//	@Success		200			{object}	ccapi.DiffTemplateResp
//	@Router			/api/v1/devops/cloudconfig/template/templates/{template_id}/diff [get]
func (c *CloudConfigController) DiffTemplate(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("template_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
	}

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

	result, err := c.svc.DiffTemplate(ctx, req.ClusterType, id, req.TemplateID)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ccapi.DiffTemplateResp{
		ConfigType:         result.ConfigType,
		ModifiedParameters: result.ModifiedParameters,
	}
	ctrl.Success(ctx, resp)
}

// ListTemplateDefaultCaseBindings
//
//	@Id				CloudConfigControllerListTemplateDefaultCaseBindings
//	@Description	list template default cases
//	@Tags			CloudConfigController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		ccapi.ListTemplateDefaultCaseBindingsReq	false	"request param"
//	@Success		200	{object}	ccapi.ListTemplateDefaultCaseBindingsResp
//	@Router			/api/v1/devops/cloudconfig/default-config/template-bindings [get]
func (c *CloudConfigController) ListTemplateDefaultCaseBindings(ctx *gin.Context) {
	req := &ccapi.ListTemplateDefaultCaseBindingsReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := ccsvcapi.ListTemplateDefaultCaseBindingsParam{
		ConfigTypeIs:            req.ConfigTypeIs,
		DefaultForVersion:       req.DefaultForVersion,
		DefaultForCloudProvider: req.DefaultForCloudProvider,
		DefaultForInstanceType:  req.DefaultForInstanceType,
		DefaultForTenantID:      req.DefaultForTenantID,
		ExactMatch:              req.ExactMatch,

		Page:    int32(req.Page),
		PerPage: int32(req.PerPage),
	}
	bindings, total, err := c.svc.ListTemplateDefaultCaseBindings(ctx, req.ClusterType, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ccapi.ListTemplateDefaultCaseBindingsResp{
		DefaultCases: bindings,
		Total:        total,
	}
	ctrl.Success(ctx, resp)
}

// UpdateTemplateDefaultCaseBindings
//
//	@Id				CloudConfigControllerUpdateTemplateDefaultCaseBindings
//	@Description	update template default cases
//	@Tags			CloudConfigController
//	@Accept			json
//	@Produce		json
//	@Param			req		body		ccapi.UpdateTemplateDefaultCaseBindingsReq	true	"request param"
//	@Success		200		{object}	api.BaseResp
//	@Failure		default	{object}	api.BaseResp
//	@Router			/api/v1/devops/cloudconfig/default-config/template-bindings [patch]
func (c *CloudConfigController) UpdateTemplateDefaultCaseBindings(ctx *gin.Context) {
	req := &ccapi.UpdateTemplateDefaultCaseBindingsReq{}
	err := ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	err = c.svc.BatchUpdateTemplateDefaultCaseBindings(ctx, req.DefaultCases)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// ListConfigurationTemplateChange
//
//	@Id				CloudConfigControllerListConfigurationTemplateChange
//	@Description	list config template changes
//	@Tags			CloudConfigController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		ccapi.ListConfigurationTemplateChangeReq	false	"request param"
//	@Success		200	{object}	ccapi.ListConfigurationChangeResp
//	@Router			/api/v1/devops/cloudconfig/template/changes [get]
func (c *CloudConfigController) ListConfigurationTemplateChange(ctx *gin.Context) {
	req := &ccapi.ListConfigurationTemplateChangeReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	loc := cvtConfigurationTemplateLocator(req.ConfigurationTemplateLocator)

	param := ccsvcapi.ListConfigurationChangesParam{
		TargetTemplate:            &loc,
		IDOnly:                    req.IDOnly,
		HaveModifiedParameterLike: req.HaveModifiedParameterLike,

		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}

	changes, total, err := c.svc.ListConfigurationChanges(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ccapi.ListConfigurationChangeResp{
		Total:   total,
		Changes: changes,
	}
	ctrl.Success(ctx, resp)
}

// CreateConfigurationTemplateChange
//
//	@Id				CloudConfigControllerCreateConfigurationTemplateChange
//	@Description	update configuration template
//	@Tags			CloudConfigController
//	@Accept			json
//	@Produce		json
//	@Param			req		body		ccapi.CreateConfigurationTemplateChangeReq	true	"request param"
//	@Success		200		{object}	ccapi.CreateConfigurationChangeResp
//	@Failure		default	{object}	api.BaseResp
//	@Router			/api/v1/devops/cloudconfig/template/changes [post]
func (c *CloudConfigController) CreateConfigurationTemplateChange(ctx *gin.Context) {
	email := ctx.GetString(consts.CtxUserEmail)
	if email == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("account not found in ctx"), api.ErrCodeInternal)
		return
	}

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

	loc := cvtConfigurationTemplateLocator(req.ConfigurationTemplateLocator)
	param := BuildParamToCreateConfigurationChange(&req.CreateConfigurationChangeReq, email)
	param.TargetTemplate = &loc

	id, err := c.svc.CreateConfigurationChange(ctx, *param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ccapi.CreateConfigurationChangeResp{ChangeID: id}
	ctrl.Success(ctx, resp)
}
