package reverse_proxy

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

	conf "zeevdiukman.com/zprox/internal/config"
)

// var config = conf.New()

type ReverseProxy struct {
	Name     string
	Host     string
	Httputil *httputil.ReverseProxy
}

func New(host string) *ReverseProxy {
	return &ReverseProxy{
		Host: host,
		Httputil: &httputil.ReverseProxy{
			Director: DefaultDirector(host),
		},
	}
}

type Data struct {
	Name  string
	Value any
}
type DirectorFunc func(req *http.Request, data []Data) (*http.Request, []Data)

func GetData[T any](key string, data []Data) (res T) {
	for _, v := range data {
		if v.Name == key {
			res = v.Value.(T)
		}
	}
	return res
}

func (rp *ReverseProxy) DefaultDirectorFunc(d []Data, fn DirectorFunc) func(*http.Request) {
	return func(r *http.Request) {
		host := ""
		req, data := fn(r, d)
		proxyData := GetData[conf.ReverseProxy]("proxy_data", data)
		host = proxyData.Host
		target, _ := url.Parse(host)
		targetQuery := target.RawQuery
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL)
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}
}
func DefaultDirector(host string) func(*http.Request) {
	return func(req *http.Request) {
		target, _ := url.Parse(host)
		targetQuery := target.RawQuery
		req.URL.Scheme = target.Scheme
		req.URL.Host = target.Host
		req.URL.Path, req.URL.RawPath = joinURLPath(target, req.URL)
		if targetQuery == "" || req.URL.RawQuery == "" {
			req.URL.RawQuery = targetQuery + req.URL.RawQuery
		} else {
			req.URL.RawQuery = targetQuery + "&" + req.URL.RawQuery
		}
	}
}
func joinURLPath(a, b *url.URL) (path, rawpath string) {
	if a.RawPath == "" && b.RawPath == "" {
		return SingleJoiningSlash(a.Path, b.Path), ""
	}
	// Same as singleJoiningSlash, but uses EscapedPath to determine
	// whether a slash should be added
	apath := a.EscapedPath()
	bpath := b.EscapedPath()

	aslash := strings.HasSuffix(apath, "/")
	bslash := strings.HasPrefix(bpath, "/")

	switch {
	case aslash && bslash:
		return a.Path + b.Path[1:], apath + bpath[1:]
	case !aslash && !bslash:
		return a.Path + "/" + b.Path, apath + "/" + bpath
	}
	return a.Path + b.Path, apath + bpath
}

func SingleJoiningSlash(a, b string) string {
	aslash := strings.HasSuffix(a, "/")
	bslash := strings.HasPrefix(b, "/")
	switch {
	case aslash && bslash:
		return a + b[1:]
	case !aslash && !bslash:
		return a + "/" + b
	}
	return a + b
}
