package userbehavior

import (
	"fmt"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/tidbcloud/devops/api"
	ubapi "github.com/tidbcloud/devops/api/userbehavior"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	ubsvc "github.com/tidbcloud/devops/pkg/service/userbehavior"
)

// ListSubscriptions
//
//	@Id				UserBehaviorControllerListSubscriptions
//	@Description	list subscriptions
//	@Tags			UserBehaviorController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		ubapi.ListSubscriptionsReq	false	"request param"
//	@Success		200	{object}	ubapi.ListSubscriptionsResp
//	@Router			/api/v1/devops/userbehavior/subscriptions [get]
func (c *UserBehaviorController) ListSubscriptions(ctx *gin.Context) {
	req := &ubapi.ListSubscriptionsReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := ubsvc.ListSubscriptionParam{
		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}

	if req.FilterByCreator != nil && *req.FilterByCreator {
		v, exist := ctx.Get(consts.CtxUserEmail)
		email := v.(string)
		if exist {
			param.Creator = &email
		}
	}

	subs, total, err := c.ubsvc.ListSubscriptions(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ubapi.ListSubscriptionsResp{
		Total:         total,
		Subscriptions: subs,
	}
	ctrl.Success(ctx, resp)

}

// GetSubscription
//
//	@Id				UserBehaviorControllerGetSubscription
//	@Description	get an subscription
//	@Tags			UserBehaviorController
//	@Accept			json
//	@Produce		json
//	@Param			subscription_id	path		int	true	"subscription name"
//	@Success		200				{object}	ubapi.GetSubscriptionResp
//	@Router			/api/v1/devops/userbehavior/subscriptions/{subscription_id} [get]
func (c *UserBehaviorController) GetSubscription(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("subscription_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	sub, err := c.ubsvc.GetSubscription(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &ubapi.GetSubscriptionResp{Subscription: sub}
	ctrl.Success(ctx, resp)
}

// CreateSubscription
//
//	@Id				UserBehaviorControllerCreateSubscription
//	@Description	create an subscription
//	@Tags			UserBehaviorController
//	@Accept			json
//	@Produce		json
//	@Param			req	body		ubapi.CreateSubscriptionReq	true	"request param"
//	@Success		200	{object}	ubapi.CreateSubscriptionResp
//	@Router			/api/v1/devops/userbehavior/subscriptions [post]
func (c *UserBehaviorController) CreateSubscription(ctx *gin.Context) {
	req := &ubapi.CreateSubscriptionReq{}
	err := ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	v, exist := ctx.Get(consts.CtxUserEmail)
	if !exist {
		ctrl.FailWithCode(ctx, fmt.Errorf("account not found in ctx"), api.ErrCodeInternal)
		return
	}
	email := v.(string)

	param, err := buildParamToCreateSubscription(*req, email)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	id, err := c.ubsvc.CreateSubscription(ctx, param)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}

	resp := &ubapi.CreateSubscriptionResp{SubscriptionID: id}
	ctrl.Success(ctx, resp)
}

// Edit Subscription
//
//	@Id				UserBehaviorControllerEditSubscription
//	@Description	edit subscription
//	@Tags			UserBehaviorService
//	@Accept			json
//	@Produce		json
//	@Param			subscription_id	path		int							true	"subscription id"
//	@Param			req				body		ubapi.EditSubscriptionReq	true	"request"
//	@Success		200				{object}	ubapi.EditSubscriptionResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/userbehavior/subscriptions/{subscription_id} [put]
func (c *UserBehaviorController) EditSubscription(ctx *gin.Context) {
	idString := ctx.Param("subscription_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse subscription id"), api.ErrCodeInvalidArgument)
		return
	}

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

	param, err := buildParamToEditSubscription(*req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	err = c.ubsvc.EditSubscription(ctx, id, param)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}

	ctrl.Success(ctx, nil)
}

