package handler

import (
	"net/http"

	"github.com/labstack/echo/v4"

	"github.com/pingcap/test-infra/common/model"
	reqM "github.com/pingcap/test-infra/common/model/tcms/request"
	respM "github.com/pingcap/test-infra/common/model/tcms/response"
	"github.com/pingcap/test-infra/services/tcms/service/common"
	serror "github.com/pingcap/test-infra/services/tcms/service/common/errors"
	"github.com/pingcap/test-infra/services/tcms/utils"
)

// ListResourcePools ...
// @Summary List ResourcePools
// @ID list-resourcepools
// @Tags resourcepool
// @Param rms_name query string false "Filter by rms_name"
// @Param names query string false "Choose resourcepools in names"
// @Success 200 {object} respM.ResourcePoolListResponse
// @Failure default {object} HTTPError
// @Router /resourcepool [get]
func (h *HTTPHandler) ListResourcePools(c echo.Context) error {
	ctx := c.(utils.Context)

	rmsName := c.QueryParam("rms_name")

	names, err := utils.ParseCSV(ctx.QueryParam("names"))
	if err != nil {
		return serror.New(http.StatusBadRequest, err)
	}

	log := ctx.Log()
	log.With("rms_name", rmsName).Infof("ListResourcePools")

	opts := common.ResourcePoolQueryOpts{
		Names:   names,
		RmsName: rmsName,
	}
	rps, total, err := h.service.ResourcePool.List(ctx, opts)
	if err != nil {
		log.Errorf(err, "ListResourcePools failed")
		return err
	}

	return ctx.JSON(http.StatusOK, respM.ResourcePoolListResponse{
		Total: total,
		Data:  rps,
	})
}

// CreateResourcePool ...
// @Summary Create ResourcePool
// @ID create-resourcepool
// @Tags resourcepool
// @Security Bearer
// @Param resourcepool body model.ResourcePool true "ResourcePool to create"
// @Success 200 {object} model.ResourcePool
// @Failure default {object} HTTPError
// @Router /resourcepool [post]
func (h *HTTPHandler) CreateResourcePool(c echo.Context) error {
	ctx := c.(utils.Context)

	rp := &model.ResourcePool{}

	if err := ctx.Bind(rp); err != nil {
		return serror.New(http.StatusBadRequest, err)
	}

	log := ctx.Log().With("RmsName", rp.RmsName, "Name", rp.Name)
	log.Infof("CreateResourcePool")

	rp, err := h.service.ResourcePool.Create(ctx, rp)
	if err != nil {
		log.Errorf(err, "CreateResourcePool failed")
		return err
	}

	return ctx.JSON(http.StatusOK, rp)
}

// UpdateResourcePoolsStatus ...
// @Summary Update ResourcePool
// @ID update-resourcepools-status
// @Tags resourcepool
// @Security Bearer
// @Param resourcepools body reqM.ResourcePoolUpdateRequest true "ResourcePools with metrics to update"
// @Success 200 {object} respM.ResourcePoolListResponse
// @Failure default {object} HTTPError
// @Router /resourcepool/status [put]
func (h *HTTPHandler) UpdateResourcePoolsStatus(c echo.Context) error {
	ctx := c.(utils.Context)

	req := &reqM.ResourcePoolUpdateRequest{}

	if err := ctx.Bind(req); err != nil {
		return serror.New(http.StatusBadRequest, err)
	}

	log := ctx.Log().With("ResourcePoolNum", len(req.ResourcePools))
	log.Infof("UpdateResourcePools")

	rps, err := h.service.ResourcePool.UpdateStatus(ctx, req.ResourcePools)
	if err != nil {
		log.Errorf(err, "UpdateResourcePool failed")
		return err
	}

	return ctx.JSON(http.StatusOK, respM.ResourcePoolListResponse{
		Total: len(rps),
		Data:  rps,
	})
}

// UpdateResourcePool ...
// @Summary Update ResourcePool
// @ID update-resourcepool
// @Tags resourcepool
// @Security Bearer
// @Param resourcepool body model.ResourcePool true "ResourcePool to update"
// @Success 200 {object} map[string]interface{}
// @Failure default {object} HTTPError
// @Router /resourcepool [put]
func (h *HTTPHandler) UpdateResourcePool(c echo.Context) error {
	ctx := c.(utils.Context)

	rp := &model.ResourcePool{}

	if err := ctx.Bind(rp); err != nil {
		return serror.New(http.StatusBadRequest, err)
	}
	if err := rp.BorrowedPolicy.Validate(); err != nil {
		return serror.New(http.StatusBadRequest, err)
	}

	log := ctx.Log().With("ResourcePool", rp)
	log.Infof("UpdateResourcePool")

	err := h.service.ResourcePool.Update(ctx, rp)
	if err != nil {
		log.Errorf(err, "UpdateResourcePoolAliases failed")
		return err
	}

	return ctx.JSON(http.StatusOK, echo.Map{"result": "ok"})
}

