package incident

import (
	"context"
	"fmt"
	"slices"
	"strconv"
	"strings"
	"time"

	"github.com/tidbcloud/devops/entity"

	"github.com/tidbcloud/devops/api"
	incidentapi "github.com/tidbcloud/devops/api/incident"
	ctrl "github.com/tidbcloud/devops/pkg/controller"
	"github.com/tidbcloud/devops/pkg/infra/config"
	"github.com/tidbcloud/devops/pkg/infra/log"
	"github.com/tidbcloud/devops/pkg/service/incident"

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

type ControllerParam struct {
	dig.In

	IncidentService incident.IncidentService
	Conf            *config.RootConfig
}

type IncidentController struct {
	incidentSvc incident.IncidentService
	conf        config.IncidentServiceConfig
}

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

func NewIncidentController(p ControllerParam) ctrl.Controller {
	return &IncidentController{
		incidentSvc: p.IncidentService,
		conf:        p.Conf.Services.Incident,
	}
}

func parseOptionalUintParam(ctx *gin.Context, name string) (*uint64, error) {
	raw := ctx.Param(name)
	if raw == "" {
		return nil, nil
	}
	value, err := strconv.ParseUint(raw, 10, 64)
	if err != nil {
		return nil, err
	}
	return &value, nil
}

func (c *IncidentController) RegisterAPI(suite *ctrl.Suite) error {
	r := suite.Router
	group := r.Group("/incidents")
	{
		group.GET("", c.ListIncidents)
		group.GET("/component_options", c.GetIncidentComponentOptions)
		group.GET("/:incident_id", c.GetIncident)
		group.POST("", c.CreateIncident)
		group.PUT("/:incident_id", c.UpdateIncident)
		group.DELETE("/:incident_id", c.DeleteIncident)

		group.GET("/:incident_id/logs", c.ListIncidentLogs)
		group.GET("/:incident_id/logs/:log_item_id", c.GetIncidentLogItem)
		group.POST("/:incident_id/logs", c.CreateIncidentLogItem)
		group.PUT("/:incident_id/logs/:log_item_id", c.UpdateIncidentLogItem)
		group.DELETE("/:incident_id/logs/:log_item_id", c.DeleteIncidentLogItem)

		group.GET("/:incident_id/postmortem", c.ListIncidentPostMortemItems)
		group.GET("/:incident_id/postmortem/:postmortem_id", c.GetIncidentPostMortem)
		group.POST("/:incident_id/postmortem", c.CreateIncidentPostMortem)
		group.POST("/:incident_id/postmortem/:postmortem_id", c.UpdateIncidentPostMortem)
		group.DELETE("/:incident_id/postmortem/:postmortem_id", c.DeleteIncidentPostMortem)

		group.GET("/:incident_id/postmortem/timeline", c.ListIncidentPostMortemTimelines)
		group.GET("/:incident_id/postmortem/timeline/:timeline_item_id", c.GetIncidentPostMortemTimeline)
		group.POST("/:incident_id/postmortem/timeline", c.CreateIncidentPostMortemTimeline)
		group.POST("/:incident_id/postmortem/timeline/:timeline_item_id", c.UpdateIncidentPostMortemTimeline)
		group.DELETE("/:incident_id/postmortem/timeline/:timeline_item_id", c.DeleteIncidentPostMortemTimeline)

		group.GET("/:incident_id/postmortems/:postmortem_id/todoactions", c.ListIncidentPostMortemToDoActions)
		group.GET("/:incident_id/postmortems/:postmortem_id/todoactions/:action_id", c.GetIncidentPostMortemToDoAction)
		group.POST("/:incident_id/postmortems/:postmortem_id/todoactions", c.CreateIncidentPostMortemToDoAction)
		group.POST("/:incident_id/postmortems/:postmortem_id/todoactions/:action_id", c.UpdateIncidentPostMortemToDoAction)
		group.DELETE("/:incident_id/postmortems/:postmortem_id/todoactions/:action_id", c.DeleteIncidentPostMortemToDoAction)
		group.POST("/:incident_id/postmortems/:postmortem_id/todoactions/sync", c.SyncPostMortemToDoActionsStatus)
		group.POST("/:incident_id/postmortem/todoactions/import", c.ImportIncidentPostMortemToDoActions)

		group.POST("/:incident_id/feishu_message", c.UpdateFeishuMessage)

		group.POST("/:incident_id/feishu_task", c.CreateFeishuTask)

		group.GET("/:incident_id/postmortem/todoaction", c.ListIncidentPostMortemToDoActions)
		group.GET("/:incident_id/postmortem/todoaction/:action_id", c.GetIncidentPostMortemToDoAction)
	}

	return nil
}

var defaultIncidentComponentOptions = []string{
	"TiDB",
	"TiKV",
	"PD",
	"Cluster-Manager",
	"Import/Export",
	"Data-Service",
	"Dataflow-Service",
	"Account",
	"Billing",
	"TiCDC",
	"DM",
	"Lightning",
	"Dumpling",
	"SLA",
	"TiDB-Optimizer",
	"TiDB-SQL",
	"TiDB-Compute",
	"Tiflash",
	"TiProxy",
	"Backup/Restore",
	"BR",
	"Serverless",
	"Essential",
	"Starter",
	"DBaaS-FE",
	"GitOps",
	"Cloud-Service",
	"API-Gateway",
	"Monitoring",
	"Config-Center",
}

func defaultIncidentComponentDefinitions() []config.IncidentComponentConfig {
	ret := make([]config.IncidentComponentConfig, 0, len(defaultIncidentComponentOptions))
	for idx, item := range defaultIncidentComponentOptions {
		ret = append(ret, config.IncidentComponentConfig{
			Key:     item,
			Name:    item,
			Enabled: ptr(true),
			Order:   idx,
		})
	}
	return ret
}

func normalizeIncidentComponentDefinitions(input []config.IncidentComponentConfig) []incidentapi.IncidentComponentDefinition {
	seen := make(map[string]struct{}, len(input))
	ret := make([]incidentapi.IncidentComponentDefinition, 0, len(input))
	for _, item := range input {
		key := strings.TrimSpace(item.Key)
		if key == "" {
			continue
		}
		if _, ok := seen[key]; ok {
			continue
		}
		seen[key] = struct{}{}

		name := strings.TrimSpace(item.Name)
		if name == "" {
			name = key
		}
		enabled := true
		if item.Enabled != nil {
			enabled = *item.Enabled
		}

		ret = append(ret, incidentapi.IncidentComponentDefinition{
			Key:           key,
			Name:          name,
			Enabled:       enabled,
			Order:         item.Order,
			T1RuleSummary: strings.TrimSpace(item.T1RuleSummary),
			T2RuleSummary: strings.TrimSpace(item.T2RuleSummary),
			T3RuleSummary: strings.TrimSpace(item.T3RuleSummary),
			RuleDocURL:    strings.TrimSpace(item.RuleDocURL),
		})
	}
	slices.SortFunc(ret, func(a, b incidentapi.IncidentComponentDefinition) int {
		if a.Order != b.Order {
			return a.Order - b.Order
		}
		if a.Name != b.Name {
			if a.Name < b.Name {
				return -1
			}
			return 1
		}
		if a.Key < b.Key {
			return -1
		}
		if a.Key > b.Key {
			return 1
		}
		return 0
	})
	return ret
}

func (c *IncidentController) incidentComponentDefinitions() []incidentapi.IncidentComponentDefinition {
	if len(c.conf.ComponentDefinitions) > 0 {
		return normalizeIncidentComponentDefinitions(c.conf.ComponentDefinitions)
	}
	if len(c.conf.ComponentOptions) > 0 {
		legacy := make([]config.IncidentComponentConfig, 0, len(c.conf.ComponentOptions))
		for idx, item := range c.conf.ComponentOptions {
			legacy = append(legacy, config.IncidentComponentConfig{
				Key:     item,
				Name:    item,
				Enabled: ptr(true),
				Order:   idx,
			})
		}
		return normalizeIncidentComponentDefinitions(legacy)
	}
	return normalizeIncidentComponentDefinitions(defaultIncidentComponentDefinitions())
}

func ptr(v bool) *bool {
	return &v
}

func (c *IncidentController) incidentComponentOptions() []string {
	definitions := c.incidentComponentDefinitions()
	ret := make([]string, 0, len(definitions))
	for _, item := range definitions {
		if !item.Enabled {
			continue
		}
		ret = append(ret, item.Name)
	}
	return ret
}

func (c *IncidentController) validateIncidentComponents(components string) error {
	if components == "" {
		return nil
	}

	allowed := make(map[string]struct{}, len(c.incidentComponentOptions()))
	for _, item := range c.incidentComponentOptions() {
		allowed[item] = struct{}{}
	}

	invalid := make([]string, 0)
	for _, item := range strings.Fields(components) {
		if _, ok := allowed[item]; ok {
			continue
		}
		if slices.Contains(invalid, item) {
			continue
		}
		invalid = append(invalid, item)
	}

	if len(invalid) > 0 {
		return fmt.Errorf("invalid incident components: %s", strings.Join(invalid, ", "))
	}

	return nil
}

// ListIncidents
//
//	@Id				ListIncidents
//	@Description	list incidents
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			page		query		int						false	"page number"
//	@Param			per_page	query		int						false	"page size"
//	@Param			war_room_id	query		uint64					false	"war room ID"
//	@Param			status		query		entity.IncidentStatus	false	"incident status"
//	@Param			tier		query		entity.IncidentTier		false	"incident tier"
//	@Param			components	query		[]string				false	"components that every incident must contain"		collectionFormat(multi)
//	@Param			biz_regions	query		[]string				false	"business regions that every incident must contain"	collectionFormat(multi)
//	@Success		200			{object}	incidentapi.ListIncidentsResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents [get]
func (c *IncidentController) ListIncidents(ctx *gin.Context) {
	req := &incidentapi.ListIncidentsReq{}
	if err := ctx.ShouldBindQuery(req); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	if _, ok := ctx.GetQuery("per_page"); !ok {
		req.PerPage = 50
	}
	if req.Page == 0 || req.PerPage == 0 || req.PerPage > 100 {
		ctrl.FailWithCode(ctx, fmt.Errorf("page must be greater than 0 and per_page must be between 1 and 100"), api.ErrCodeInvalidArgument)
		return
	}

	incidents, total, err := c.incidentSvc.ListIncidents(ctx, incident.ListIncidentsParam{
		Page:              int(req.Page),
		PerPage:           int(req.PerPage),
		WarRoomID:         req.WarRoomID,
		StatusIs:          req.StatusIs,
		TierIs:            req.TierIs,
		ComponentsContain: req.ComponentsContain,
		BizRegionsContain: req.BizRegionsContain,
	})
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentapi.ListIncidentsResp{Total: total, Incidents: incidents}
	ctrl.Success(ctx, &resp)
}

// GetIncidentComponentOptions
//
//	@Id				GetIncidentComponentOptions
//	@Description	get incident component options
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Success		200	{object}	incidentapi.GetIncidentComponentOptionsResp
//	@Failure		500	{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/component_options [get]
func (c *IncidentController) GetIncidentComponentOptions(ctx *gin.Context) {
	ctrl.Success(ctx, incidentapi.GetIncidentComponentOptionsResp{
		Components:  c.incidentComponentOptions(),
		Definitions: c.incidentComponentDefinitions(),
	})
}

// GetIncident
//
//	@Id				GetIncident
//	@Description	get incident
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int	true	"incident id"
//	@Success		200			{object}	entity.Incident
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id} [get]
func (c *IncidentController) GetIncident(ctx *gin.Context) {
	idString := ctx.Param("incident_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}

	incidentEntity, err := c.incidentSvc.GetIncident(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentEntity
	ctrl.Success(ctx, resp)
}

// CreateIncident
//
//	@Id				CreateIncident
//	@Description	create Incident
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			req	body		incidentapi.CreateIncidentReq	true	"request"
//	@Success		200	{object}	incidentapi.CreateIncidentResp
//	@Failure		500	{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents [post]
func (c *IncidentController) CreateIncident(ctx *gin.Context) {
	req := &incidentapi.CreateIncidentReq{NeedGenerateFeishuMessage: false}
	err := ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	incidentEntity := req.ToIncidentEntity()
	if err := c.validateIncidentComponents(incidentEntity.Components); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	id, err := c.incidentSvc.CreateIncident(ctx, incidentEntity)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}

	if req.NeedGenerateFeishuMessage {
		err := c.incidentSvc.GenerateReportInFeishuChannel(ctx, id)
		if err != nil {
			ctrl.FailWithCode(ctx, fmt.Errorf("creation succeeded but send feishu message failed: %s", err), api.ErrCodeInternal)
			return
		}
	}
	ctrl.Success(ctx, incidentapi.CreateIncidentResp{IncidentId: id})
}

// UpdateIncident
//
//	@Id				UpdateIncident
//	@Description	update incident
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int								true	"incident id"
//	@Param			req			body		incidentapi.UpdateIncidentReq	true	"request"
//	@Success		200			{object}	incidentapi.UpdateIncidentResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id} [put]
func (c *IncidentController) UpdateIncident(ctx *gin.Context) {
	idString := ctx.Param("incident_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}

	req := &incidentapi.UpdateIncidentReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	if req.ExtractPostMortemToDoActions {
		if req.FeishuUrl == nil || strings.TrimSpace(*req.FeishuUrl) == "" {
			ctrl.FailWithCode(ctx, fmt.Errorf("feishu_url is required when extracting postmortem todo actions"), api.ErrCodeInvalidArgument)
			return
		}
	}

	current, err := c.incidentSvc.GetIncident(ctx, id)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	if req.ExtractPostMortemToDoActions &&
		current.PostMortemToDoActionImportStatus == entity.PostMortemToDoActionImportStatusImporting {
		ctrl.Success(ctx, incidentapi.UpdateIncidentResp{
			PostMortemToDoActionImportStarted: true,
			PostMortemToDoActionImportStatus:  entity.PostMortemToDoActionImportStatusImporting,
		})
		return
	}

	incidentEntity := req.ApplyToIncidentEntity(current)
	if req.ExtractPostMortemToDoActions {
		incidentEntity.PostMortemToDoActionImportStatus = entity.PostMortemToDoActionImportStatusImporting
	}
	if err := c.validateIncidentComponents(incidentEntity.Components); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	err = c.incidentSvc.UpdateIncident(ctx, id, incidentEntity)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	if req.ExtractPostMortemToDoActions {
		c.startPostMortemToDoActionImport(id, *req.FeishuUrl)
	}

	ctrl.Success(ctx, incidentapi.UpdateIncidentResp{
		PostMortemToDoActionImportStarted: req.ExtractPostMortemToDoActions,
		PostMortemToDoActionImportStatus:  incidentEntity.PostMortemToDoActionImportStatus,
	})
}

func (c *IncidentController) startPostMortemToDoActionImport(incidentID uint64, feishuURL string) {
	go func() {
		ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
		defer cancel()

		status := entity.PostMortemToDoActionImportStatusSucceeded
		if _, err := c.incidentSvc.CreatePostMortemToDoActionsFromFeishuDoc(ctx, incidentID, feishuURL); err != nil {
			log.FromContext(ctx).Error(err, "async postmortem todo action import failed", "incident_id", incidentID, "feishu_url", feishuURL)
			status = entity.PostMortemToDoActionImportStatusFailed
		}
		statusCtx, statusCancel := context.WithTimeout(context.Background(), 30*time.Second)
		defer statusCancel()
		if err := c.incidentSvc.UpdatePostMortemToDoActionImportStatus(statusCtx, incidentID, status); err != nil {
			log.FromContext(statusCtx).Error(err, "update async postmortem todo action import status failed", "incident_id", incidentID, "status", status)
		}
	}()
}

// ImportIncidentPostMortemToDoActions
//
//	@Id				ImportIncidentPostMortemToDoActions
//	@Description	import todo actions from the incident's existing postmortem Feishu document
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int		true	"incident id"
//	@Param			force		query		bool	false	"force import even when todo actions already exist"
//	@Success		200			{object}	incidentapi.ImportIncidentPostMortemToDoActionsResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortem/todoactions/import [post]
func (c *IncidentController) ImportIncidentPostMortemToDoActions(ctx *gin.Context) {
	idString := ctx.Param("incident_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}

	current, err := c.incidentSvc.GetIncident(ctx, id)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	if current.PostMortemToDoActionImportStatus == entity.PostMortemToDoActionImportStatusImporting {
		ctrl.Success(ctx, incidentapi.ImportIncidentPostMortemToDoActionsResp{
			PostMortemToDoActionImportStarted: true,
			PostMortemToDoActionImportStatus:  entity.PostMortemToDoActionImportStatusImporting,
		})
		return
	}

	feishuURL := strings.TrimSpace(current.FeishuUrl)
	if feishuURL == "" {
		ctrl.FailWithCode(ctx, fmt.Errorf("incident has no postmortem feishu_url"), api.ErrCodeInvalidArgument)
		return
	}

	force, err := strconv.ParseBool(ctx.DefaultQuery("force", "false"))
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("invalid force query: %w", err), api.ErrCodeInvalidArgument)
		return
	}
	if !force {
		existing, err := c.incidentSvc.ListIncidentPostMortemToDoActions(ctx, id, nil)
		if err != nil {
			ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
			return
		}
		if len(existing) > 0 {
			ctrl.FailWithCode(ctx, fmt.Errorf("incident already has postmortem todo actions, use force=true to import again"), api.ErrCodeInvalidArgument)
			return
		}
	}

	if err := c.incidentSvc.UpdatePostMortemToDoActionImportStatus(ctx, id, entity.PostMortemToDoActionImportStatusImporting); err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}
	c.startPostMortemToDoActionImport(id, feishuURL)

	ctrl.Success(ctx, incidentapi.ImportIncidentPostMortemToDoActionsResp{
		PostMortemToDoActionImportStarted: true,
		PostMortemToDoActionImportStatus:  entity.PostMortemToDoActionImportStatusImporting,
	})
}

