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 SvcTaskRunHandler struct {
	svc     service.SvcTaskRunService
	decoder *schema.Decoder
}

// List SvcTaskRun ...
// @Summary List SvcTaskRun
// @ID list-SvcTaskRun
// @Tags SvcTaskRun
// @Param count query integer false "Number of taskruns.SvcTaskRun returned" default(20) minimum(1) maximum(100)
// @Param offset query integer false "Offset/skip number of plans" default(0) minimum(0)
// @Param dryRun query bool false "dry run" default(false)
// @Success 200 {object} taskruns.SvcTaskRunList
// @Failure default {object} HTTPError
// @Router /svctaskruns [get]
func (h SvcTaskRunHandler) List(c echo.Context) error {
	ctx := c.(utils.Context)
	opts := taskruns.ListSvcTaskRunOpts{}
	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 SvcTaskRun ...
// @Summary Options SvcTaskRun
// @ID options-SvcTaskRun
// @Tags SvcTaskRun
// @Header 200 {string} Allow "OPTIONS, GET, POST"
// @Router /svctaskruns [options]
func (h SvcTaskRunHandler) Options(c echo.Context) error {
	c.Response().Header().Set("Allow", "OPTIONS, GET, POST")
	return c.NoContent(http.StatusNoContent)
}

// Get SvcTaskRun ...
// @Summary Get SvcTaskRun by ID
// @ID get-SvcTaskRun
// @Tags SvcTaskRun
// @Param id path integer true "ID"
// @Success 200 {object} taskruns.SvcTaskRun
// @Failure default {object} HTTPError
// @Router /svctaskruns/{id} [get]
func (h SvcTaskRunHandler) 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.GetSvcTaskRunOpts{}
	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
	}
	if obj == nil {
		return serror.New(http.StatusNotFound, err)
	}
	return c.JSON(http.StatusOK, *obj)
}

// Delete SvcTaskRun ...
// @Summary Delete SvcTaskRun by ID
// @ID delete-SvcTaskRun
// @Tags SvcTaskRun
// @Security Bearer
// @Param id path integer true "SvcTaskRun ID"
// @Success 200 {object} taskruns.SvcTaskRun
// @Failure default {object} HTTPError
// @Router /svctaskruns/{id} [delete]
func (h SvcTaskRunHandler) 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.DeleteSvcTaskRunOpts{}
	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 SvcTaskRun ...
// @Summary Create new SvcTaskRun
// @ID create-SvcTaskRun
// @Tags SvcTaskRun
// @Security Bearer
// @Param obj body taskruns.SvcTaskRun true "taskruns.SvcTaskRun to create"
// @Success 200 {object} taskruns.SvcTaskRun
// @Failure default {object} HTTPError
// @Router /svctaskruns [post]
func (h SvcTaskRunHandler) Create(c echo.Context) error {
	ctx := c.(utils.Context)
	opts := taskruns.CreateSvcTaskRunOpts{}
	err := h.decoder.Decode(&opts, ctx.QueryParams())
	if err != nil {
		return err
	}
	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)
}

// UpdateStatus Update SvcTaskRun Status ...
// @Summary Update SvcTaskRun Status
// @ID update-SvcTaskRun-status
// @Tags SvcTaskRun
// @Security Bearer
// @Param obj body taskruns.SvcTaskRun true "taskruns.SvcTaskRun to create"
// @Param id path integer true "ID"
// @Success 200 {object} taskruns.SvcTaskRun
// @Failure default {object} HTTPError
// @Router /svctaskruns/:id/status [put]
func (h SvcTaskRunHandler) UpdateStatus(c echo.Context) error {
	ctx := c.(utils.Context)
	opts := taskruns.UpdateSvcTaskRunOpts{}
	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
	}
	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"))
	}
	req.MetaData.ID = id
	updated, err := h.svc.UpdateStatus(utils.EmbedIntoContext(ctx), *req, opts)
	if err != nil {
		return err
	}
	return c.JSON(http.StatusOK, *updated)
}

func (h SvcTaskRunHandler) bind(ctx echo.Context) (*taskruns.SvcTaskRun, error) {
	req := &taskruns.SvcTaskRun{}
	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
}