// DeleteResourcePools ...
// @Summary Delete an existing resourcepool
// @ID delete-resourcepool
// @Tags resourcepool
// @Security Bearer
// @Param rms_name query string false "Filter by rms_name"
// @Param names query string false "Choose resourcepools in names"
// @Success 200 {object} map[string]interface{}
// @Failure default {object} HTTPError
// @Router /resourcepool [delete]
func (h *HTTPHandler) DeleteResourcePools(c echo.Context) error {
	ctx := c.(utils.Context)

	rmsName := c.QueryParam("rms_name")
	names, err := utils.ParseCSV(ctx.QueryParam("names"))
	if err != nil {
		return serror.New(http.StatusBadRequest, err)
	}

	log := ctx.Log().With("rmsName", rmsName, "Names", names)
	log.Infof("DeleteResourcePool")

	err = h.service.ResourcePool.Delete(ctx, string(rmsName), names)
	if err != nil {
		log.Errorf(err, "delete ResourcePool failed")
		return err
	}

	return ctx.JSON(http.StatusOK, echo.Map{"result": "ok"})
}

// CreateRMS ...
// @Summary Create RMS
// @ID create-rms
// @Tags rms
// @Security Bearer
// @Param rms body model.RMS true "RMS to create"
// @Success 200 {object} model.RMS
// @Failure default {object} HTTPError
// @Router /rms [post]
func (h *HTTPHandler) CreateRMS(c echo.Context) error {
	ctx := c.(utils.Context)

	rms := &model.RMS{}

	if err := ctx.Bind(rms); err != nil {
		return serror.New(http.StatusBadRequest, err)
	}

	log := ctx.Log().With("Endpoint", rms.Endpoint)
	log.Infof("CreateRMS")

	rms, err := h.service.RMS.Create(ctx, *rms)
	if err != nil {
		log.Errorf(err, "CreateRMS failed")
		return err
	}

	return ctx.JSON(http.StatusOK, rms)
}

// UpdateRMS ...
// @Summary Update RMS
// @ID update-rms
// @Tags rms
// @Security Bearer
// @Param rms body model.RMS true "RMS to update"
// @Success 200 {object} map[string]interface{}
// @Failure default {object} HTTPError
// @Router /rms [put]
func (h *HTTPHandler) UpdateRMS(c echo.Context) error {
	ctx := c.(utils.Context)

	rms := &model.RMS{}

	if err := ctx.Bind(rms); err != nil {
		return serror.New(http.StatusBadRequest, err)
	}

	log := ctx.Log().With("RMS", rms)
	log.Infof("UpdateRMS")

	err := h.service.RMS.Update(ctx, *rms)
	if err != nil {
		log.Errorf(err, "UpdateRMS failed")
		return err
	}

	return ctx.JSON(http.StatusOK, echo.Map{"result": "ok"})
}

// GetRMS ...
// @Summary Get RMSs
// @ID get-rms
// @Tags rms
// @Param name path string true "RMS name"
// @Success 200 {object} model.RMS
// @Failure default {object} HTTPError
// @Router /rms/:name [get]
func (h *HTTPHandler) GetRMS(c echo.Context) error {
	ctx := c.(utils.Context)

	name := ctx.Param("name")

	resp, err := h.service.RMS.Get(ctx, name)
	if err != nil {
		ctx.Log().Errorf(err, "GetRMS failed")
		return err
	} else if resp == nil {
		return serror.New(http.StatusNotFound)
	}

	return ctx.JSON(http.StatusOK, resp)
}

// ListRMSs ...
// @Summary List RMSs
// @ID list-rms
// @Tags rms
// @Success 200 {object} []model.RMS
// @Failure default {object} HTTPError
// @Router /rms [get]
func (h *HTTPHandler) ListRMSs(c echo.Context) error {
	ctx := c.(utils.Context)

	resp, err := h.service.RMS.List(ctx)
	if err != nil {
		ctx.Log().Errorf(err, "ListRMSs failed")
		return err
	}

	return ctx.JSON(http.StatusOK, resp)
}
