package proxy

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"strings"

	"github.com/pingcap/log"
	"github.com/tidbcloud/tiinf/monitor/tilog"
	"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
	"go.uber.org/zap"
)

const proxyPrefix = "/api/v1/tcidm/proxy/"

type proxyRoute struct {
	name    string
	handler http.Handler
}

var proxyRoutes []proxyRoute

func registerProxyRoute(r proxyRoute) {
	proxyRoutes = append(proxyRoutes, r)
}

func Handler() http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		ctx := req.Context()
		path := req.URL.Path
		path = strings.TrimPrefix(path, proxyPrefix)
		items := strings.Split(path, "/")
		if len(items) < 1 {
			tilog.Error(ctx, "invalid proxy path", zap.String("path", req.URL.Path))
			http.Error(w, fmt.Sprintf("invalid proxy path: %s", req.URL.Path), http.StatusBadRequest)
			return
		}
		routeName := items[0]
		routePath := items[1:]
		for _, route := range proxyRoutes {
			if routeName == route.name {
				req.URL.Path = strings.Join(routePath, "/")
				route.handler.ServeHTTP(w, req)
				return
			}
		}
		http.Error(w, fmt.Sprintf("no route matched for proxy path: %s", req.URL.Path), http.StatusNotFound)
	})
}

func genReverseProxy(addr string) *httputil.ReverseProxy {
	// input addr should include schema, like : http://xxx
	targetURL, _ := url.Parse(addr)
	proxy := httputil.NewSingleHostReverseProxy(targetURL)
	proxy.ErrorHandler = func(w http.ResponseWriter, r *http.Request, err error) {
		w.WriteHeader(http.StatusBadGateway)
		log.L().Error("proxying the request failed", zap.String("path", r.URL.Path), zap.Error(err))
	}
	
	// Use OpenTelemetry instrumented transport
	baseTransport := &http.Transport{
		DisableKeepAlives: true,
	}
	
	// Wrap the transport with OpenTelemetry instrumentation
	proxy.Transport = otelhttp.NewTransport(
		baseTransport,
		otelhttp.WithSpanNameFormatter(func(operation string, r *http.Request) string {
			return fmt.Sprintf("%s %s %s", "tcidm-proxy", r.Method, r.URL.Path)
		}),
	)
	
	return proxy
}