// DeleteIncident
//
//	@Id				DeleteIncident
//	@Description	delete incident
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int	true	"incident id"
//	@Success		200			{object}	incidentapi.DeleteIncidentResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id} [delete]
func (c *IncidentController) DeleteIncident(ctx *gin.Context) {
	ctrl.Success(ctx, incidentapi.DeleteIncidentResp{})
}

// ListIncidentLogs
//
//	@Id				ListIncidentLogs
//	@Description	list incident logs
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int	true	"incident id"
//	@Success		200			{object}	incidentapi.ListIncidentLogsResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/logs [get]
func (c *IncidentController) ListIncidentLogs(ctx *gin.Context) {
	incidentIdString := ctx.Param("incident_id")
	incidentId, err := strconv.ParseUint(incidentIdString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}

	logItems, err := c.incidentSvc.ListIncidentLogs(ctx, incidentId)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentapi.ListIncidentLogsResp{IncidentId: incidentId, LogItems: logItems}
	ctrl.Success(ctx, resp)

}

// GetIncidentLogItem
//
//	@Id				GetIncidentLogItem
//	@Description	get incident log item
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int	true	"incident id"
//	@Param			log_item_id	path		int	true	"log item id"
//	@Success		200			{object}	entity.IncidentLogItem
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/logs/{log_item_id} [get]
func (c *IncidentController) GetIncidentLogItem(ctx *gin.Context) {
	idString := ctx.Param("log_item_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident log item id"), api.ErrCodeInvalidArgument)
		return
	}

	logItem, err := c.incidentSvc.GetIncidentLogItem(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, logItem)
}

