package api

import (
	"net/http"
	"strconv"

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

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

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

	if _, err := h.mgr.GetTagByNameAndKind(t.Name, t.Kind); err == nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("kind %s tag %s alredy exist", t.Kind, t.Name))
		return
	}

	if err := h.mgr.CreateTag(t); err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("store tag %+v error %v", t, err))
		return
	}

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

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

	oldTag, err := h.mgr.GetTag(id)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get tag %d error %v", id, err))
		return
	}

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

	t.ID = id
	t.Ctime = oldTag.Ctime

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

	if err := h.mgr.UpdateTag(t); err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("store tag %+v error %v", t, err))
		return
	}

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

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

	t, err := h.mgr.GetTag(id)
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get tag %d error %v", id, err))
		return
	}

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

// GetTagByNameAndKind returns the tag according the name and kind
func (h *ManagerHandler) GetTagByNameAndKind(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	if util.IsEmpty(name) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponse("tag name can not be empty"))
		return
	}

	kind := mux.Vars(r)["kind"]
	if util.IsEmpty(kind) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponse("tag kind can not be empty"))
		return
	}

	if !types.MatchInTagKinds(types.TagKind(kind)) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("tag kind %s not be supported", kind))
		return
	}

	t, err := h.mgr.GetTagByNameAndKind(name, types.TagKind(kind))
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get %s tag %s error %v", kind, name, err))
		return
	}

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

// GetTagsByKind returns all tag with the same kind
func (h *ManagerHandler) GetTagsByKind(w http.ResponseWriter, r *http.Request) {
	kind := mux.Vars(r)["kind"]
	if util.IsEmpty(kind) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponse("tag kind can not be empty"))
		return
	}

	if !types.MatchInTagKinds(types.TagKind(kind)) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("tag kind %s not be supported", kind))
		return
	}

	tags, err := h.mgr.GetTagsByKind(types.TagKind(kind))
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get %s tag error %v", kind, err))
		return
	}
	size := getSize(r)
	offset := getOffset(r)
	start, end := getPagingIndexes(int64(len(tags)), offset, size)

	listData := ListData{
		List:   tags[start:end],
		Offset: offset,
		Size:   size,
		Total:  int64(len(tags)),
	}
	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse(listData))
}

// GetTagsNamesByKind returns all tag with the same kind
func (h *ManagerHandler) GetTagsNamesByKind(w http.ResponseWriter, r *http.Request) {
	kind := mux.Vars(r)["kind"]
	if util.IsEmpty(kind) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponse("tag kind can not be empty"))
		return
	}

	if !types.MatchInTagKinds(types.TagKind(kind)) {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("tag kind %s not be supported", kind))
		return
	}

	tags, err := h.mgr.GetTagsByKind(types.TagKind(kind))
	if err != nil {
		h.rdr.JSON(w, http.StatusOK, util.ErrResponsef("get %s tag error %v", kind, err))
		return
	}

	names := tags.Names()
	size := getSize(r)
	offset := getOffset(r)
	start, end := getPagingIndexes(int64(len(names)), offset, size)

	listData := ListData{
		List:   names[start:end],
		Offset: offset,
		Size:   size,
		Total:  int64(len(names)),
	}
	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse(listData))
}

// ListTags lists all tags
func (h *ManagerHandler) ListTags(w http.ResponseWriter, r *http.Request) {
	tags := h.mgr.ListTags()
	size := getSize(r)
	offset := getOffset(r)
	start, end := getPagingIndexes(int64(len(tags)), offset, size)

	listData := ListData{
		List:   tags[start:end],
		Offset: offset,
		Size:   size,
		Total:  int64(len(tags)),
	}
	h.rdr.JSON(w, http.StatusOK, util.SuccessResponse(listData))
}
