package http

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/http/httputil"
	"net/url"

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

type proxyOpts func(req *http.Request)

func ReplacePath(path string) proxyOpts {
	return func(req *http.Request) {
		req.URL.Path = path
	}
}

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

func AddHeader(header map[string]string) proxyOpts {
	return func(req *http.Request) {
		if len(header) != 0 {
			for k, v := range header {
				req.Header.Add(k, v)
			}
		}
	}
}

func Proxy(c *gin.Context, proxy *httputil.ReverseProxy, opts ...proxyOpts) {
	originDirector := proxy.Director
	director := func(req *http.Request) {
		for _, opt := range opts {
			opt(req)
		}

		originDirector(req)
	}
	proxy.Director = director
	proxy.ServeHTTP(c.Writer, c.Request)
}

func ProxySimply(c *gin.Context, scheme, host string, opts ...proxyOpts) {
	targetURL := &url.URL{
		Scheme: scheme,
		Host:   host,
	}
	proxy := httputil.NewSingleHostReverseProxy(targetURL)
	Proxy(c, proxy, opts...)
}

func DoSimply(cli *http.Client, req *http.Request, resp any) error {
	rawResp, err := cli.Do(req)
	if err != nil {
		return err
	}
	defer rawResp.Body.Close()

	body, err := io.ReadAll(rawResp.Body)
	if err != nil {
		return err
	}

	if rawResp.StatusCode/100 != 2 {
		return fmt.Errorf("(code: %d, body: %s)", rawResp.StatusCode, string(body))
	}

	if resp != nil {
		err = json.Unmarshal(body, resp)
		if err != nil {
			return err
		}
	}

	return nil

}