// CreateIncidentLogItem
//
//	@Id				CreateIncidentLogItem
//	@Description	create Incident log item
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int						true	"incident id"
//	@Param			req			body		entity.IncidentLogItem	true	"request"
//	@Success		200			{object}	api.EmptyResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/logs [post]
func (c *IncidentController) CreateIncidentLogItem(ctx *gin.Context) {
	idString := ctx.Param("incident_id")
	incidentId, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}

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

	_, err = c.incidentSvc.CreateIncidentLogItem(ctx, incidentId, req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
		return
	}

	ctrl.Success(ctx, nil)

}

// UpdateIncidentLogItem
//
//	@Id				UpdateIncidentLogItem
//	@Description	update incident log item
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int						true	"incident id"
//	@Param			log_item_id	path		int						true	"log item id"
//	@Param			req			body		entity.IncidentLogItem	true	"request"
//	@Success		200			{object}	api.EmptyResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/log/{log_item_id} [put]
func (c *IncidentController) UpdateIncidentLogItem(ctx *gin.Context) {
	idString := ctx.Param("log_item_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident log item id"), api.ErrCodeInvalidArgument)
		return
	}

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

	c.incidentSvc.UpdateIncidentLogItem(ctx, id, req)
	ctrl.Success(ctx, nil)
}

