package api

import (
	"net/http"
	"net/http/pprof"
	"time"

	"github.com/gorilla/mux"
	"github.com/ngaut/log"
	"github.com/pingcap/schrodinger/manager/core"
)

type traceResponseWriter struct {
	http.ResponseWriter
	statusCode int
}

func (w *traceResponseWriter) WriteHeader(code int) {
	w.statusCode = code
	w.ResponseWriter.WriteHeader(code)
}

func httpRequestMiddleware(h http.Handler, mgr *core.Manager) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		log.Infof("Request : %s - %s - %s", r.RemoteAddr, r.Method, r.URL)
		start := time.Now()
		tw := &traceResponseWriter{w, http.StatusOK}
		h.ServeHTTP(tw, r)
		log.Infof("Response [%d] : %s - %s - %s (%.3f sec)",
			tw.statusCode, r.RemoteAddr, r.Method, r.URL, time.Since(start).Seconds())
	})
}

// AttachProfiler attachs pprofs
func AttachProfiler(router *mux.Router) {
	router.HandleFunc("/debug/pprof/", pprof.Index)
	router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	router.HandleFunc("/debug/pprof/profile", pprof.Profile)
	router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)

	// Manually add support for paths linked to by index page at /debug/pprof/
	router.Handle("/debug/pprof/goroutine", pprof.Handler("goroutine"))
	router.Handle("/debug/pprof/heap", pprof.Handler("heap"))
	router.Handle("/debug/pprof/threadcreate", pprof.Handler("threadcreate"))
	router.Handle("/debug/pprof/block", pprof.Handler("block"))
}

