package remediationticket

import (
	"fmt"
	"strconv"

	"github.com/gin-gonic/gin"

	"github.com/tidbcloud/devops/api"
	rtapi "github.com/tidbcloud/devops/api/remediationticket"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/consts"
	rtsvcapi "github.com/tidbcloud/devops/pkg/service/remediationticket/api"
)

// ListTickets
//
//	@Id				RemediationTicketControllerListTickets
//	@Description	list remediation tickets
//	@Tags			RemediationTicketService
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	rtapi.ListRemediationTicketsResp
//	@Failure		500	{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets [get]
func (c *RemediationTicketController) ListTickets(ctx *gin.Context) {
	tickets, count, err := c.rtSvc.ListTickets(ctx, rtsvcapi.ListTicketParam{})
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := rtapi.ListRemediationTicketsResp{
		Tickets: tickets,
		Count:   uint64(count),
	}

	ctrl.Success(ctx, resp)
}

// CreateTicket
//
//	@Id				RemediationTicketControllerCreateTicket
//	@Description	create Ticket
//	@Tags			RemediationTicketService
//	@Accept			json
//	@Produce		json
//	@Param			req	body		rtapi.CreateRemediationTicketReq	true	"request"
//	@Success		200	{object}	rtapi.CreateRemediationTicketResp
//	@Failure		500	{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets [post]
func (c *RemediationTicketController) CreateTicket(ctx *gin.Context) {
	req := &rtapi.CreateRemediationTicketReq{}
	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)

	tenantID, err := strconv.ParseUint(req.TenantID, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	clusterID, err := strconv.ParseUint(req.ClusterID, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	_, err = c.rtSvc.CreateTicket(ctx, rtsvcapi.CreateTicketParam{
		IncidentID: req.IncidentID,

		BizRegion:   req.BizRegion,
		Customer:    req.Customer,
		ClusterType: req.ClusterType,
		TenantID:    &tenantID,
		ClusterID:   &clusterID,
		Month:       req.Month,

		ApplicantEmail: email,
	})
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := rtapi.CreateRemediationTicketResp{}
	ctrl.Success(ctx, resp)
}

// GetTicket
//
//	@Id				RemediationTicketControllerGetTicket
//	@Description	get a remediation ticket
//	@Tags			RemediationTicketController
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int	true	"ticket id"
//	@Success		200			{object}	rtapi.GetRemediationTicketResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id} [get]
func (c *RemediationTicketController) GetTicket(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), api.ErrCodeInvalidArgument)
		return
	}

	ticket, err := c.rtSvc.GetTicket(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := rtapi.GetRemediationTicketResp{
		Ticket: *ticket,
	}
	ctrl.Success(ctx, resp)
}

// CancelTicket
//
//	@Id				RemediationTicketControllerCancelTicket
//	@Description	cancel a remediation ticket
//	@Tags			RemediationTicketController
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int	true	"ticket id"
//	@Success		200			{object}	api.BaseResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id}/cancel [post]
func (c *RemediationTicketController) CancelTicket(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), 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)

	err = c.rtSvc.CancelTicket(ctx, id, rtsvcapi.CancelTicketParam{CancellerEmail: email})
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// SubmitTicketPhase
//
//	@Id				RemediationTicketControllerSubmitTicketPhase
//	@Description	transfer a remediation ticket phase
//	@Tags			RemediationTicketController
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int							true	"ticket id"
//	@Param			req			body		rtapi.SubmitTicketPhaseReq	true	"request"
//	@Success		200			{object}	api.BaseResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id}/submit [post]
func (c *RemediationTicketController) SubmitTicketPhase(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), api.ErrCodeInvalidArgument)
		return
	}

	req := &rtapi.SubmitTicketPhaseReq{}
	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 := rtsvcapi.TransferTicketParam{
		RootCauseFromGS: req.RootCauseFromGS,

		// TODO: `AccumulatedDownMinutes` and `MonthlyUptimePercentage` will be removed from TransferTicketParam in the future.
		AccumulatedDownMinutes:  req.AccumulatedDownMinutes,
		MonthlyUptimePercentage: req.MonthlyUptimePercentage,

		RootCauseFromRDG:   req.RootCauseFromRDG,
		AccidentReportLink: req.AccidentReportLink,

		ClusterBilling:    req.ClusterBilling,
		PredefinedPercent: req.PredefinedPercent,
		ExtraPercent:      req.ExtraPercent,
		TotalCompensation: req.TotalCompensation,

		Version:        req.Version,
		SubmitterEmail: email,

		// Will be deprecated
		Reason:     req.Reason,
		ReportLink: req.ReportLink,
	}
	err = c.rtSvc.SubmitPhaseTransitionApproval(ctx, id, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// ApproveTicketPhase
//
//	@Id				RemediationTicketControllerApproveTicketPhase
//	@Description	transfer a remediation ticket phase
//	@Tags			RemediationTicketController
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int							true	"ticket id"
//	@Param			req			body		rtapi.ApproveTicketPhaseReq	true	"request"
//	@Success		200			{object}	api.BaseResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id}/approve [post]
func (c *RemediationTicketController) ApproveTicketPhase(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), api.ErrCodeInvalidArgument)
		return
	}

	req := &rtapi.ApproveTicketPhaseReq{}
	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 := rtsvcapi.ApprovePhaseParam{
		ApprID:        req.ApprID,
		ApproverEmail: email,
		Version:       req.Version,
		Input: rtsvcapi.TransferTicketParam{
			RootCauseFromGS: req.RootCauseFromGS,

			// TODO: `AccumulatedDownMinutes` and `MonthlyUptimePercentage` will be removed from TransferTicketParam in the future.
			AccumulatedDownMinutes:  req.AccumulatedDownMinutes,
			MonthlyUptimePercentage: req.MonthlyUptimePercentage,

			RootCauseFromRDG:   req.RootCauseFromRDG,
			AccidentReportLink: req.AccidentReportLink,

			ClusterBilling:    req.ClusterBilling,
			PredefinedPercent: req.PredefinedPercent,
			ExtraPercent:      req.ExtraPercent,
			TotalCompensation: req.TotalCompensation,

			Version:        req.Version,
			SubmitterEmail: email,

			// Will be deprecated
			Reason:     req.Reason,
			ReportLink: req.ReportLink,
		},
	}
	err = c.rtSvc.ApproveTicketPhase(ctx, id, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// RejectTicketPhase
//
//	@Id				RemediationTicketControllerRejectTicketPhase
//	@Description	transfer a remediation ticket phase
//	@Tags			RemediationTicketController
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int							true	"ticket id"
//	@Param			req			body		rtapi.RejectTicketPhaseReq	true	"request"
//	@Success		200			{object}	api.BaseResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id}/reject [post]
func (c *RemediationTicketController) RejectTicketPhase(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), api.ErrCodeInvalidArgument)
		return
	}

	req := &rtapi.RejectTicketPhaseReq{}
	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 := rtsvcapi.RejectPhaseParam{
		ApprID:        req.ApprID,
		RejecterEmail: email,
		Message:       req.Message,
	}
	err = c.rtSvc.RejectTicketPhase(ctx, id, param)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

