package server

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/pprof"
	"time"

	"github.com/gorilla/mux"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"sigs.k8s.io/controller-runtime/pkg/client"

	pkgconfig "github.com/tidbcloud/observability/agent-control-server/pkg/config"
)

type Server struct {
	addr      string
	server    *http.Server
	router    *mux.Router
	client    client.Client
	config    *pkgconfig.RootConfig
	namespace string
}

func New(addr string, client client.Client, config *pkgconfig.RootConfig, namespace ...string) *Server {
	router := mux.NewRouter()
	ownedNamespace := ""
	if len(namespace) > 0 {
		ownedNamespace = namespace[0]
	}
	return &Server{
		addr:      addr,
		client:    client,
		config:    config,
		namespace: ownedNamespace,
		router:    router,
		server: &http.Server{
			Addr:         addr,
			Handler:      router,
			ReadTimeout:  time.Minute,
			WriteTimeout: time.Minute,
		},
	}
}

func (s *Server) listOptions() []client.ListOption {
	if s.namespace == "" {
		return nil
	}
	return []client.ListOption{client.InNamespace(s.namespace)}
}

func (s *Server) registerRoutes() {
	s.router.Handle("/metrics", promhttp.Handler())

	s.router.HandleFunc("/debug/pprof/", pprof.Index)
	s.router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
	s.router.HandleFunc("/debug/pprof/profile", pprof.Profile)
	s.router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
	s.router.HandleFunc("/debug/pprof/trace", pprof.Trace)

	s.router.HandleFunc("/agents", s.routeListAgents).Methods(http.MethodGet)
	s.router.HandleFunc("/agents/spec", s.routeGetAgentSpec).Methods(http.MethodGet)
	s.router.HandleFunc("/agents/spec", s.routePostAgentSpec).Methods(http.MethodPost)
	s.router.HandleFunc("/agents/pause", s.routePostPauseAgent).Methods(http.MethodPost)
	s.router.HandleFunc("/agents/resume", s.routePostResumeAgent).Methods(http.MethodPost)
	s.router.HandleFunc("/agents/restart", s.routePostRestartAgent).Methods(http.MethodPost)
	s.router.HandleFunc("/agents/drainnode", s.routePostDrainNode).Methods(http.MethodPost)
	s.router.HandleFunc("/agents/storage", s.routePostAgentStorage).Methods(http.MethodPost)

	s.router.HandleFunc("/ops/nodegroups", s.routeListNodeGroups).Methods(http.MethodGet)
	s.router.HandleFunc("/ops/nodegroups/upgrade", s.routePostNodeGroupsUpgrade).Methods(http.MethodPost)
}

func (s *Server) Start() error {
	s.registerRoutes()
	return s.server.ListenAndServe()
}

func (s *Server) Shutdown(ctx context.Context) error {
	return s.server.Shutdown(ctx)
}

type CommonResponse struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
	Data    any    `json:"data"`
}

func (s *Server) respond(w http.ResponseWriter, code int, message string) {
	s.respondWithData(w, code, message, nil)
}

func (s *Server) respondWithError(w http.ResponseWriter, code int, err error) {
	s.respondWithData(w, code, err.Error(), nil)
}

func (s *Server) respondWithData(w http.ResponseWriter, code int, message string, data any) {
	w.Header().Set("Content-Type", "application/json")
	if code >= 400 {
		w.WriteHeader(code)
	}
	_ = json.NewEncoder(w).Encode(CommonResponse{
		Code:    code,
		Message: message,
		Data:    data,
	})
}
