package toolkit

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

	"dev.mediocregopher.com/mediocre-go-lib.git/mlog"
	"github.com/tv42/httpunix"
)

// HTTPClient is an interface around the default http.Client type.
type HTTPClient interface {
	Do(*http.Request) (*http.Response, error)

	// Close cleans up any resources being held by the HTTPClient.
	Close() error
}

type httpClient struct {
	logger *mlog.Logger
	*http.Client
}

// NewHTTPClient returns a new HTTPClient using a clone of
// [http.DefaultTransport].
func NewHTTPClient(logger *mlog.Logger) HTTPClient {
	return &httpClient{
		logger,
		&http.Client{
			Transport: http.DefaultTransport.(*http.Transport).Clone(),
		},
	}
}

// NewUnixHTTPClient returns an HTTPClient which will use the given
// unixSocketPath to serve requests. The base URL (scheme and host) which should
// be used for requests is returned as well.
func NewUnixHTTPClient(
	logger *mlog.Logger, unixSocketPath string,
) (
	HTTPClient, *url.URL,
) {
	const host = "uds"

	u := &url.URL{
		Scheme: httpunix.Scheme,
		Host:   host,
	}

	transport := new(httpunix.Transport)
	transport.RegisterLocation(host, unixSocketPath)

	return &httpClient{logger, &http.Client{Transport: transport}}, u
}

func (c *httpClient) Do(req *http.Request) (*http.Response, error) {
	if c.logger.MaxLevel() < mlog.LevelDebug.Int() {
		return c.Client.Do(req)
	}

	var (
		ctx        = req.Context()
		origScheme = req.URL.Scheme
	)

	// httputil.DumpRequestOut doesn't like the httpunix.Scheme, so we
	// temporarily switch it.
	if req.URL.Scheme == httpunix.Scheme {
		req.URL.Scheme = "http"
	}

	reqB, err := httputil.DumpRequestOut(req, true)
	if err != nil {
		c.logger.Error(ctx, "failed to dump http request", err)
	} else {
		c.logger.Debug(ctx, "------ request ------\n"+string(reqB)+"\n")
	}

	req.URL.Scheme = origScheme

	res, err := c.Client.Do(req)

	if res != nil {
		resB, err := httputil.DumpResponse(res, true)
		if err != nil {
			c.logger.Error(ctx, "failed to dump http response", err)
		} else {
			c.logger.Debug(ctx, "------ response ------\n"+string(resB)+"\n")
		}
	}

	return res, err
}

func (c *httpClient) Close() error {
	c.CloseIdleConnections()
	return nil
}