// GetTicketProgress
//
//	@Id				RemediationTicketControllerGetTicketProgress
//	@Description	get remediation ticket progress
//	@Tags			RemediationTicketService
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int	true	"ticket id"
//	@Success		200			{object}	rtapi.GetProgressResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id}/progress [get]
func (c *RemediationTicketController) GetProgress(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), api.ErrCodeInvalidArgument)
		return
	}

	arr, currPhase, err := c.rtSvc.GetTicketProgress(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := rtapi.GetProgressResp{
		Phases:       arr,
		CurrentPhase: currPhase,
	}
	ctrl.Success(ctx, resp)
}

// GetTicketApprovals
//
//	@Id				RemediationTicketControllerGetTicketApprovals
//	@Description	get remediation ticket approvals
//	@Tags			RemediationTicketService
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int	true	"ticket id"
//	@Success		200			{object}	rtapi.GetTicketApprovalsResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id}/ticket_approvals [get]
func (c *RemediationTicketController) GetTicketApprovals(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), api.ErrCodeInvalidArgument)
		return
	}

	arr, err := c.rtSvc.GetTicketApprovals(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := rtapi.GetTicketApprovalsResp{
		PhaseApprovals: arr,
	}

	ctrl.Success(ctx, resp)
}

// GetApprovalFlow
//
//	@Id				RemediationTicketControllerGetApprovalFlow
//	@Description	get remediation approval flow
//	@Tags			RemediationTicketService
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int	true	"ticket id"
//	@Success		200			{object}	rtapi.GetApprovalFlowResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id}/approval_flow [get]
func (c *RemediationTicketController) GetApprovalFlow(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), api.ErrCodeInvalidArgument)
		return
	}

	flow, err := c.rtSvc.GetApprovalFlow(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := rtapi.GetApprovalFlowResp{
		Flow: *flow,
	}
	ctrl.Success(ctx, resp)
}

// GenerateFinalDoc
//
//	@Id				RemediationTicketControllerGenerateFinalDoc
//	@Description	generate final doc for the remediation ticket
//	@Tags			RemediationTicketController
//	@Accept			json
//	@Produce		json
//	@Param			ticket_id	path		int							true	"ticket id"
//	@Param			req			body		rtapi.GenerateFinalDocReq	true	"request"
//	@Success		200			{object}	api.BaseResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/remediationtickets/{ticket_id}/generate_doc [post]
func (c *RemediationTicketController) GenerateFinalDoc(ctx *gin.Context) {
	idString := ctx.Param("ticket_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse ticket id"), api.ErrCodeInvalidArgument)
		return
	}

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

	_, err = c.rtSvc.GenerateFinalDoc(ctx, id, req.ForcedRegenerate)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}
