package dispatch

import (
	"net/http"
	"sort"
	"strconv"

	"github.com/gin-gonic/gin"

	calendarv1 "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/api/core-server/calendar/v1"
	dispatchapi "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/api/ticket-manager/dispatch"
	dispatchent "github.com/tidbcloud/aisupport/packages/sdk-go/contracts/entity/ticket-manager/dispatch"
	apiutils "github.com/tidbcloud/aisupport/packages/sdk-go/infra/apiserver/utils"
	authconsts "github.com/tidbcloud/aisupport/packages/sdk-go/infra/auth/consts"
	"github.com/tidbcloud/aisupport/packages/sdk-go/infra/errs"
	dispatchsvcapi "github.com/tidbcloud/aisupport/services/ticket-manager/internal/services/dispatch/api"
)

// GetOptions returns dispatch policy editor options.
//
//	@Summary	Get dispatch policy options
//	@Id			DispatchHandlerGetOptions
//	@Tags		DispatchHandler
//	@Accept		json
//	@Produce	json
//	@Success	200		{object}	dispatchapi.GetOptionsResponse
//	@Failure	default	{object}	base.BaseResponse
//	@Router		/api/v1/ticketmanager/dispatch/options [get]
func (h *Handler) GetOptions(c *gin.Context) {
	calendars, err := h.Core.Calendar.ListCalendarConfigs(c, &calendarv1.ListCalendarConfigsRequest{})
	if err != nil {
		apiutils.Fail(c, err)
		return
	}
	teams, err := h.Oncall.ListTeams(c)
	if err != nil {
		apiutils.Fail(c, err)
		return
	}
	options := dispatchent.DefaultFieldOption()
	options.RuleCondition.CalendarNames = make([]string, 0, len(calendars.Configs))
	for _, calendar := range calendars.Configs {
		options.RuleCondition.CalendarNames = append(options.RuleCondition.CalendarNames, calendar.Name)
	}
	sort.Strings(options.RuleCondition.CalendarNames)
	options.Action.BotEmails = append([]string(nil), h.Config.BotEmails...)
	options.Action.OnCallTeams = make([]dispatchent.OnCallTeamOption, 0, len(teams))
	for _, team := range teams {
		options.Action.OnCallTeams = append(options.Action.OnCallTeams, dispatchent.OnCallTeamOption{
			TeamKey:  team.TeamKey,
			TeamName: team.TeamName,
		})
	}
	apiutils.Success(c, &dispatchapi.GetOptionsResponse{Options: options})
}

// ListPolicies lists dispatch policies.
//
//	@Summary	List dispatch policies
//	@Id			DispatchHandlerListPolicies
//	@Tags		DispatchHandler
//	@Accept		json
//	@Produce	json
//	@Param		param	query		dispatchapi.ListPoliciesRequest	false	"param"
//	@Success	200		{object}	dispatchapi.ListPoliciesResponse
//	@Failure	default	{object}	base.BaseResponse
//	@Router		/api/v1/ticketmanager/dispatch/policies [get]
func (h *Handler) ListPolicies(c *gin.Context) {
	var req dispatchapi.ListPoliciesRequest
	if err := c.ShouldBindQuery(&req); err != nil {
		apiutils.Fail(c, errs.APIErrorNew(http.StatusBadRequest, err))
		return
	}
	param := dispatchsvcapi.ListPoliciesParam{
		Page: dispatchsvcapi.PageParam{Page: int(req.Page), PageSize: int(req.PageSize)},
	}
	if req.FamilyID != "" {
		param.FamilyIDIs = &req.FamilyID
	}
	if req.NameLike != "" {
		param.NameLike = &req.NameLike
	}
	if req.Status != "" {
		param.StatusIs = &req.Status
	}
	param.Statuses = req.Statuses
	policies, total, err := h.Dispatch.ListPolicies(c, param)
	if err != nil {
		apiutils.Fail(c, err)
		return
	}
	apiutils.Success(c, &dispatchapi.ListPoliciesResponse{Policies: policies, Total: total})
}

