package http

import (
	"bytes"
	"fmt"
	"git.sxidc.com/student-physical-examination/contract_lock_sdk/commons"
	"git.sxidc.com/student-physical-examination/contract_lock_sdk/config"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

func DoService(interfaceUrl string, httpParameter *HttpParameter, header *HttpHeader, proxyinfo *config.ProxyInfo) string {
	method := httpParameter.HttpMethod()
	switch method {
	case http.MethodPost:
		return Post(interfaceUrl, httpParameter, header, proxyinfo)
	case http.MethodGet:
		return Get(interfaceUrl, httpParameter, header, proxyinfo)
	default:
		return ""
	}
}

// post请求json格式
func DoServiceWithJson(interfaceUrl string, httpParameter *HttpParameter, header *HttpHeader, proxyinfo *config.ProxyInfo) string {
	client := SetProxy(proxyinfo)
	req, err := http.NewRequest(http.MethodPost, interfaceUrl, bytes.NewReader([]byte(httpParameter.jsonParamer)))
	// 设置header参数
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	SetHeaders(req, header)
	return GetResponseAsString(err, req, client)
}

// get请求
func Get(interfaceUrl string, httpParameter *HttpParameter, header *HttpHeader, proxyinfo *config.ProxyInfo) string {
	client := SetProxy(proxyinfo)
	if len(httpParameter.Params()) > 0 {
		params := encodeParams(httpParameter.Params())
		encodedURL := url.PathEscape(params)
		interfaceUrl += "?" + encodedURL
		print(interfaceUrl)

	}
	req, err := http.NewRequest(http.MethodGet, interfaceUrl, nil)
	SetHeaders(req, header)
	return GetResponseAsString(err, req, client)
}

// post请求，支持普通参数和文件类型参数
func Post(interfaceUrl string, httpParameter *HttpParameter, header *HttpHeader, proxyinfo *config.ProxyInfo) string {
	var req *http.Request
	client := SetProxy(proxyinfo)
	var err error
	if httpParameter.IsMultipart() {
		req, err = newFileUploadRequest(interfaceUrl, httpParameter, header)
	} else {
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		SetHeaders(req, header)
		params := encodeParams(httpParameter.Params())
		req, err = http.NewRequest(http.MethodPost, interfaceUrl, strings.NewReader(params))
	}
	return GetResponseAsString(err, req, client)
}

func DoDownloadWithJson(url string, httpParameter *HttpParameter, header *HttpHeader, file *os.File, proxyinfo *config.ProxyInfo) string {
	client := SetProxy(proxyinfo)
	// 构造请求对象
	req, err := http.NewRequest(http.MethodPost, url, bytes.NewReader([]byte(httpParameter.jsonParamer)))
	// 设置header参数
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	SetHeaders(req, header)
	return GetResponseAsOutputStream(err, req, file, client)
}

func DoDownload(url string, httpParameter *HttpParameter, header *HttpHeader, file *os.File, proxyinfo *config.ProxyInfo) string {
	client := SetProxy(proxyinfo)
	if len(httpParameter.Params()) > 0 {
		url += "?" + encodeParams(httpParameter.Params())
	}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	SetHeaders(req, header)
	return GetResponseAsOutputStream(err, req, file, client)
}

// 创建文件上传请求
func newFileUploadRequest(url string, httpParameter *HttpParameter, header *HttpHeader) (*http.Request, error) {
	body := &bytes.Buffer{}
	writer := multipart.NewWriter(body)
	// 普通参数
	if len(httpParameter.params) > 0 {
		for k, v := range httpParameter.Params() {
			if commons.IsNil(v) {
				continue
			}
			switch t := v.(type) {
			case string:
				if len(t) != 0 {
					writer.WriteField(k, t)
				}
			case *int, *int32, *int64:
				if f, ok := v.(*int64); ok {
					writer.WriteField(k, fmt.Sprintf("%d", *f))
				}
			case *float32, *float64:
				if f, ok := v.(*float64); ok {
					writer.WriteField(k, fmt.Sprintf("%f", *f))
				}
			case *bool:
				if f, ok := v.(*bool); ok {
					writer.WriteField(k, fmt.Sprintf("%t", *f))
				}
			default:
				writer.WriteField(k, fmt.Sprintf("%v", t))
			}
		}
	}
	// 文件参数
	if len(httpParameter.files) > 0 {
		for k, v := range httpParameter.Files() {
			file, err := os.Open(v.Name())
			if err != nil {
				log.Println(err)
			}
			defer file.Close()
			part, err := writer.CreateFormFile(k, filepath.Base(v.Name()))
			if err != nil {
				log.Println(err)
			}
			_, err = io.Copy(part, file)
			if err != nil {
				log.Println(err)
			}
		}
	}
	// 文件列表参数
	if len(httpParameter.listFiles) > 0 {
		for k, files := range httpParameter.ListFiles() {
			if len(files) > 0 {
				for _, v := range files {
					file, err := os.Open(v.Name())
					if err != nil {
						log.Println(err)
					}
					defer file.Close()
					part, err := writer.CreateFormFile(k, filepath.Base(v.Name()))
					if err != nil {
						log.Println(err)
					}
					_, err = io.Copy(part, file)
					if err != nil {
						log.Println(err)
					}
				}
			}
		}
	}
	err := writer.Close()
	if err != nil {
		return nil, err
	}
	req, err := http.NewRequest(http.MethodPost, url, body)
	req.Header.Set("Content-Type", writer.FormDataContentType())
	SetHeaders(req, header)
	return req, err
}

// 将返回结果解析为字符串
func GetResponseAsString(err error, req *http.Request, client *http.Client) string {
	if err != nil {
		log.Fatal("NewRequest failed:", err)
	}
	response, err := client.Do(req)
	if err != nil {
		log.Fatal("Do request failed:", err)
	}
	defer response.Body.Close()
	// 处理响应
	responseDataBytes, err := ioutil.ReadAll(response.Body)
	if err != nil {
		log.Fatal("ReadAll failed:", err)
	}
	return string(responseDataBytes)
}

// 将返回结果解析为流
func GetResponseAsOutputStream(err error, req *http.Request, file *os.File, client *http.Client) string {
	if err != nil {
		log.Fatal("NewRequest failed:", err)
	}
	response, err := client.Do(req)
	if err != nil {
		log.Fatal("Do request failed:", err)
	}
	defer response.Body.Close()
	responseDataBytes, err := ioutil.ReadAll(response.Body)
	//处理返回结果
	contentType := response.Header.Get("Content-Type")
	if len(contentType) > 0 {
		lowerStr := strings.ToLower(contentType)
		if strings.Contains(lowerStr, "application/json") {
			//返回的是字符串
			return string(responseDataBytes)
		} else {
			//返回的是流
			if err != nil {
				log.Fatal("ReadAll failed:", err)
			}
			_, err = file.Write(responseDataBytes)
			if err != nil {
				log.Fatal("WriteFile failed：", err)
				os.Exit(1)
			}
		}
	}
	return ""
}

// 编码请求参数
func encodeParams(params map[string]interface{}) string {
	var parts []string
	for k, v := range params {
		if commons.IsNil(v) {
			continue
		}
		switch t := v.(type) {
		case string:
			if len(t) != 0 {
				parts = append(parts, k+commons.EQUALSIGN+fmt.Sprintf("%v", t))
			}
		case *int, *int32, *int64:
			if f, ok := v.(*int64); ok {
				parts = append(parts, k+commons.EQUALSIGN+fmt.Sprintf("%d", *f))
			}
		case *float32, *float64:
			if f, ok := v.(*float64); ok {
				parts = append(parts, k+commons.EQUALSIGN+fmt.Sprintf("%f", *f))
			}
		case *bool:
			if f, ok := v.(*bool); ok {
				parts = append(parts, k+commons.EQUALSIGN+fmt.Sprintf("%t", *f))
			}
		default:
			parts = append(parts, k+commons.EQUALSIGN+fmt.Sprintf("%v", t))
		}
	}
	return strings.Join(parts, commons.CONNECTOR)
}

func SetHeaders(req *http.Request, header *HttpHeader) {
	req.Header.Set(commons.ACCESS_TOKEN, header.AccessToken)
	req.Header.Set(commons.TIMESTAMP, header.Timestamp)
	req.Header.Set(commons.SIGNATURE, header.Signature)
	req.Header.Set(commons.NONCE, header.Nonce)
	req.Header.Set(commons.SIGNATURE_VERSION, header.SignatureVersion)
	req.Header.Set(commons.IDEMPOTENTTOKEN, header.IdempotentToken)
	req.Header.Set(commons.TRACEID, header.TraceId)
	req.Header.Set(commons.BODY, "")
	req.Header.Set("Connection", "close")
	if header.CustomHeaders != nil && len(header.CustomHeaders) != 0 {
		for k, v := range header.CustomHeaders {
			req.Header.Set(k, v)
		}
	}
}

func SetProxy(proxyinfo *config.ProxyInfo) *http.Client {
	if proxyinfo != nil {
		if len(proxyinfo.ProxyHost) > 0 && len(proxyinfo.ProxyPort) > 0 {
			// 创建一个代理 URL
			proxyStr := "http://" + proxyinfo.ProxyHost + ":" + proxyinfo.ProxyPort
			proxyURL, err := url.Parse(proxyStr)
			if err != nil {
				fmt.Println("无效的代理 URL：", err)
				return &http.Client{}
			}
			// 设置代理的用户名和密码
			if len(proxyinfo.ProxyUsername) > 0 && len(proxyinfo.ProxyPassword) > 0 {
				proxyURL.User = url.UserPassword(proxyinfo.ProxyUsername, proxyinfo.ProxyPassword)
			}
			// 创建一个自定义的 Transport
			transport := &http.Transport{
				Proxy: http.ProxyURL(proxyURL),
			}
			// 创建自定义的 Client
			client := &http.Client{
				Transport: transport,
			}
			return client
		}
	}
	return &http.Client{}
}
