package api

import (
	"net/http"
	"strconv"

	"github.com/gorilla/mux"
	"github.com/pingcap/schrodinger/pkg/storage"
	"github.com/pingcap/schrodinger/pkg/types"
	"github.com/pingcap/schrodinger/util"
)

// CreateBox creates box
func (h *ManagerHandler) CreateBox(w http.ResponseWriter, r *http.Request) {
	box := new(types.Box)
	if err := readJSON(r.Body, box); err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("read json body error %v", err))
		return
	}

	if err := box.Verify(); err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("verify box %+v error %v", box, err))
		return
	}

	box.SetDefaultValue()

	err := h.mgr.CreateBox(box)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("create box %+v error %v", box, err))
		return
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse("ok"))
}

// GetBox gets box
func (h *ManagerHandler) GetBox(w http.ResponseWriter, r *http.Request) {
	boxID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get box id error %v", err))
		return
	}

	box, err := h.mgr.GetBox(boxID)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get box %d failed %v", boxID, err))
		return
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse(box))
}

// GetBoxByName gets box by name
func (h *ManagerHandler) GetBoxByName(w http.ResponseWriter, r *http.Request) {
	boxName := mux.Vars(r)["name"]
	if util.IsEmpty(boxName) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("box name can not be empty"))
		return
	}

	box, err := h.mgr.GetBoxByName(boxName)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get box %s failed %v", boxName, err))
		return
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse(box))
}

// GetBoxesByTag gets boxes by kind
func (h *ManagerHandler) GetBoxesByTag(w http.ResponseWriter, r *http.Request) {
	tag := mux.Vars(r)["tag"]
	if util.IsEmpty(tag) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("box tag can not be empty"))
		return
	}

	boxes := h.mgr.GetBoxesByTag(tag)
	size := getSize(r)
	offset := getOffset(r)
	start, end := getPagingIndexes(int64(len(boxes)), offset, size)

	listData := ListData{
		List:   boxes[start:end],
		Offset: offset,
		Size:   size,
		Total:  int64(len(boxes)),
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse(listData))
}

// UpdateBox updates box
func (h *ManagerHandler) UpdateBox(w http.ResponseWriter, r *http.Request) {
	boxID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get box id error %v", err))
		return
	}

	box := new(types.Box)
	if err = readJSON(r.Body, box); err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("read json body error %v", err))
		return
	}

	if err = box.Verify(); err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("verify box %+v error %v", box, err))
		return
	}

	stroageBox, err := h.mgr.GetBox(boxID)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("can not get box %d from storage", boxID))
		return
	}

	newBox := mergeBox(stroageBox, box)

	err = h.mgr.UpdateBox(newBox)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("update box %+v error %v", box, err))
		return
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse("ok"))
}

// ListBox lists box
func (h *ManagerHandler) ListBox(w http.ResponseWriter, r *http.Request) {
	expStage := r.URL.Query()["stage"]
	expStatus := r.URL.Query()["status"]
	expDestroyed := r.URL.Query()["destroyed"]
	boxType := r.URL.Query()["box_type"]
	names := r.URL.Query()["name"]
	LimitEverBoxStr := r.URL.Query().Get("experimentsLimitEveryBox")

	LimitEveryBox, err := strconv.ParseInt(LimitEverBoxStr, 10, 64)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("list box error %v", err))
		return
	}

	size := getSize(r)
	offset := getOffset(r)

	boxes, total := h.mgr.GetBoxWithSummaryPagination(&storage.BoxConditions{
		BoxType:       boxType,
		Names:         names,
		ExpStage:      expStage,
		ExpStatus:     expStatus,
		ExpDestroyed:  expDestroyed,
		LimitEveryBox: LimitEveryBox,
		Limit:         size,
		Offset:        offset,
	}, &storage.ExperimentProcessConditions{
		LimitEveryBox: LimitEveryBox,
	})

	listData := ListData{
		List:   boxes,
		Offset: offset,
		Size:   size,
		Total:  total,
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse(listData))
}

// DeleteBox deletes box
func (h *ManagerHandler) DeleteBox(w http.ResponseWriter, r *http.Request) {
	boxID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get box id error %v", err))
		return
	}

	err = h.mgr.DeleteBox(boxID)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("delete box %d error %v", boxID, err))
		return
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse("ok"))
}

// StopBox stops box
func (h *ManagerHandler) StopBox(w http.ResponseWriter, r *http.Request) {
	boxID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get box id error %v", err))
		return
	}

	err = h.mgr.StopBox(boxID)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("stop box %d error %v", boxID, err))
		return
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse("ok"))
}

// RecoverBox recovers box
func (h *ManagerHandler) RecoverBox(w http.ResponseWriter, r *http.Request) {
	boxID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get box id error %v", err))
		return
	}

	err = h.mgr.RecoverBox(boxID)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("recover box %d error %v", boxID, err))
		return
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse("ok"))
}

// ManualTrigger trigger box immediately
func (h *ManagerHandler) ManualTrigger(w http.ResponseWriter, r *http.Request) {
	boxID, err := strconv.ParseInt(mux.Vars(r)["id"], 10, 64)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get box id error %v", err))
		return
	}

	err = h.mgr.CreateImmediatelyBox(boxID)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("stop box %d error %v", boxID, err))
		return
	}

	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse("ok"))
}

// GetBoxType gets box types
func (h *ManagerHandler) GetBoxType(w http.ResponseWriter, r *http.Request) {
	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse(types.BoxTypes))
}