// CancelSubscription
//
//	@Id				UserBehaviorControllerCancelSubscription
//	@Description	cancel an subscription
//	@Tags			UserBehaviorController
//	@Accept			json
//	@Produce		json
//	@Param			subscription_id	path		int	true	"subscription id"
//	@Success		200				{object}	api.BaseResp
//	@Router			/api/v1/devops/userbehavior/subscriptions/{subscription_id}/cancel [post]
func (c *UserBehaviorController) CancelSubscription(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("subscription_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

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

	ctrl.Success(ctx, nil)
}

// GetLarkChatOptions
//
//	@Id				UserBehaviorControllerGetLarkChatOptions
//	@Description	get lark chat options
//	@Tags			UserBehaviorController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		ubapi.ListLarkChatOptionsReq	false	"request param"
//	@Success		200	{object}	ubapi.ListLarkChatOptionsResp
//	@Router			/api/v1/devops/userbehavior/subscriptions/larkchat_options [get]
func (c *UserBehaviorController) GetLarkChatOptions(ctx *gin.Context) {
	chats, err := c.ubsvc.GetLarkChatOptions(ctx)
	if err != nil {
		ctrl.Fail(ctx, err)
	}

	ctrl.Success(ctx, ubapi.ListLarkChatOptionsResp{Total: int64(len(chats)), LarkChatOptions: chats})
}

// SendMockEvent
//
//	@Id				UserBehaviorControllerSendMockEvent
//	@Description	send a mock event to subscription
//	@Tags			UserBehaviorController
//	@Accept			json
//	@Produce		json
//	@Param			subscription_id	path		int	true	"subscription name"
//	@Success		200				{object}	api.BaseResp
//	@Router			/api/v1/devops/userbehavior/subscriptions/{subscription_id}/mock [post]
func (c *UserBehaviorController) SendMockEventToSubscription(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("subscription_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("id is not a int"), api.ErrCodeInvalidArgument)
		return
	}

	_, exist := ctx.Get(consts.CtxUserEmail)
	if !exist {
		ctrl.FailWithCode(ctx, fmt.Errorf("account not found in ctx"), api.ErrCodeInternal)
		return
	}

	err = c.ubsvc.SendMockEventToSubscription(ctx, id)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}

	resp := &ubapi.SendMockEventToSubscriptionResp{}
	ctrl.Success(ctx, resp)
}

// AddExternalLarkChat
//
//	@Id				UserBehaviorControllerAddExternalLarkChat
//	@Description	add an external lark chat to lark chat list
//	@Tags			UserBehaviorController
//	@Accept			json
//	@Produce		json
//	@Param			req	body		ubapi.AddExternalLarkChatReq	true	"request param"
//	@Success		200	{object}	api.BaseResp
//	@Router			/api/v1/devops/userbehavior/subscriptions/add_chat [post]
func (c *UserBehaviorController) AddExternalLarkChat(ctx *gin.Context) {
	req := &ubapi.AddExternalLarkChatReq{}
	err := ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
	}

	err = c.ubsvc.AddExternalLarkChat(ctx, ubsvc.AddExternalLarkChatParam{
		Name:      req.Name,
		ChatURL:   req.ChatURL,
		AvatarURL: req.Avatar,
	})
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}
	ctrl.Success(ctx, nil)
}

// SuppressGTMEmail
//
//	@Id				UserBehaviorControllerSuppressGTMEmail
//	@Description	suppress a GTM email campaign for a tenant
//	@Tags			UserBehaviorController
//	@Accept			json
//	@Produce		json
//	@Param			req	body		ubapi.SuppressGTMEmailReq	true	"request param"
//	@Success		200	{object}	ubapi.SuppressGTMEmailResp
//	@Router			/api/v1/devops/userbehavior/gtm/emails/suppress [post]
func (c *UserBehaviorController) SuppressGTMEmail(ctx *gin.Context) {
	req := &ubapi.SuppressGTMEmailReq{}
	if err := ctx.ShouldBindJSON(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	result, err := c.ubsvc.SuppressGTMEmail(ctx, ubsvc.SuppressGTMEmailParam{
		TenantID:     req.TenantID,
		CampaignName: req.CampaignName,
		Source:       req.Source,
		Reason:       req.Reason,
	})
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, &ubapi.SuppressGTMEmailResp{
		Status:         result.Status,
		CancelledCount: result.CancelledCount,
	})
}

func buildParamToCreateSubscription(req ubapi.CreateSubscriptionReq, email string) (ubsvc.CreateSubscriptionParam, error) {
	param := ubsvc.CreateSubscriptionParam{
		Name:          req.Name,
		SubscribeType: req.SubscribeType,
		LarkChats:     req.LarkChats,
		EventType:     req.EventType,
		Template:      req.Template,
		Selector:      req.Selector,
		// Modify in the future
		OnlyPaidTenant:     true,
		OnlyExternalTenant: true,
		Creator:            email,
	}
	return param, nil
}

func buildParamToEditSubscription(req ubapi.EditSubscriptionReq) (ubsvc.EditSubscriptionParam, error) {
	param := ubsvc.EditSubscriptionParam{
		Name:          req.Name,
		SubscribeType: req.SubscribeType,
		LarkChats:     req.LarkChats,
		EventType:     req.EventType,
		Template:      req.Template,
		Selector:      req.Selector,
		// Modify in the future
		OnlyPaidTenant: true,
	}
	return param, nil
}
