package audit

import (
	"strconv"

	"github.com/tidbcloud/devops/api"
	auditapi "github.com/tidbcloud/devops/api/audit"
	auditent "github.com/tidbcloud/devops/entity/audit"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/service/audit"

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

type ControllerParam struct {
	dig.In

	Audit audit.AuditServiceAPI
}

type AuditController struct {
	auditSvc audit.AuditServiceAPI
}

func (c *AuditController) Name() string {
	return "AuditController"
}

func NewAuditController(p ControllerParam) ctrl.Controller {
	return &AuditController{
		auditSvc: p.Audit,
	}
}

func (c *AuditController) RegisterAPI(suite *ctrl.Suite) error {
	group := suite.Router.Group("/audit")
	{
		group.GET("", c.ListAuditLog)
		group.GET("/types", c.ListAuditLogTypes)
		group.GET("/changes", c.ListChangeAuditLog)
		group.GET("/ctl", c.ListCtlAuditLog)
		group.GET("/cluster-maintenance", c.ListClusterMaintenanceAuditLog)
		group.GET("/operations", c.ListOperationAuditLog)
		group.GET("/jumps", c.ListJumpAuditLog)
	}
	return nil
}

// ListAuditLogTypes
//
//	@Id				AuditControllerListAuditLogTypes
//	@Description	list audit log types
//	@Tags			AuditController
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	auditapi.ListAuditLogTypesResp
//	@Router			/api/v1/devops/audit/types [get]
func (c *AuditController) ListAuditLogTypes(ctx *gin.Context) {
	types, err := c.auditSvc.ListAuditLogTypes(ctx)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &auditapi.ListAuditLogTypesResp{Types: types}
	ctrl.Success(ctx, resp)
}

// ListChangeAuditLog
//
//	@Id				AuditControllerListChangeAuditLog
//	@Description	list audit logs for changes
//	@Tags			AuditController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		auditapi.ListChangeAuditLogReq	false	"request param"
//	@Success		200	{object}	auditapi.ListChangeAuditLogResp
//	@Router			/api/v1/devops/audit/changes [get]
func (c *AuditController) ListChangeAuditLog(ctx *gin.Context) {
	req := &auditapi.ListChangeAuditLogReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := audit.ListAuditLogParam{
		Type:    auditent.AuditLogTypeChange,
		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}
	if req.ChangeID != 0 {
		param.ObjectIDs = []string{strconv.FormatUint(req.ChangeID, 10)}
	}

	auditLogs, total, err := c.auditSvc.ListAuditLog(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &auditapi.ListChangeAuditLogResp{Total: total, AuditLogs: auditLogs}
	ctrl.Success(ctx, resp)
}

// ListCtlAuditLog
//
//	@Id				AuditControllerListCtlAuditLog
//	@Description	list audit logs for ctl
//	@Tags			AuditController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		auditapi.ListCtlAuditLogReq	false	"request param"
//	@Success		200	{object}	auditapi.ListCtlAuditLogResp
//	@Router			/api/v1/devops/audit/ctl [get]
func (c *AuditController) ListCtlAuditLog(ctx *gin.Context) {
	req := &auditapi.ListCtlAuditLogReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := audit.ListAuditLogParam{
		Type:    auditent.AuditLogTypeCtl,
		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}
	if req.ClusterID != "" {
		param.ObjectIDs = []string{req.ClusterID}
	}

	auditLogs, total, err := c.auditSvc.ListAuditLog(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &auditapi.ListCtlAuditLogResp{Total: total, AuditLogs: auditLogs}
	ctrl.Success(ctx, resp)
}

// ListClusterMaintenanceAuditLog
//
//	@Id				AuditControllerListClusterMaintenanceAuditLog
//	@Description	list audit logs for cluster maintenance
//	@Tags			AuditController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		auditapi.ListClusterMaintenanceAuditLogReq	false	"request param"
//	@Success		200	{object}	auditapi.ListClusterMaintenanceAuditLogResp
//	@Router			/api/v1/devops/audit/cluster-maintenance [get]
func (c *AuditController) ListClusterMaintenanceAuditLog(ctx *gin.Context) {
	req := &auditapi.ListClusterMaintenanceAuditLogReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := audit.ListAuditLogParam{
		Type:    auditent.AuditLogTypeClusterMaintenance,
		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}
	if req.MaintenanceID != 0 {
		param.ObjectIDs = []string{strconv.FormatUint(req.MaintenanceID, 10)}
	}

	auditLogs, total, err := c.auditSvc.ListAuditLog(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &auditapi.ListClusterMaintenanceAuditLogResp{Total: total, AuditLogs: auditLogs}
	ctrl.Success(ctx, resp)
}

// ListOperationAuditLog
//
//	@Id				AuditControllerListOperationAuditLog
//	@Description	list audit logs for operation instances
//	@Tags			AuditController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		auditapi.ListOperationAuditLogReq	false	"request param"
//	@Success		200	{object}	auditapi.ListOperationAuditLogResp
//	@Router			/api/v1/devops/audit/operations [get]
func (c *AuditController) ListOperationAuditLog(ctx *gin.Context) {
	req := &auditapi.ListOperationAuditLogReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := audit.ListAuditLogParam{
		Type:    auditent.AuditLogTypeOperation,
		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}
	if req.InstanceID != 0 {
		param.ObjectIDs = []string{strconv.FormatUint(req.InstanceID, 10)}
	}

	auditLogs, total, err := c.auditSvc.ListAuditLog(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &auditapi.ListOperationAuditLogResp{Total: total, AuditLogs: auditLogs}
	ctrl.Success(ctx, resp)
}

// ListAuditLog
//
//	@Id				AuditControllerListAuditLog
//	@Description	list audit logs
//	@Tags			AuditController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		auditapi.ListAuditLogReq	false	"request param"
//	@Success		200	{object}	auditapi.ListAuditLogResp
//	@Router			/api/v1/devops/audit [get]
func (c *AuditController) ListAuditLog(ctx *gin.Context) {
	req := &auditapi.ListAuditLogReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := audit.ListAuditLogParam{
		Type:    req.Type,
		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}
	if req.ObjectID != "" {
		param.ObjectIDs = []string{req.ObjectID}
	}

	auditLogs, total, err := c.auditSvc.ListAuditLog(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &auditapi.ListAuditLogResp{Total: total, AuditLogs: auditLogs}
	ctrl.Success(ctx, resp)
}

// ListJumpAuditLog
//
//	@Id				AuditControllerListJumpAuditLog
//	@Description	list audit logs for jump authorizations
//	@Tags			AuditController
//	@Accept			json
//	@Produce		json
//	@Param			req	query		auditapi.ListJumpAuditLogReq	false	"request param"
//	@Success		200	{object}	auditapi.ListJumpAuditLogResp
//	@Router			/api/v1/devops/audit/jumps [get]
func (c *AuditController) ListJumpAuditLog(ctx *gin.Context) {
	req := &auditapi.ListJumpAuditLogReq{}
	err := ctx.ShouldBindQuery(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	param := audit.ListAuditLogParam{
		Type:    auditent.AuditLogTypeJump,
		Page:    int(req.Page),
		PerPage: int(req.PerPage),
	}
	if req.AuthorizationID != "" {
		param.ObjectIDs = []string{req.AuthorizationID}
	}

	auditLogs, total, err := c.auditSvc.ListAuditLog(ctx, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := &auditapi.ListJumpAuditLogResp{Total: total, AuditLogs: auditLogs}
	ctrl.Success(ctx, resp)
}
