// package cluster provides services and proxies related to the Kubernetes cluster.
package cluster

import (
	"fmt"
	"net/http"
	"path"
	"strings"

	"github.com/pingcap/log"
	"go.uber.org/zap"

	"github.com/tidbcloud/clusterproxy/resolve"
	"github.com/tidbcloud/clusterproxy/route"
	"github.com/tidbcloud/clusterproxy/route/router"
)

const (
	dwWorkerAPIPort = "8185"
)

var dwWorkerRoute *route.Route

func init() {
	defaultDWWorkerGetHandler := NewDWWorkerGetHandlerBuilder(generateReverseProxyForDWWorker).Build()
	dwWorkerRoute = &route.Route{
		Name:    "dwworker-v1alpha1",
		Path:    "dwworker",
		Version: route.V1Alpha1,
		Schema: &route.Schema{
			PathRegex:       `.*`,
			RequiredHeaders: []string{},
			JSONBodyModel:   nil,
		},
		GetHandler: defaultDWWorkerGetHandler,
	}
	router.RegisterRoute(dwWorkerRoute)
}

type generateDWWorkerHandlerFunc func(c *route.Context, cl resolve.Cluster, dwWorkerID string) http.Handler

type DWWorkerGetHandlerBuilder struct {
	handlerGen generateDWWorkerHandlerFunc
}

func NewDWWorkerGetHandlerBuilder(handlerGen generateDWWorkerHandlerFunc) *DWWorkerGetHandlerBuilder {
	return &DWWorkerGetHandlerBuilder{
		handlerGen: handlerGen,
	}
}

func (b *DWWorkerGetHandlerBuilder) Build() func(*route.Context, resolve.Cluster) http.Handler {
	return func(c *route.Context, cl resolve.Cluster) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			dwWorkerID, processedPath := parseDWWorkerURLPath(r.URL.Path)
			log.Info("dwwoker url parse result", zap.Any("original url", r.URL), zap.String("dwwoker_id", dwWorkerID), zap.String("processed_path", processedPath))
			if len(dwWorkerID) == 0 {
				log.Warn("dwwoker id is empty")
				http.NotFound(w, r)
				return
			}
			r.URL.Path = processedPath
			dwWorkerHandler := b.handlerGen(c, cl, dwWorkerID)
			dwWorkerHandler.ServeHTTP(w, r)
		})
	}
}

func generateReverseProxyForDWWorker(c *route.Context, cl resolve.Cluster, dwWorkerID string) http.Handler {
	namespace := cl.Namespace
	dwServiceName := fmt.Sprintf("dwworker-%s", dwWorkerID)
	proxy := route.GetKubernetesServiceProxy("http", namespace, dwServiceName, dwWorkerAPIPort, nil)
	return proxy
}

// parseDWWorkerURLPath parses the dwWorker id and the processed path from the original path.
func parseDWWorkerURLPath(originalPath string) (string, string) {
	if len(originalPath) == 0 {
		return "", ""
	}
	cleanedPath := path.Clean(originalPath)
	noPrefixSlashPath := strings.TrimPrefix(cleanedPath, "/")
	paths := strings.Split(noPrefixSlashPath, "/")
	dwWorkerID := paths[0]
	if len(dwWorkerID) == 0 {
		return "", ""
	}
	processedPath := path.Join(
		append(
			[]string{"/"},
			paths[1:]...,
		)...,
	)
	return dwWorkerID, processedPath
}
