/*

 Copyright (c) [2023] [pcm]
 [pcm-coordinator] is licensed under Mulan PSL v2.
 You can use this software according to the terms and conditions of the Mulan PSL v2.
 You may obtain a copy of Mulan PSL v2 at:
          http://license.coscl.org.cn/MulanPSL2
 THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 EITHER EXPaRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 See the Mulan PSL v2 for more details.

*/

package httputils

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"github.com/go-resty/resty/v2"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"reflect"
	"strconv"
	"time"
)

const (
	GET    = "GET"
	PUT    = "PUT"
	POST   = "POST"
	DELETE = "DELETE"
)
const (
	ContentType               = "Content-Type"
	ApplicationJson           = "application/json"
	ApplicationFromUrlencoded = "application/x-www-from-urlencoded"
)

var httpClient *resty.Client = nil
var httpsClient *resty.Client = nil

func NewHttpsClient() *resty.Client {
	if httpsClient != nil {
		return httpsClient
	}

	c := resty.New()
	c.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
	c.SetTimeout(5 * time.Second)
	c.SetRetryCount(3)

	debug := "true"
	if len(debug) > 0 && debug == "ON" {
		c.SetDebug(true)
	}

	httpsClient = c
	return c
}

func HttpClient(method string, url string, payload io.Reader, token string) ([]byte, error) {
	request, err := http.NewRequest(method, url, payload)
	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("User-Agent", "API Explorer")
	request.Header.Add("x-auth-token", token)
	client := &http.Client{}
	res, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	body, err := io.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	return body, err
}

func HttpGetWithResult(params map[string]string, url string, result interface{}) error {
	client := NewHttpsClient()

	req := client.R()
	// 添加查询参数
	for k, v := range params {
		req.SetQueryParam(k, v)
	}

	_, err := req.SetResult(result).Get(url)
	return err
}

// 发送POST请求
// url:请求地址，data:POST请求提交的数据,contentType:请求体格式，如：application/json
// content:请求放回的内容
func HttpPost(url string, data interface{}) (content string, err error) {
	jsonStr, _ := json.Marshal(data)
	req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
	req.Header.Add("content-type", "application/json")
	if err != nil {
		return
	}
	defer req.Body.Close()

	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Do(req)
	if err != nil {
		return
	}
	defer resp.Body.Close()

	result, _ := ioutil.ReadAll(resp.Body)
	content = string(result)
	return
}

func HttpClientStatusCode(method string, url string, payload io.Reader, token string) (int, error) {
	request, err := http.NewRequest(method, url, payload)
	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("User-Agent", "API Explorer")
	request.Header.Add("x-auth-token", token)

	client := &http.Client{}
	res, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}

	return res.StatusCode, err
}

func HttpClientWithQueries[T any](method string, url string, payload io.Reader, token string, param T) ([]byte, error) {
	request, err := http.NewRequest(method, url, payload)
	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("User-Agent", "API Explorer")
	request.Header.Add("x-auth-token", token)

	convertStructToQueryUrl(request, param)

	client := &http.Client{}
	res, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	body, err := io.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}

	return body, err
}

func convertStructToQueryUrl[T any](request *http.Request, param T) {

	query := request.URL.Query()
	values := reflect.ValueOf(param)

	types := values.Type()
	for i := 0; i < values.NumField(); i++ {
		if !values.Field(i).IsZero() {
			if values.Field(i).CanInt() {
				query.Add(types.Field(i).Name, strconv.FormatInt(values.Field(i).Int(), 10))
			} else if values.Field(i).Kind() == reflect.Bool {
				query.Add(types.Field(i).Name, strconv.FormatBool(values.Field(i).Bool()))
			} else {
				query.Add(types.Field(i).Name, values.Field(i).String())
			}
		}
	}

	request.URL.RawQuery = query.Encode()
}

func HttpClientWithBodyAndCode(method string, url string, payload io.Reader, token string) (int, []byte, error) {
	request, err := http.NewRequest(method, url, payload)
	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("User-Agent", "API Explorer")
	request.Header.Add("x-auth-token", token)

	client := &http.Client{}
	res, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	body, err := io.ReadAll(res.Body)

	return res.StatusCode, body, err
}

func HttpClientWithScreen(method string, url string, payload io.Reader) (int, []byte, error) {
	request, err := http.NewRequest(method, url, payload)

	client := &http.Client{
		Transport: &http.Transport{
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}
	res, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()
	body, err := io.ReadAll(res.Body)

	return res.StatusCode, body, err
}
