package l_request

import (
	"crypto/tls"
	"encoding/json"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"

	"github.com/go-kratos/kratos/v2/log"
)

// 请求结构体
type Request struct {
	Method   string                 `json:"method"`   // 请求方法
	Url      string                 `json:"url"`      // 请求url
	Params   map[string]string      `json:"params"`   // Query参数
	Headers  map[string]string      `json:"headers"`  // 请求头
	Cookies  map[string]string      `json:"cookies"`  // todo 处理 Cookies
	Data     map[string]string      `json:"data"`     // 表单格式请求数据
	Json     map[string]interface{} `json:"json"`     // JSON格式请求数据 todo 多层 嵌套
	Files    map[string]string      `json:"files"`    // todo 处理 Files
	Raw      string                 `json:"raw"`      // 原始请求数据
	JsonByte []byte                 `json:"json_raw"` // JSON格式请求数据 todo 多层 嵌套
	Xml      []byte                 `json:"xml"`      // xml
}

// 响应结构体
type Response struct {
	StatusCode int               `json:"status_code"` // 状态码
	Reason     string            `json:"reason"`      // 状态码说明
	Elapsed    float64           `json:"elapsed"`     // 请求耗时(秒)
	Content    []byte            `json:"content"`     // 响应二进制内容
	Text       string            `json:"text"`        // 响应文本
	Headers    map[string]string `json:"headers"`     // 响应头
	Cookies    map[string]string `json:"cookies"`     // todo 添加响应Cookies
	Request    *Request          `json:"request"`     // 原始请求
}

// 处理请求方法
func (r *Request) getMethod() string {
	return strings.ToUpper(r.Method) // 必须转为全部大写
}

// 组装URL
func (r *Request) getUrl() string {
	if r.Params != nil {
		urlValues := url.Values{}
		Url, _ := url.Parse(r.Url) // todo 处理err
		for key, value := range r.Params {
			urlValues.Set(key, value)
		}
		Url.RawQuery = urlValues.Encode()
		return Url.String()
	}
	return r.Url
}

// 组装请求数据
func (r *Request) getData() io.Reader {
	var reqBody string
	if r.Headers == nil {
		r.Headers = make(map[string]string, 1)
	}
	if r.Raw != "" {
		reqBody = r.Raw
	} else if r.Data != nil {
		urlValues := url.Values{}
		for key, value := range r.Data {
			urlValues.Add(key, value)
		}
		reqBody = urlValues.Encode()
		if _, ex := r.Headers["Content-Type"]; !ex {
			r.Headers["Content-Type"] = "application/x-www-form-urlencoded"
		}

	} else if r.Json != nil {
		bytesData, _ := json.Marshal(r.Json)
		reqBody = string(bytesData)
		if _, ex := r.Headers["Content-Type"]; !ex {
			r.Headers["Content-Type"] = "application/json"
		}
	} else if r.JsonByte != nil {
		reqBody = string(r.JsonByte)
		if _, ex := r.Headers["Content-Type"]; !ex {
			r.Headers["Content-Type"] = "application/json"
		}
	} else if r.Xml != nil {
		reqBody = string(r.Xml)
		if _, ex := r.Headers["Content-Type"]; !ex {
			r.Headers["Content-Type"] = "application/xml"
		}
	}
	return strings.NewReader(reqBody)
}

// 添加请求头-需要在getData后使用
func (r *Request) addHeaders(req *http.Request) {
	if r.Headers != nil {
		for key, value := range r.Headers {
			req.Header.Add(key, value)
		}
	}
}

// 准备请求
func (r *Request) prepare() *http.Request {
	Method := r.getMethod()
	Url := r.getUrl()
	Data := r.getData()
	req, err := http.NewRequest(Method, Url, Data)
	if err != nil {
		log.Error(err)
		return nil
	}
	r.addHeaders(req)
	return req
}

// 组装响应对象
func (r *Request) packResponse(res *http.Response, elapsed float64) Response {
	var resp Response
	resBody, _ := io.ReadAll(res.Body)
	resp.Content = resBody
	resp.Text = string(resBody)
	resp.StatusCode = res.StatusCode
	resp.Reason = strings.Split(res.Status, " ")[1]
	resp.Elapsed = elapsed
	resp.Headers = map[string]string{}
	for key, value := range res.Header {
		resp.Headers[key] = strings.Join(value, ";")
	}
	return resp
}

// 发送请求
func (r *Request) Send() (Response, error) {
	req := r.prepare()
	client := &http.Client{}
	start := time.Now()
	res, err := client.Do(req)
	if err != nil {
		return Response{}, err
	}
	defer res.Body.Close()
	elapsed := time.Since(start).Seconds()
	return r.packResponse(res, elapsed), nil
}

// 跳过证书发送请求
func (r *Request) SendWithoutSsl() (Response, error) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	req := r.prepare()
	client := &http.Client{Transport: tr}
	start := time.Now()
	res, err := client.Do(req)
	if err != nil {
		return Response{}, err
	}
	defer res.Body.Close()
	elapsed := time.Since(start).Seconds()
	return r.packResponse(res, elapsed), nil
}

// 发送请求
func (r *Request) SendNoParseResponse() (*http.Response, error) {
	req := r.prepare()
	client := &http.Client{}
	res, err := client.Do(req)
	return res, err
}