// CreatePolicy creates a dispatch policy.
//
//	@Summary	Create dispatch policy
//	@Id			DispatchHandlerCreatePolicy
//	@Tags		DispatchHandler
//	@Accept		json
//	@Produce	json
//	@Param		request	body		dispatchapi.CreatePolicyRequest	true	"Policy payload"
//	@Success	200		{object}	dispatchapi.CreatePolicyResponse
//	@Failure	default	{object}	base.BaseResponse
//	@Router		/api/v1/ticketmanager/dispatch/policies [post]
func (h *Handler) CreatePolicy(c *gin.Context) {
	var req dispatchapi.CreatePolicyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		apiutils.Fail(c, errs.APIErrorNew(http.StatusBadRequest, err))
		return
	}
	operator, ok := requiredOperator(c)
	if !ok {
		return
	}
	policy, err := h.Dispatch.CreatePolicy(c, dispatchsvcapi.CreatePolicyParam{
		Name:  req.Name,
		Rules: req.Rules,
		Trigger: dispatchent.PolicyTrigger{
			Enable: req.Trigger.Enable, SourceIn: req.Trigger.SourceIn, IssueLabelAll: req.Trigger.IssueLabelAll,
			IssueStatusIn: req.Trigger.IssueStatusIn, ProjectKeyIn: req.Trigger.ProjectKeyIn,
		},
		CreatedBy:        operator,
		ReplacesPolicyID: req.ReplacesPolicyID,
	})
	if err != nil {
		apiutils.Fail(c, err)
		return
	}
	apiutils.Success(c, &dispatchapi.CreatePolicyResponse{Policy: policy})
}

// GetPolicy gets a dispatch policy.
//
//	@Summary	Get dispatch policy
//	@Id			DispatchHandlerGetPolicy
//	@Tags		DispatchHandler
//	@Accept		json
//	@Produce	json
//	@Param		id		path		int	true	"Policy ID"
//	@Success	200		{object}	dispatchapi.GetPolicyResponse
//	@Failure	default	{object}	base.BaseResponse
//	@Router		/api/v1/ticketmanager/dispatch/policies/{id} [get]
func (h *Handler) GetPolicy(c *gin.Context) {
	id, err := policyID(c)
	if err != nil {
		apiutils.Fail(c, errs.APIErrorNew(http.StatusBadRequest, err))
		return
	}
	policy, err := h.Dispatch.GetPolicy(c, id)
	if err != nil {
		apiutils.Fail(c, err)
		return
	}
	apiutils.Success(c, &dispatchapi.GetPolicyResponse{Policy: policy})
}

// UpdatePolicy replaces a dispatch policy.
//
//	@Summary	Update dispatch policy
//	@Id			DispatchHandlerUpdatePolicy
//	@Tags		DispatchHandler
//	@Accept		json
//	@Produce	json
//	@Param		id		path		int								true	"Policy ID"
//	@Param		request	body		dispatchapi.UpdatePolicyRequest	true	"Policy payload"
//	@Success	200		{object}	dispatchapi.UpdatePolicyResponse
//	@Failure	default	{object}	base.BaseResponse
//	@Router		/api/v1/ticketmanager/dispatch/policies/{id} [put]
func (h *Handler) UpdatePolicy(c *gin.Context) {
	id, err := policyID(c)
	if err != nil {
		apiutils.Fail(c, errs.APIErrorNew(http.StatusBadRequest, err))
		return
	}
	var req dispatchapi.UpdatePolicyRequest
	if err := c.ShouldBindJSON(&req); err != nil {
		apiutils.Fail(c, errs.APIErrorNew(http.StatusBadRequest, err))
		return
	}
	policy, err := h.Dispatch.UpdatePolicy(c, id, dispatchsvcapi.UpdatePolicyParam{
		Name:  req.Name,
		Rules: req.Rules,
		Trigger: dispatchent.PolicyTrigger{
			Enable: req.Trigger.Enable, SourceIn: req.Trigger.SourceIn, IssueLabelAll: req.Trigger.IssueLabelAll,
			IssueStatusIn: req.Trigger.IssueStatusIn, ProjectKeyIn: req.Trigger.ProjectKeyIn,
		},
	})
	if err != nil {
		apiutils.Fail(c, err)
		return
	}
	apiutils.Success(c, &dispatchapi.UpdatePolicyResponse{Policy: policy})
}