// CreateRouter creates router for api
func CreateRouter(mgr *core.Manager) http.Handler {
	r := mux.NewRouter()
	AttachProfiler(r)
	hdl := newManagerHandler(mgr)

	// box
	r.HandleFunc("/boxes", hdl.ListBox).Methods("GET")
	r.HandleFunc("/boxes", hdl.CreateBox).Methods("POST")
	r.HandleFunc("/boxes/{id}", hdl.GetBox).Methods("GET")
	r.HandleFunc("/boxes/{id}", hdl.UpdateBox).Methods("PUT")
	r.HandleFunc("/boxes/{id}", hdl.DeleteBox).Methods("DELETE")
	r.HandleFunc("/boxes/{id}/stop", hdl.StopBox).Methods("POST")
	r.HandleFunc("/boxes/{id}/recover", hdl.RecoverBox).Methods("POST")
	r.HandleFunc("/boxes/{id}/trigger", hdl.ManualTrigger).Methods("POST")
	r.HandleFunc("/boxes/byname/{name}", hdl.GetBoxByName).Methods("GET")
	r.HandleFunc("/boxes/bytag/{tag}", hdl.GetBoxesByTag).Methods("GET")

	// box types
	r.HandleFunc("/box/types", hdl.GetBoxType).Methods("GET")

	// experiment
	r.HandleFunc("/boxes/{boxID}/experiments", hdl.ListExperimentByBoxID).Methods("GET")
	r.HandleFunc("/boxes/{boxID}/experiments/{id}", hdl.GetExperiment).Methods("GET")
	r.HandleFunc("/boxes/{boxID}/experiments/{id}/detail", hdl.GetExperimentDetail).Methods("GET")
	r.HandleFunc("/boxes/{boxID}/experiments/{id}/destroy", hdl.DestroyExperiment).Methods("POST")
	r.HandleFunc("/boxes/{boxID}/experiments/{id}/cancel", hdl.CancelExperiment).Methods("POST")
	r.HandleFunc("/boxes/{boxID}/experiments/{id}/redo", hdl.RedoExperiment).Methods("POST")
	r.HandleFunc("/boxes/{boxID}/experiments/{id}/replaceCat", hdl.ReplaceCat).Methods("POST")
	r.HandleFunc("/boxes/{boxID}/experiments/{id}/mark", hdl.AnnotateExperiment).Methods("PUT")

	// experiments list
	r.HandleFunc("/experiments", hdl.ListExperiment).Methods("GET")
	r.HandleFunc("/experiments/{id}/detail", hdl.GetExperimentDetailReport).Methods("GET")

	// test
	r.HandleFunc("/boxes/{boxID}/experiments/{experimentID}/tests", hdl.ListTest).Methods("GET")
	r.HandleFunc("/boxes/{boxID}/experiments/{experimentID}/tests/{name}", hdl.GetTest).Methods("GET")
	r.HandleFunc("/boxes/{boxID}/experiments/{experimentID}/tests/{name}/stop", hdl.StopTest).Methods("POST")
	r.HandleFunc("/boxes/{boxID}/experiments/{experimentID}/tests/{name}/start", hdl.StartTest).Methods("POST")
	r.HandleFunc("/boxes/{boxID}/experiments/{experimentID}/tests/{name}/remove", hdl.RemoveTest).Methods("POST")
	r.HandleFunc("/boxes/{boxID}/experiments/{experimentID}/tests/{name}/detail", hdl.GetTestDetail).Methods("GET")
	r.HandleFunc("/boxes/{boxID}/experiments/{experimentID}/tests/{name}/upload", hdl.UploadTest).Methods("POST")

	// cat
	r.HandleFunc("/cats", hdl.ListCat).Methods("GET")
	r.HandleFunc("/cats", hdl.CreateCat).Methods("POST")
	r.HandleFunc("/cats/{id}", hdl.GetCat).Methods("GET")
	r.HandleFunc("/cats/{id}", hdl.DestroyCat).Methods("DELETE")
	r.HandleFunc("/cats/{id}/upgrade", hdl.UpgradeCat).Methods("POST")
	r.HandleFunc("/cats/{id}/detail", hdl.GetCatDetail).Methods("GET")
	r.HandleFunc("/cats/{id}/backup/{backupName}", hdl.BackupCat).Methods("POST")
	r.HandleFunc("/cats/{id}/backups/list", hdl.ListCatBackups).Methods("GET")
	r.HandleFunc("/cats/{id}/restore/{backupName}", hdl.RestoreCat).Methods("POST")
	r.HandleFunc("/cats/{id}/status", hdl.GetCatStatus).Methods("GET")
	r.HandleFunc("/cats/byname/{name}", hdl.GetCatByName).Methods("GET")
	r.HandleFunc("/cats/list/names", hdl.ListCatNames).Methods("GET")

	// cat config template
	r.HandleFunc("/cat_config/templates", hdl.ListCatConfigTemplates).Methods("GET")
	r.HandleFunc("/cat_config/templates", hdl.CreateCatConfigTemplate).Methods("POST")
	r.HandleFunc("/cat_config/templates/{id}", hdl.UpdateCatConfigTemplate).Methods("PUT")
	r.HandleFunc("/cat_config/templates/{id}", hdl.GetCatConfigTemplate).Methods("GET")
	r.HandleFunc("/cat_cinfig/templates/name/{name}", hdl.GetCatConfigTemplateByName).Methods("GET")
	r.HandleFunc("/cat_config/templates/{id}", hdl.DeleteCatConfigTemplate).Methods("DELETE")
	r.HandleFunc("/cat_config/templates/tag/{tag}", hdl.GetCatConfigTemplatesByTag).Methods("GET")
	r.HandleFunc("/cat_config/templates/list/names", hdl.ListCatConfigTemplateNames).Methods("GET")

	// compatible with old test_client
	r.HandleFunc("/cats/{id}/backup", hdl.BackupCat).Methods("POST")
	r.HandleFunc("/cats/{id}/restore", hdl.RestoreCat).Methods("POST")

	// test template
	r.HandleFunc("/tests/templates", hdl.CreateTestTemplate).Methods("POST")
	r.HandleFunc("/tests/templates", hdl.ListTestTemplate).Methods("GET")

	r.HandleFunc("/tests/templates/list/names", hdl.ListTestTemplateNames).Methods("GET")
	r.HandleFunc("/tests/templates/list/nameWithTags", hdl.ListTestTemplateNameWithTags).Methods("GET")
	r.HandleFunc("/tests/templates/{name}", hdl.GetTestTemplateByName).Methods("GET")
	r.HandleFunc("/tests/templates/{name}/latest", hdl.GetTestTemplateByNameAndLatest).Methods("GET")
	r.HandleFunc("/tests/templates/{name}/pr/{id}", hdl.GetTestTemplateByNameAndPR).Methods("GET")
	r.HandleFunc("/tests/templates/{name}/{id}", hdl.GetTestTemplateByID).Methods("GET")

	r.HandleFunc("/tests/templates/{name}/{id}", hdl.UpdateTestTemplate).Methods("PUT")
	r.HandleFunc("/tests/templates/{name}/{id}", hdl.DeleteTestTemplate).Methods("DELETE")
	r.HandleFunc("/tests/templates/bytag/{tag}", hdl.GetTestTemplatesByTag).Methods("GET")

	// test result
	r.HandleFunc("/tests/results", hdl.SetTestResult).Methods("POST")
	r.HandleFunc("/tests/results/{id}", hdl.GetTestResult).Methods("GET")
	r.HandleFunc("/tests/results/experiments/{experimentID}/{testName}", hdl.GetTestResultByExperimentWithTestName).Methods("GET")
	r.HandleFunc("/tests/results/experiments/{experimentID}", hdl.ListTestResultsByExperiment).Methods("GET")
	r.HandleFunc("/tests/results/box/{boxID}", hdl.ListTestResultsByBox).Methods("GET")

	// test config
	r.HandleFunc("/testConfig/{id}", hdl.GetTestConfig).Methods("GET")

	// failpoint
	r.HandleFunc("/failpoints", hdl.SetFailpoint).Methods("POST")
	r.HandleFunc("/failpoints", hdl.ListFailpoints).Methods("GET")
	r.HandleFunc("/failpoints/{name}", hdl.GetFailpoint).Methods("GET")

	// manager config
	r.HandleFunc("/managerConfig", hdl.ManagerConfig).Methods("GET")

	// summary
	r.HandleFunc("/summary/experiments", hdl.ListExperimentByTime).Methods("GET")

	// configmap
	r.HandleFunc("/configmaps", hdl.ConfigMap).Methods("GET")

	// misc
	r.HandleFunc("/tasks", hdl.ListRunningTasks).Methods("GET")

	// app
	r.HandleFunc("/app", hdl.CreateAPP).Methods("POST")
	r.HandleFunc("/app/{namespace}/{name}", hdl.DeleteAPP).Methods("DELETE")

	// object
	r.HandleFunc("/objects", hdl.CreateObject).Methods("POST")
	r.HandleFunc("/objects", hdl.ListObjects).Methods("GET")
	r.HandleFunc("/objects/{id}", hdl.GetObject).Methods("GET")
	r.HandleFunc("/objects/{id}", hdl.UpdateObject).Methods("PUT")
	r.HandleFunc("/boxes/{boxID}/objects", hdl.ListObjectsByBoxID).Methods("GET")
	r.HandleFunc("/boxes/{boxID}/objects/{name}", hdl.GetObjectByBoxID).Methods("GET")
	r.HandleFunc("/experiments/{experimentID}/objects", hdl.ListObjectsByExperimentID).Methods("GET")
	r.HandleFunc("/experiments/{experimentID}/objects/{name}", hdl.GetObjectByExperimentID).Methods("GET")

	// resource usage
	r.HandleFunc("/resource/usage", hdl.GetResourceUsage).Methods("GET")
	// manager config
	r.HandleFunc("/config/general-experiment-running-size-limit", hdl.SetGeneralExperimentRunningSizeLimit).Methods("POST")
	r.HandleFunc("/config/general-experiment-running-size-limit", hdl.GetGeneralExperimentRunningSizeLimit).Methods("GET")
	r.HandleFunc("/config/all-experiment-running-size-limit", hdl.GetAllExperimentRunningSizeLimit).Methods("GET")
	r.HandleFunc("/config/bench-experiment-running-size-limit", hdl.SetBenchExperimentRunningSizeLimit).Methods("POST")
	r.HandleFunc("/config/bench-experiment-running-size-limit", hdl.GetBenchExperimentRunningSizeLimit).Methods("GET")

	// tags
	r.HandleFunc("/tags", hdl.CreateTag).Methods("POST")
	r.HandleFunc("/tags", hdl.ListTags).Methods("GET")
	r.HandleFunc("/tags/{id}", hdl.GetTag).Methods("GET")
	r.HandleFunc("/tags/{id}", hdl.UpdateTag).Methods("PUT")
	r.HandleFunc("/tags/kind/{kind}", hdl.GetTagsByKind).Methods("GET")
	r.HandleFunc("/tags/{kind}/{name}/tag", hdl.GetTagByNameAndKind).Methods("GET")
	r.HandleFunc("/tags/kind/{kind}/list/names", hdl.GetTagsNamesByKind).Methods("GET")

	return httpRequestMiddleware(r, mgr)
}
