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

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

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

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

const (
	lightningAPIPort = "8289"
)

var importRoute *route.Route

func init() {
	defaultImportGetHandler := NewImportGetHandlerBuilder(generateReverseProxyForImport).Build()
	importRoute = &route.Route{
		Name:    "import-v1alpha1",
		Path:    "import",
		Version: route.V1Alpha1,
		Schema: &route.Schema{
			PathRegex:       `.*`,
			RequiredHeaders: []string{},
			JSONBodyModel:   nil,
		},
		GetHandler: defaultImportGetHandler,
	}
	router.RegisterRoute(importRoute)
}

type generateImportHandlerFunc func(c *route.Context, cl resolve.Cluster, importID string) (http.Handler, error)

type ImportGetHandlerBuilder struct {
	handlerGen generateImportHandlerFunc
}

func NewImportGetHandlerBuilder(handlerGen generateImportHandlerFunc) *ImportGetHandlerBuilder {
	return &ImportGetHandlerBuilder{
		handlerGen: handlerGen,
	}
}

func (b *ImportGetHandlerBuilder) 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) {
			importID, processedPath := parseImportURLPath(r.URL.Path)
			log.Info("dump url parse result", zap.Any("original url", r.URL), zap.String("import_id", importID), zap.String("processed_path", processedPath))
			if len(importID) == 0 {
				log.Warn("import id is empty")
				http.NotFound(w, r)
				return
			}
			r.URL.Path = processedPath
			importHandler, err := b.handlerGen(c, cl, importID)
			if err != nil {
				log.Error("cannot generate import handler", zap.String("import_id", importID), zap.Error(err))
				http.Error(w, err.Error(), http.StatusInternalServerError)
				return
			}
			importHandler.ServeHTTP(w, r)
		})
	}
}

func generateReverseProxyForImport(c *route.Context, cl resolve.Cluster, importID string) (http.Handler, error) {
	namespace := cl.Namespace
	importServiceName := fmt.Sprintf("import-%s", importID)
	tlsClientSecretName := fmt.Sprintf(tlsClientSecretFormat, cl.Name)
	config, err := util.GetTLSConfig(c.Cli, namespace, "", tlsClientSecretName)
	if err != nil {
		return nil, errors.Annotate(err, "get tls config failed")
	}
	config.InsecureSkipVerify = true
	proxy := route.GetKubernetesServiceProxy("https", namespace, importServiceName, lightningAPIPort, &http.Transport{
		TLSClientConfig: config,
		MaxIdleConns:    1,
		IdleConnTimeout: 30 * time.Second,
	})
	return proxy, nil
}

func parseImportURLPath(originalPath string) (string, string) {
	if len(originalPath) == 0 {
		return "", ""
	}
	cleanedPath := path.Clean(originalPath)
	noPrefixSlashPath := strings.TrimPrefix(cleanedPath, "/")
	paths := strings.Split(noPrefixSlashPath, "/")
	importID := paths[0]
	if len(importID) == 0 {
		return "", ""
	}
	processedPath := path.Join(
		append(
			[]string{"/"},
			paths[1:]...,
		)...,
	)
	return importID, processedPath
}
