package api

import (
	"net/http"
	"strconv"

	"github.com/gin-gonic/gin"
	"github.com/samber/lo"

	"github.com/pingcap-inc/clinic/pkg/db"
	"github.com/pingcap-inc/clinic/pkg/ngm/error"
	"github.com/pingcap-inc/clinic/pkg/ngm/performance_insight/service"
)

type IndexAdvisorAPI struct {
	iaSvc service.IndexAdvisorService
}

func NewIndexAdvisorAPI(iaSvc service.IndexAdvisorService) *IndexAdvisorAPI {
	return &IndexAdvisorAPI{
		iaSvc: iaSvc,
	}
}

// @Summary		List Index Advises
// @Tags			performance_insight_index_advisor
// @Description	List results of index advisor.
// @Produce		json
// @Param			X-CLOUD-USER			header		string	true	"dbaas user header, added automatically by clinic api-gateway, {id:1} encodes to eyJpZCI6MX0="
// @Param			order_by_update_time	query		string	false	"sort by update time before pagination"	Enums(asc,desc)	default(desc)
// @Param			limit					query		uint	false	"the number of items per page"			default(30)
// @Param			page					query		uint	false	"the page index"						default(1)
// @Success		200						{object}	ListIndexAdvisesResponse
// @Failure		400						{object}	error.ErrorResponse
// @Failure		401						{object}	error.ErrorResponse
// @Failure		403						{object}	error.ErrorResponse
// @Failure		404						{object}	error.ErrorResponse
// @Failure		412						{object}	error.ErrorResponse
// @Failure		429						{object}	error.ErrorResponse
// @Failure		500						{object}	error.ErrorResponse
// @Router			/performance_insight/index_advisor/results [get]
func (i *IndexAdvisorAPI) HandleGetResultList(c *gin.Context) {
	// Check user header auth info.
	_, err := getUserId(c)
	if err != nil {
		_ = c.Error(error.ErrUnauthorized.New("%s", err.Error()))
	}

	// Get org and cluster id from path.
	oid, cid, err := getOrgAndClusterIDFromPath(c)
	if err != nil {
		_ = c.Error(error.ErrBadRequest.New("%s", err.Error()))
		return
	}

	limit := c.Query("limit")
	if limit == "" {
		limit = "30"
	}
	limitInt, err := strconv.ParseUint(limit, 10, 64)
	if err != nil {
		_ = c.Error(error.ErrBadRequest.New("invalid limit"))
		return
	}

	page := c.Query("page")
	if page == "" {
		page = "1"
	}
	pageInt, err := strconv.ParseUint(page, 10, 64)
	if err != nil || pageInt == 0 {
		_ = c.Error(error.ErrBadRequest.New("invalid page"))
		return
	}

	timeOrder := c.Query("order_by_update_time")
	if timeOrder == "" {
		timeOrder = "desc"
	}

	results, count, err := i.iaSvc.QueryResults(c.Request.Context(), oid, cid, limitInt, pageInt, timeOrder)
	if err != nil {
		_ = c.Error(err)
		return
	}
	resp := lo.Map(results, func(r db.IndexAdvisorResult, _ int) IndexAdvisorResult {
		return fromModelIndexAdvisorResult(r)
	})
	c.JSON(http.StatusOK, ListIndexAdvisesResponse{
		Count:   count,
		Results: resp,
	})
}

// @Summary		Get Details of Index Advise
// @Tags			performance_insight_index_advisor
// @Description	Get details of index advisor result.
// @Produce		json
// @Param			X-CLOUD-USER	header		string	true	"dbaas user header, added automatically by clinic api-gateway, {id:1} encodes to eyJpZCI6MX0="
// @Param			id				path		uint	true	"result id"
// @Success		200				{object}	IndexAdvisorResult
// @Failure		400				{object}	error.ErrorResponse
// @Failure		401				{object}	error.ErrorResponse
// @Failure		403				{object}	error.ErrorResponse
// @Failure		404				{object}	error.ErrorResponse
// @Failure		412				{object}	error.ErrorResponse
// @Failure		429				{object}	error.ErrorResponse
// @Failure		500				{object}	error.ErrorResponse
// @Router			/performance_insight/index_advisor/results/{id} [get]
func (i *IndexAdvisorAPI) HandleGetResultDetail(c *gin.Context) {
	// Check user header auth info.
	_, err := getUserId(c)
	if err != nil {
		_ = c.Error(error.ErrUnauthorized.New("%s", err.Error()))
	}

	// Get org and cluster id from path.
	oid, cid, err := getOrgAndClusterIDFromPath(c)
	if err != nil {
		_ = c.Error(error.ErrBadRequest.New("%s", err.Error()))
		return
	}

	id := c.Param("id")
	idInt, err := strconv.ParseUint(id, 10, 64)
	if id == "" || err != nil {
		_ = c.Error(error.ErrBadRequest.New("invalid id"))
		return
	}

	result, err := i.iaSvc.QueryResult(c.Request.Context(), idInt)
	if err != nil {
		_ = c.Error(err)
		return
	}

	// Check org and cluster id.
	if result.DbaasOrgID != oid || result.DbaasClusterID != cid {
		_ = c.Error(error.ErrForbidden.New("forbidden"))
		return
	}

	c.JSON(http.StatusOK, fromModelIndexAdvisorResult(result))
}