// DeleteIncidentLogItem
//
//	@Id				DeleteIncidentLogItem
//	@Description	delete incident log item
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int	true	"incident id"
//	@Param			log_item_id	path		int	true	"log item id"
//	@Success		200			{object}	incidentapi.DeleteIncidentLogItemResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/logs/{log_item_id} [delete]
func (c *IncidentController) DeleteIncidentLogItem(ctx *gin.Context) {
	ctrl.Success(ctx, incidentapi.DeleteIncidentLogItemResp{})
}

// ListIncidentPostMortemItems
//
//	@Id				ListIncidentPostMortemItems
//	@Description	list incident postmortem items
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int	true	"postmortem id"
//	@Success		200			{object}	incidentapi.ListIncidentPostMortemItemsResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems [get]
func (c *IncidentController) ListIncidentPostMortemItems(ctx *gin.Context) {
	incidentIdString := ctx.Param("incident_id")
	incidentId, err := strconv.ParseUint(incidentIdString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}

	postMortemItems, err := c.incidentSvc.ListIncidentPostMortemItems(ctx, incidentId)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentapi.ListIncidentPostMortemItemsResp{IncidentId: incidentId, PostMortemItems: postMortemItems}

	ctrl.Success(ctx, resp)
}

// GetIncidentPostMortem
//
//	@Id				GetIncidentPostMortem
//	@Description	get incident postmortem
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Success		200				{object}	incidentapi.GetIncidentPostMortemResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id} [get]
func (c *IncidentController) GetIncidentPostMortem(ctx *gin.Context) {
	idString := ctx.Param("postmortem_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}

	postMortem, err := c.incidentSvc.GetIncidentPostMortem(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentapi.GetIncidentPostMortemResp{PostMortem: postMortem}

	ctrl.Success(ctx, resp)
}

// CreateIncidentPostMortem
//
//	@Id				CreateIncidentPostMortem
//	@Description	create incident postmortem
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id	path		int										true	"incident id"
//	@Param			req			body		incidentapi.CreateIncidentPostMortemReq	true	"CreateIncidentPostMortemReq"
//	@Success		200			{object}	incidentapi.CreateIncidentPostMortemResp
//	@Failure		500			{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems [post]
func (c *IncidentController) CreateIncidentPostMortem(ctx *gin.Context) {
	incidentId, err := strconv.ParseUint(ctx.Param("incident_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}
	req := &incidentapi.CreateIncidentPostMortemReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	entity, err := c.incidentSvc.CreateIncidentPostMortem(ctx, &entity.IncidentPostMortem{
		IncidentId:            incidentId,
		CreatedAt:             req.CreatedAt,
		ReviewedAt:            req.ReviewedAt,
		InvolvedEngineers:     req.InvolvedEngineers,
		IncidentStartTime:     req.IncidentStartTime,
		IncidentEndTime:       req.IncidentEndTime,
		ImpactedTenants:       req.ImpactedTenants,
		TechnicalImpactLevel:  req.TechnicalImpactLevel,
		MinutesToResolved:     req.MinutesToResolved,
		CustomerImpactMinutes: req.CustomerImpactMinutes,
		Summary:               req.Summary,
		Impact:                req.Impact,
		MinutesToDetect:       req.MinutesToDetect,
		Resolution:            req.Resolution,
		RootCauses:            req.RootCauses,
		Background:            req.Background,
		BugMechanism:          req.BugMechanism,
		WhatWorked:            req.WhatWorked,
		WhatDidNotWork:        req.WhatDidNotWork,
		WhereWeWereLucky:      req.WhereWeWereLucky,
		TopIssues:             req.TopIssues,
	})

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

	ctrl.Success(ctx, entity)
}

// UpdateIncidentPostMortem
//
//	@Id				UpdateIncidentPostMortem
//	@Description	update incident postmortem
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int										true	"incident id"
//	@Param			postmortem_id	path		int										true	"postmortem id"
//	@Param			req				body		incidentapi.UpdateIncidentPostMortemReq	true	"UpdateIncidentPostMortemReq"
//	@Success		200				{object}	incidentapi.UpdateIncidentPostMortemResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id} [post]
func (c *IncidentController) UpdateIncidentPostMortem(ctx *gin.Context) {

	id, err := strconv.ParseUint(ctx.Param("postmortem_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}

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

	err = c.incidentSvc.UpdateIncidentPostMortem(ctx, id, &entity.IncidentPostMortem{
		CreatedAt:             req.CreatedAt,
		ReviewedAt:            req.ReviewedAt,
		InvolvedEngineers:     req.InvolvedEngineers,
		IncidentStartTime:     req.IncidentStartTime,
		IncidentEndTime:       req.IncidentEndTime,
		ImpactedTenants:       req.ImpactedTenants,
		TechnicalImpactLevel:  req.TechnicalImpactLevel,
		MinutesToResolved:     req.MinutesToResolved,
		CustomerImpactMinutes: req.CustomerImpactMinutes,
		Summary:               req.Summary,
		Impact:                req.Impact,
		MinutesToDetect:       req.MinutesToDetect,
		Resolution:            req.Resolution,
		RootCauses:            req.RootCauses,
		Background:            req.Background,
		BugMechanism:          req.BugMechanism,
		WhatWorked:            req.WhatWorked,
		WhatDidNotWork:        req.WhatDidNotWork,
		WhereWeWereLucky:      req.WhereWeWereLucky,
		TopIssues:             req.TopIssues,
	})

	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}
	ctrl.Success(ctx, incidentapi.UpdateIncidentPostMortemResp{})
}

// DeleteIncidentPostMortem
//
//	@Id				DeleteIncidentPostMortem
//	@Description	delete incident postmortem
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Success		200				{object}	incidentapi.DeleteIncidentPostMortemResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id} [delete]
func (c *IncidentController) DeleteIncidentPostMortem(ctx *gin.Context) {
	idString := ctx.Param("postmortem_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}

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

	ctrl.Success(ctx, incidentapi.DeleteIncidentPostMortemResp{})
}

// ListIncidentPostMortemTimelines
//
//	@Id				ListIncidentPostMortemTimelines
//	@Description	list incident postmortem timelines
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Success		200				{object}	incidentapi.ListIncidentPostMortemTimelinesResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/timelines [get]
func (c *IncidentController) ListIncidentPostMortemTimelines(ctx *gin.Context) {
	postmortemId, err := strconv.ParseUint(ctx.Param("postmortem_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}

	timelineItems, err := c.incidentSvc.ListIncidentPostMortemTimelines(ctx, postmortemId)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentapi.ListIncidentPostMortemTimelinesResp{PostmortemId: postmortemId, TimelineItems: timelineItems}

	ctrl.Success(ctx, resp)
}

// GetIncidentPostMortemTimeline
//
//	@Id				GetIncidentPostMortemTimeline
//	@Description	get incident postmortem timeline
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Param			timeline_id		path		int	true	"timeline id"
//	@Success		200				{object}	incidentapi.GetIncidentPostMortemTimelineResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/timelines/{timeline_id} [get]
func (c *IncidentController) GetIncidentPostMortemTimeline(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("timeline_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse timeline id"), api.ErrCodeInvalidArgument)
		return
	}

	timeline, err := c.incidentSvc.GetIncidentPostMortemTimelineItem(ctx, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentapi.GetIncidentPostMortemTimelineResp{Timeline: timeline}

	ctrl.Success(ctx, resp)
}

// CreateIncidentPostMortemTimeline
//
//	@Id				CreateIncidentPostMortemTimeline
//	@Description	create incident postmortem timeline
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int												true	"incident id"
//	@Param			postmortem_id	path		int												true	"postmortem id"
//	@Param			req				body		incidentapi.CreateIncidentPostMortemTimelineReq	true	"CreateIncidentPostMortemTimelineReq"
//	@Success		200				{object}	incidentapi.CreateIncidentPostMortemTimelineResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/timelines [post]
func (c *IncidentController) CreateIncidentPostMortemTimeline(ctx *gin.Context) {
	postmortemId, err := strconv.ParseUint(ctx.Param("postmortem_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}
	req := &incidentapi.CreateIncidentPostMortemTimelineReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}
	entity, err := c.incidentSvc.CreateIncidentPostMortemTimelineItem(ctx, &entity.IncidentPostMortemTimelineItem{
		PostMortemId: postmortemId,
		HappenedAt:   req.HappenedAt,
		ItemType:     req.ItemType,
		Description:  req.Description,
	})

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

	ctrl.Success(ctx, incidentapi.CreateIncidentPostMortemTimelineResp{Id: entity.Id})
}

// UpdateIncidentPostMortemTimeline
//
//	@Id				UpdateIncidentPostMortemTimeline
//	@Description	update incident postmortem timeline
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int												true	"incident id"
//	@Param			postmortem_id	path		int												true	"postmortem id"
//	@Param			timeline_id		path		int												true	"action id"
//	@Param			req				body		incidentapi.UpdateIncidentPostMortemTimelineReq	true	"UpdateIncidentPostMortemTimelineReq"
//	@Success		200				{object}	incidentapi.UpdateIncidentPostMortemTimelineResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/timelines/{timeline_id} [post]
func (c *IncidentController) UpdateIncidentPostMortemTimeline(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("timeline_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse timeline id"), api.ErrCodeInvalidArgument)
		return
	}

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

	err = c.incidentSvc.UpdateIncidentPostMortemTimelineItem(ctx, id, &entity.IncidentPostMortemTimelineItem{
		PostMortemId: req.PostMortemId,
		HappenedAt:   req.HappenedAt,
		ItemType:     req.ItemType,
		Description:  req.Description,
	})

	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}
	ctrl.Success(ctx, incidentapi.UpdateIncidentPostMortemTimelineResp{})
}

// DeleteIncidentPostMortemTimeline
//
//	@Id				DeleteIncidentPostMortemTimeline
//	@Description	delete incident postmortem timeline
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Param			timeline_id		path		int	true	"timeline id"
//	@Success		200				{object}	incidentapi.DeleteIncidentPostMortemTimelineResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/timelines/{timeline_id} [delete]
func (c *IncidentController) DeleteIncidentPostMortemTimeline(ctx *gin.Context) {
	id, err := strconv.ParseUint(ctx.Param("timeline_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse timeline id"), api.ErrCodeInvalidArgument)
		return
	}

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

	ctrl.Success(ctx, incidentapi.DeleteIncidentPostMortemTimelineResp{})
}

// ListIncidentPostMortemToDoActions
//
//	@Id				ListIncidentPostMortemToDoActions
//	@Description	list incident postmortem todo actions
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Success		200				{object}	incidentapi.ListIncidentPostMortemToDoActionsResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/todoactions [get]
func (c *IncidentController) ListIncidentPostMortemToDoActions(ctx *gin.Context) {
	incidentId, err := strconv.ParseUint(ctx.Param("incident_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}
	postMortemId, err := parseOptionalUintParam(ctx, "postmortem_id")
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}

	actionItems, err := c.incidentSvc.ListIncidentPostMortemToDoActions(ctx, incidentId, postMortemId)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentapi.ListIncidentPostMortemToDoActionsResp{IncidentId: incidentId, ToDoActionItems: actionItems}

	ctrl.Success(ctx, resp)
}

// GetIncidentPostMortemToDoAction
//
//	@Id				GetIncidentPostMortemToDoAction
//	@Description	get incident postmortem todo action
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Param			action_id		path		int	true	"action id"
//	@Success		200				{object}	incidentapi.GetIncidentPostMortemToDoActionResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/todoactions/{action_id} [get]
func (c *IncidentController) GetIncidentPostMortemToDoAction(ctx *gin.Context) {
	incidentId, err := strconv.ParseUint(ctx.Param("incident_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}
	postMortemId, err := parseOptionalUintParam(ctx, "postmortem_id")
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}
	id, err := strconv.ParseUint(ctx.Param("action_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse timeline id"), api.ErrCodeInvalidArgument)
		return
	}

	action, err := c.incidentSvc.GetIncidentPostMortemToDoActionItem(ctx, incidentId, postMortemId, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	resp := incidentapi.GetIncidentPostMortemToDoActionResp{ToDoAction: action}

	ctrl.Success(ctx, resp)
}

// CreateIncidentPostMortemToDoAction
//
//	@Id				CreateIncidentPostMortemToDoAction
//	@Description	create incident postmortem todo action
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int													true	"incident id"
//	@Param			postmortem_id	path		int													true	"postmortem id"
//	@Param			req				body		incidentapi.CreateIncidentPostMortemToDoActionReq	true	"CreateIncidentPostMortemToDoActionReq"
//	@Success		200				{object}	incidentapi.CreateIncidentPostMortemToDoActionResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/todoactions [post]
func (c *IncidentController) CreateIncidentPostMortemToDoAction(ctx *gin.Context) {
	postmortemId, err := strconv.ParseUint(ctx.Param("postmortem_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}
	incidentId, err := strconv.ParseUint(ctx.Param("incident_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}
	req := &incidentapi.CreateIncidentPostMortemToDoActionReq{}
	err = ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	entity, err := c.incidentSvc.CreateIncidentPostMortemToDoActionItem(ctx, &incident.CreateIncidentPostMortemActionParams{
		IncidentId:               incidentId,
		PostMortemId:             &postmortemId,
		Status:                   req.Status,
		Persons:                  req.Persons,
		Deadline:                 req.Deadline,
		Description:              req.Description,
		Title:                    req.Title,
		Type:                     req.Type,
		Priority:                 req.Priority,
		LinkedJiraIssue:          req.LinkedJiraIssue,
		CreateAssociatedLarkTask: req.CreateFeishuTask,
	})

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

	ctrl.Success(ctx, incidentapi.CreateIncidentPostMortemToDoActionResp{Id: entity.Id})
}

// UpdateIncidentPostMortemToDoAction
//
//	@Id				UpdateIncidentPostMortemToDoAction
//	@Description	update incident postmortem todo action
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int													true	"incident id"
//	@Param			postmortem_id	path		int													true	"postmortem id"
//	@Param			action_id		path		int													true	"action id"
//	@Param			req				body		incidentapi.UpdateIncidentPostMortemToDoActionReq	true	"UpdateIncidentPostMortemToDoActionReq"
//	@Success		200				{object}	incidentapi.UpdateIncidentPostMortemToDoActionResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/todoactions/{action_id} [post]
func (c *IncidentController) UpdateIncidentPostMortemToDoAction(ctx *gin.Context) {
	incidentId, err := strconv.ParseUint(ctx.Param("incident_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}
	postMortemId, err := parseOptionalUintParam(ctx, "postmortem_id")
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}
	id, err := strconv.ParseUint(ctx.Param("action_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse todo action id"), api.ErrCodeInvalidArgument)
		return
	}

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

	err = c.incidentSvc.UpdateIncidentPostMortemToDoActionItem(ctx, incidentId, postMortemId, id, &incident.EditIncidentPostMortemToDoActionParams{
		Status:          req.Status,
		Persons:         req.Persons,
		Deadline:        req.Deadline,
		Title:           req.Title,
		Type:            req.Type,
		Priority:        req.Priority,
		Description:     req.Description,
		LinkedJiraIssue: req.LinkedJiraIssue,
	})

	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}
	ctrl.Success(ctx, incidentapi.UpdateIncidentPostMortemToDoActionResp{})
}

// DeleteIncidentPostMortemToDoAction
//
//	@Id				DeleteIncidentPostMortemToDoAction
//	@Description	delete incident postmortem todo action
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Param			action_id		path		int	true	"action id"
//	@Success		200				{object}	incidentapi.DeleteIncidentPostMortemToDoActionResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/todoactions/{action_id} [delete]
func (c *IncidentController) DeleteIncidentPostMortemToDoAction(ctx *gin.Context) {
	incidentId, err := strconv.ParseUint(ctx.Param("incident_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}
	postMortemId, err := parseOptionalUintParam(ctx, "postmortem_id")
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}
	id, err := strconv.ParseUint(ctx.Param("action_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse todo action id"), api.ErrCodeInvalidArgument)
		return
	}

	err = c.incidentSvc.DeleteIncidentPostMortemToDoActionItem(ctx, incidentId, postMortemId, id)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, incidentapi.DeleteIncidentPostMortemToDoActionResp{})
}

// SyncIncidentPostMortemToDoActionsStatus
//
//	@Id				SyncIncidentPostMortemToDoActionStatus
//	@Description	sync incident postmortem todo action status from TCOCP
//	@Tags			IncidentService
//	@Accept			json
//	@Produce		json
//	@Param			incident_id		path		int	true	"incident id"
//	@Param			postmortem_id	path		int	true	"postmortem id"
//	@Success		200				{object}	api.BaseResp
//	@Failure		500				{object}	api.BaseResp
//	@Router			/api/v1/devops/incidents/{incident_id}/postmortems/{postmortem_id}/todoactions/sync [post]
func (c *IncidentController) SyncPostMortemToDoActionsStatus(ctx *gin.Context) {
	incidentId, err := strconv.ParseUint(ctx.Param("incident_id"), 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident id"), api.ErrCodeInvalidArgument)
		return
	}
	postMortemId, err := parseOptionalUintParam(ctx, "postmortem_id")
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse postmortem id"), api.ErrCodeInvalidArgument)
		return
	}

	err = c.incidentSvc.SyncToDoActionStatusFromTCOCP(ctx, incidentId, postMortemId)
	if err != nil {
		ctrl.Fail(ctx, err)
		return
	}

	ctrl.Success(ctx, nil)
}

func (c *IncidentController) UpdateFeishuMessage(ctx *gin.Context) {
	idString := ctx.Param("incident_id")
	id, err := strconv.ParseUint(idString, 10, 64)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to parse incident incident id"), api.ErrCodeInvalidArgument)
		return
	}

	err = c.incidentSvc.GenerateReportInFeishuChannel(ctx, id)
	if err != nil {
		ctrl.FailWithCode(ctx, fmt.Errorf("fail to generate feishu message: %w", err), api.ErrCodeInternal)
		return
	}
	ctrl.Success(ctx, nil)
}

func (c *IncidentController) CreateFeishuTask(ctx *gin.Context) {
	req := &incidentapi.CreateFeishuTaskReq{}
	err := ctx.ShouldBindJSON(req)
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInvalidArgument)
		return
	}

	id, err := c.incidentSvc.CreateFeishuTask(ctx, req.ToIncidentEntity())
	if err != nil {
		ctrl.FailWithCode(ctx, err, api.ErrCodeInternal)
	}

	ctrl.Success(ctx, incidentapi.CreateFeishuTaskResp{TaskId: id})
}
