package handler

import (
	"fmt"
	"net/http"

	"github.com/creasty/defaults"
	"github.com/gorilla/schema"
	"github.com/labstack/echo/v4"
	"github.com/pingcap/test-infra/common/model/tcms/taskruns"
	"github.com/pingcap/test-infra/services/tcms/service"
	serror "github.com/pingcap/test-infra/services/tcms/service/common/errors"
	"github.com/pingcap/test-infra/services/tcms/utils"
)

type SvcTaskHandler struct {
	svc     service.SvcTaskService
	decoder *schema.Decoder
}

// List SvcTask ...
// @Summary List SvcTask
// @ID list-SvcTask
// @Tags SvcTask
// @Param count query integer false "Number of taskruns.SvcTask returned" default(20) minimum(1) maximum(100)
// @Param offset query integer false "Offset/skip number of taskruns.SvcTask" default(0) minimum(0)
// @Param dryRun query bool false "dry run" default(false)
// @Success 200 {object} taskruns.SvcTaskList
// @Failure default {object} HTTPError
// @Router /svctasks [get]
func (h SvcTaskHandler) List(c echo.Context) error {
	ctx := c.(utils.Context)
	opts := taskruns.ListSvcTaskOpts{}
	err := h.decoder.Decode(&opts, ctx.QueryParams())
	if err != nil {
		return err
	}
	if c.QueryParam("count") == "" {
		var defaultLimit uint = 100
		opts.Count = defaultLimit
	}
	list, err := h.svc.List(utils.EmbedIntoContext(ctx), opts)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, *list)
}

// Options SvcTask ...
// @Summary Options SvcTask
// @ID options-SvcTask
// @Tags SvcTask
// @Header 200 {string} Allow "OPTIONS, GET, POST"
// @Router /svctasks [options]
func (h SvcTaskHandler) Options(c echo.Context) error {
	c.Response().Header().Set("Allow", "OPTIONS, GET, POST")
	return c.NoContent(http.StatusNoContent)
}

// Get SvcTask ...
// @Summary Get SvcTask by ID
// @ID get-SvcTask
// @Tags SvcTask
// @Param id path integer true "ID"
// @Success 200 {object} taskruns.SvcTask
// @Failure default {object} HTTPError
// @Router /svctasks/{id} [get]
func (h SvcTaskHandler) Get(c echo.Context) error {
	ctx := c.(utils.Context)
	id, err := utils.ParseID(ctx.Param("id"))
	if err != nil {
		return serror.New(http.StatusBadRequest, err)
	}
	opts := taskruns.GetSvcTaskOpts{}
	err = h.decoder.Decode(&opts, ctx.QueryParams())
	if err != nil {
		return err
	}
	obj, err := h.svc.Get(utils.EmbedIntoContext(ctx), id, opts)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, *obj)
}

// GetByName SvcTask ...
// @Summary Get SvcTask by Name
// @ID get-SvcTask-by-name
// @Tags SvcTask
// @Param name path string true "name"
// @Success 200 {object} taskruns.SvcTask
// @Failure default {object} HTTPError
// @Router /svctasks/name/{name} [get]
func (h SvcTaskHandler) GetByName(c echo.Context) error {
	ctx := c.(utils.Context)
	name := ctx.Param("name")
	if name == "" {
		return serror.NewMsg(http.StatusBadRequest, "name must be provided")
	}
	opts := taskruns.GetSvcTaskOpts{}
	err := h.decoder.Decode(&opts, ctx.QueryParams())
	if err != nil {
		return err
	}
	obj, err := h.svc.GetByName(utils.EmbedIntoContext(ctx), name, opts)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, *obj)
}

// Delete SvcTask ...
// @Summary Delete SvcTask by ID
// @ID delete-SvcTask
// @Tags SvcTask
// @Security Bearer
// @Param id path integer true "SvcTask ID"
// @Success 200 {object} taskruns.SvcTask
// @Failure default {object} HTTPError
// @Router /svctasks/{id} [delete]
func (h SvcTaskHandler) Delete(c echo.Context) error {
	ctx := c.(utils.Context)
	id, err := utils.ParseID(ctx.Param("id"))
	if err != nil {
		return serror.New(http.StatusBadRequest, err)
	}
	opts := taskruns.DeleteSvcTaskOpts{}
	err = h.decoder.Decode(&opts, ctx.QueryParams())
	if err != nil {
		return err
	}
	obj, err := h.svc.Delete(utils.EmbedIntoContext(ctx), id, opts)
	if err != nil {
		return err
	}
	if obj == nil {
		return serror.New(http.StatusNotFound, err)
	}
	return c.JSON(http.StatusOK, *obj)
}

// Create SvcTask ...
// @Summary Create new SvcTask
// @ID create-SvcTask
// @Tags SvcTask
// @Security Bearer
// @Param obj body taskruns.SvcTask true "taskruns.SvcTask to create"
// @Success 200 {object} taskruns.SvcTask
// @Failure default {object} HTTPError
// @Router /svctasks [post]
func (h SvcTaskHandler) Create(c echo.Context) error {
	ctx := c.(utils.Context)
	opts := taskruns.CreateSvcTaskOpts{}
	err := h.decoder.Decode(&opts, ctx.QueryParams())
	if err != nil {
		return err
	}
	if ctx.DryRun() {
		opts.DryRun = ctx.DryRun()
	}
	req, err := h.bind(ctx)
	if err != nil {
		return err
	}
	created, err := h.svc.Create(utils.EmbedIntoContext(ctx), *req, opts)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, *created)
}

// Update SvcTask ...
// @Summary Create new SvcTask
// @ID create-SvcTask
// @Tags SvcTask
// @Security Bearer
// @Param obj body taskruns.SvcTask true "taskruns.SvcTask to create"
// @Param id path integer true "ID"
// @Success 200 {object} taskruns.SvcTask
// @Failure default {object} HTTPError
// @Router /svctasks [put]
func (h SvcTaskHandler) Update(c echo.Context) error {
	ctx := c.(utils.Context)
	opts := taskruns.UpdateSvcTaskOpts{}
	id, err := utils.ParseID(ctx.Param("id"))
	if err != nil {
		return serror.New(http.StatusBadRequest, err)
	}
	err = h.decoder.Decode(&opts, ctx.QueryParams())
	if err != nil {
		return err
	}
	if ctx.DryRun() {
		opts.DryRun = ctx.DryRun()
	}
	req, err := h.bind(ctx)
	if err != nil {
		return err
	}
	if req.MetaData.ID != 0 && req.MetaData.ID != id {
		return serror.New(http.StatusBadRequest, fmt.Errorf("id in path is inconsistent with body"))
	}
	updated, err := h.svc.Update(utils.EmbedIntoContext(ctx), *req, opts)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, *updated)
}

func (h SvcTaskHandler) bind(ctx echo.Context) (*taskruns.SvcTask, error) {
	req := &taskruns.SvcTask{}
	if err := ctx.Bind(req); err != nil {
		return nil, serror.New(http.StatusBadRequest, err)
	}
	defaults.MustSet(req)
	if err := req.Validate(); err != nil {
		return nil, serror.New(http.StatusBadRequest, err)
	}
	return req, nil
}
