package k8s

import (
	"context"
	"crypto/tls"
	"fmt"
	"io"
	"net/http"
	"strings"
	"time"

	"github.com/pingcap/errors"
	"github.com/pingcap/log"
)

const (
	MaxConnsPerHost     = 1024
	MaxIdleConnsPerHost = 128
	IdleConnTimeout     = 30 * time.Second
)

// http request with same endpoint
type HttpLink interface {
	Get(string) ([]byte, error)
	Post(string, interface{}) ([]byte, error)
}

type RequestDecorator func(req *http.Request) error

// in one network
type HttpLinkNormal struct {
	ctx     context.Context
	baseURL string
	cli     *http.Client
}

// http client for normal link
func NewHTTPLinkNormal(ctx context.Context, scheme, ns, name, port string) HttpLink {

	return NewRawHTTPLinkNormal(ctx, fmt.Sprintf("%s://%s.%s:%s", scheme, name, ns, port))
}

func NewRawHTTPLinkNormal(ctx context.Context, baseURL string) *HttpLinkNormal {
	tr := &http.Transport{
		MaxConnsPerHost:     MaxConnsPerHost,
		MaxIdleConnsPerHost: MaxIdleConnsPerHost,
		IdleConnTimeout:     IdleConnTimeout,
		/* #nosec */
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	cli := &http.Client{Transport: tr}

	link := &HttpLinkNormal{
		ctx:     ctx,
		baseURL: baseURL,
		cli:     cli,
	}

	return link
}

func (g *HttpLinkNormal) Get(apiPath string) ([]byte, error) {

	taskURL, err := URLJoint(g.baseURL, apiPath)
	if err != nil {
		return nil, err
	}

	body, _, err := getHTTPResponseBody(g.ctx, g.cli, http.MethodGet, taskURL, nil)
	if err != nil {
		return nil, err
	}
	return body, nil
}

func (g *HttpLinkNormal) Post(apiPath string, data interface{}) ([]byte, error) {
	taskURL, err := URLJoint(g.baseURL, apiPath)
	if err != nil {
		return nil, err
	}
	body, _, err := getHTTPResponseBody(g.ctx, g.cli, http.MethodPost, taskURL, strings.NewReader(fmt.Sprint(data)))
	if err != nil {
		return nil, err
	}

	return body, nil
}

func getHTTPResponseBody(ctx context.Context, httpClient *http.Client, method, urlStr string, body io.Reader, reqDecos ...RequestDecorator) ([]byte, int, error) {
	req, err := http.NewRequestWithContext(ctx, method, urlStr, body)
	if err != nil {
		log.S().Errorf("http error %s,%s,%+v", urlStr, body, err)
		return nil, 0, err
	}
	for _, deco := range reqDecos {
		if deco != nil {
			if err := deco(req); err != nil {
				return nil, 0, errors.Annotate(err, "cannot decorate http request")
			}
		}
	}
	resp, err := httpClient.Do(req)
	if err != nil {
		return nil, 0, err
	}
	defer resp.Body.Close()
	data, err := io.ReadAll(resp.Body)
	if err != nil {
		log.S().Errorf("http error %s,%s,%d,%s", urlStr, data, resp.StatusCode, err)
		return nil, resp.StatusCode, err
	}
	return data, resp.StatusCode, err
}