// DeletePolicy deletes a dispatch policy.
//
//	@Summary	Delete dispatch policy
//	@Id			DispatchHandlerDeletePolicy
//	@Tags		DispatchHandler
//	@Accept		json
//	@Produce	json
//	@Param		id		path		int	true	"Policy ID"
//	@Success	200		{object}	base.BaseResponse
//	@Failure	default	{object}	base.BaseResponse
//	@Router		/api/v1/ticketmanager/dispatch/policies/{id} [delete]
func (h *Handler) DeletePolicy(c *gin.Context) {
	id, err := policyID(c)
	if err != nil {
		apiutils.Fail(c, errs.APIErrorNew(http.StatusBadRequest, err))
		return
	}
	if err := h.Dispatch.DeletePolicy(c, id); err != nil {
		apiutils.Fail(c, err)
		return
	}
	apiutils.Success(c, nil)
}

// PublishPolicy publishes a dispatch policy.
//
//	@Summary	Publish dispatch policy
//	@Id			DispatchHandlerPublishPolicy
//	@Tags		DispatchHandler
//	@Accept		json
//	@Produce	json
//	@Param		id		path		int	true	"Policy ID"
//	@Success	200		{object}	dispatchapi.PublishPolicyResponse
//	@Failure	default	{object}	base.BaseResponse
//	@Router		/api/v1/ticketmanager/dispatch/policies/{id}/publish [post]
func (h *Handler) PublishPolicy(c *gin.Context) {
	id, err := policyID(c)
	if err != nil {
		apiutils.Fail(c, errs.APIErrorNew(http.StatusBadRequest, err))
		return
	}
	operator, ok := requiredOperator(c)
	if !ok {
		return
	}
	policy, err := h.Dispatch.PublishPolicy(c, id, operator)
	if err != nil {
		apiutils.Fail(c, err)
		return
	}
	apiutils.Success(c, &dispatchapi.PublishPolicyResponse{Policy: policy})
}

// RetirePolicy retires an active dispatch policy.
//
//	@Summary	Retire dispatch policy
//	@Id			DispatchHandlerRetirePolicy
//	@Tags		DispatchHandler
//	@Accept		json
//	@Produce	json
//	@Param		id		path		int	true	"Policy ID"
//	@Success	200		{object}	dispatchapi.RetirePolicyResponse
//	@Failure	default	{object}	base.BaseResponse
//	@Router		/api/v1/ticketmanager/dispatch/policies/{id}/retire [post]
func (h *Handler) RetirePolicy(c *gin.Context) {
	id, err := policyID(c)
	if err != nil {
		apiutils.Fail(c, errs.APIErrorNew(http.StatusBadRequest, err))
		return
	}
	policy, err := h.Dispatch.RetirePolicy(c, id)
	if err != nil {
		apiutils.Fail(c, err)
		return
	}
	apiutils.Success(c, &dispatchapi.RetirePolicyResponse{Policy: policy})
}

func policyID(c *gin.Context) (uint64, error) {
	return strconv.ParseUint(c.Param("id"), 10, 64)
}

func requiredOperator(c *gin.Context) (string, bool) {
	operator := c.GetHeader(authconsts.HeaderOperator)
	if operator == "" {
		apiutils.Fail(c, errs.APIErrorf(http.StatusBadRequest, "%s is required", authconsts.HeaderOperator))
		return "", false
	}
	return operator, true
}
