package client

import (
	"crypto/tls"
	"net/http"
	"sync"
	"time"

	"github.com/go-logr/logr"
	ctrl "sigs.k8s.io/controller-runtime"
)

// avoid recreating client too frequently.
const (
	recreateManagerClientInterval = time.Minute
	HttpManagerTimeout            = 180 * time.Second
)

// wraps http.Client and auto retry after timeout.
type ManagerServerClient struct {
	logger logr.Logger

	sync.RWMutex
	httpClient   *http.Client
	lastRecreate time.Time
}

func NewManagerServerClient() *ManagerServerClient {
	c := &ManagerServerClient{
		logger: ctrl.Log.WithName("ManagerServerClient"),
	}
	c.recreate()
	return c
}

func (c *ManagerServerClient) recreate() {
	c.Lock()
	defer c.Unlock()
	if time.Since(c.lastRecreate) < recreateManagerClientInterval {
		return
	}
	c.logger.Info("recreate http client")
	c.httpClient = &http.Client{
		Timeout: HttpManagerTimeout,
		//nolint:mnd,gosec // ignore
		Transport: &http.Transport{
			IdleConnTimeout:     60 * time.Second,
			TLSHandshakeTimeout: 5 * time.Second,
			TLSClientConfig:     &tls.Config{InsecureSkipVerify: true},
			ForceAttemptHTTP2:   true,
		},
	}
	c.lastRecreate = time.Now()
}

func (c *ManagerServerClient) getInnerClient() *http.Client {
	c.RLock()
	defer c.RUnlock()
	return c.httpClient
}

func (c *ManagerServerClient) Do(req *http.Request) (*http.Response, error) {
	res, err := c.getInnerClient().Do(req)
	if err != nil {
		c.logger.Error(err, "query manager server failed")
		c.recreate()
	}
	return res, err
}
