package http

import (
	"net/http"
	"net/http/httputil"

	"github.com/gin-gonic/gin"
	"github.com/go-logr/logr"
)

type RequestOpts func(req *httputil.ProxyRequest)

type ProxyOpts func(proxy *httputil.ReverseProxy)

func ReplaceScheme(scheme string) RequestOpts {
	return func(req *httputil.ProxyRequest) {
		req.Out.URL.Scheme = scheme
	}
}

func ReplaceHost(host string) RequestOpts {
	return func(req *httputil.ProxyRequest) {
		req.Out.URL.Host = host
		req.Out.Host = host
	}
}

func AddQuery(query map[string]string) RequestOpts {
	return func(req *httputil.ProxyRequest) {
		if len(query) != 0 {
			q := req.Out.URL.Query()
			for k, v := range query {
				q.Add(k, v)
			}
			req.Out.URL.RawQuery = q.Encode()
		}
	}
}

func SetHeader(header map[string]string) RequestOpts {
	return func(req *httputil.ProxyRequest) {
		if len(header) != 0 {
			for k, v := range header {
				req.Out.Header.Add(k, v)
			}
		}
	}
}

func WithErrorLogger(logger logr.Logger) ProxyOpts {
	return func(proxy *httputil.ReverseProxy) {
		proxy.ErrorHandler = func(rw http.ResponseWriter, request *http.Request, err error) {
			logger.Error(err, "proxy failed", "url", request.URL.String())
			rw.WriteHeader(http.StatusBadGateway)
		}
	}
}

func Proxy(c *gin.Context, ropts []RequestOpts, popts []ProxyOpts) {
	proxy := &httputil.ReverseProxy{}
	proxy.Rewrite = func(preq *httputil.ProxyRequest) {
		preq.Out.Header["X-Forwarded-For"] = preq.In.Header["X-Forwarded-For"]
		preq.SetXForwarded()

		for _, opt := range ropts {
			opt(preq)
		}
	}
	for _, opt := range popts {
		opt(proxy)
	}

	proxy.ServeHTTP(c.Writer, c.Request)
}
