package api

import (
	"encoding/json"
	"io"
	"io/ioutil"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/schrodinger/agent/core"
	"github.com/unrolled/render"
)

type processHandler struct {
	a  *Agent
	m  *core.DaemonManager
	rd *render.Render
}

func newProcessHandler(a *Agent, rd *render.Render) *processHandler {
	return &processHandler{
		a:  a,
		m:  core.NewProcessManager(),
		rd: rd,
	}
}

func (p *processHandler) Start(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	log.Debugf("start %s", name)

	di := &core.DaemonInfo{}
	err := readJSON(r.Body, di)
	if err != nil {
		p.rd.JSON(w, http.StatusBadRequest, err.Error())
		return
	}

	err = p.m.Start(di)
	if err != nil {
		p.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}

	p.rd.JSON(w, http.StatusOK, nil)
}

func (p *processHandler) Down(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	log.Debugf("down %s", name)

	err := p.m.Down(name)
	if err != nil {
		p.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}

	p.rd.JSON(w, http.StatusOK, nil)
}

func (p *processHandler) Up(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	log.Debugf("up %s", name)

	err := p.m.Up(name)
	if err != nil {
		p.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}

	p.rd.JSON(w, http.StatusOK, nil)
}

func (p *processHandler) Kill(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	signal := mux.Vars(r)["signal"]
	log.Debugf("kill %s with signal %s", name, signal)

	err := p.m.Kill(name, signal)
	if err != nil {
		p.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}

	p.rd.JSON(w, http.StatusOK, nil)
}

func (p *processHandler) Stop(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]
	log.Debugf("stop %s", name)

	err := p.m.Stop(name)
	if err != nil {
		p.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}

	p.rd.JSON(w, http.StatusOK, nil)
}

func (p *processHandler) State(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]

	state, err := p.m.GetState(name)
	if err != nil {
		p.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}
	log.Debugf("state %s", state)

	p.rd.JSON(w, http.StatusOK, state)
}

func (p *processHandler) Detail(w http.ResponseWriter, r *http.Request) {
	name := mux.Vars(r)["name"]

	detail, err := p.m.GetDetail(name)
	if err != nil {
		p.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}
	log.Debugf("detail %+v", detail)

	p.rd.JSON(w, http.StatusOK, detail)
}

func (p *processHandler) Shell(w http.ResponseWriter, r *http.Request) {
	shell := &core.ShellCommand{}
	err := readJSON(r.Body, shell)
	if err != nil {
		p.rd.JSON(w, http.StatusBadRequest, err.Error())
		return
	}

	output, err := p.m.ExecShell(shell)
	if err != nil {
		p.rd.JSON(w, http.StatusInternalServerError, err.Error())
		return
	}
	log.Debugf("output %+v", output)

	p.rd.JSON(w, http.StatusOK, output)
}

func readJSON(r io.ReadCloser, data interface{}) error {
	defer r.Close()

	b, err := ioutil.ReadAll(r)
	if err != nil {
		return errors.Trace(err)
	}
	err = json.Unmarshal(b, data)
	if err != nil {
		return errors.Trace(err)
	}

	return nil
}
