package gorequest

import (
	"bytes"
	"context"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"go.dtapp.net/gostring"
	"go.dtapp.net/gotime"
	"go.dtapp.net/gotrace_id"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"runtime"
	"strings"
	"time"
)

// Response 返回内容
type Response struct {
	RequestUri            string      //【请求】链接
	RequestParams         Params      //【请求】参数
	RequestMethod         string      //【请求】方式
	RequestHeader         Headers     //【请求】头部
	RequestTime           time.Time   //【请求】时间
	ResponseHeader        http.Header //【返回】头部
	ResponseStatus        string      //【返回】状态
	ResponseStatusCode    int         //【返回】状态码
	ResponseBody          []byte      //【返回】内容
	ResponseContentLength int64       //【返回】大小
	ResponseTime          time.Time   //【返回】时间
}

// App 实例
type App struct {
	Uri             string           // 全局请求地址，没有设置url才会使用
	Error           error            // 错误
	httpUri         string           // 请求地址
	httpMethod      string           // 请求方法
	httpHeader      Headers          // 请求头
	httpParams      Params           // 请求参数
	responseContent Response         // 返回内容
	httpContentType string           // 请求内容类型
	debug           bool             // 是否开启调试模式
	p12Cert         *tls.Certificate // p12证书内容
}

// NewHttp 实例化
func NewHttp() *App {
	return &App{
		httpHeader: NewHeaders(),
		httpParams: NewParams(),
	}
}

// SetDebug 设置调试模式
func (app *App) SetDebug() {
	app.debug = true
}

// SetUri 设置请求地址
func (app *App) SetUri(uri string) {
	app.httpUri = uri
}

// SetMethod 设置请求方式
func (app *App) SetMethod(method string) {
	app.httpMethod = method
}

// SetHeader 设置请求头
func (app *App) SetHeader(key, value string) {
	if key == "" {
		panic("url is empty")
	}
	app.httpHeader.Set(key, value)
}

// SetHeaders 批量设置请求头
func (app *App) SetHeaders(headers Headers) {
	for key, value := range headers {
		app.httpHeader.Set(key, value)
	}
}

// SetAuthToken 设置身份验证令牌
func (app *App) SetAuthToken(token string) {
	app.httpHeader.Set("Authorization", fmt.Sprintf("Bearer %s", token))
}

// SetUserAgent 设置用户代理，空字符串就随机设置
func (app *App) SetUserAgent(ua string) {
	if ua == "" {
		ua = GetRandomUserAgent()
	}
	app.httpHeader.Set("User-Agent", ua)
}

// SetContentTypeJson 设置JSON格式
func (app *App) SetContentTypeJson() {
	app.httpContentType = httpParamsModeJson
}

// SetContentTypeForm 设置FORM格式
func (app *App) SetContentTypeForm() {
	app.httpContentType = httpParamsModeForm
}

// SetContentTypeXml 设置XML格式
func (app *App) SetContentTypeXml() {
	app.httpContentType = httpParamsModeXml
}

// SetParam 设置请求参数
func (app *App) SetParam(key string, value interface{}) {
	if key == "" {
		panic("url is empty")
	}
	app.httpParams.Set(key, value)
}

// SetParams 批量设置请求参数
func (app *App) SetParams(params Params) {
	for key, value := range params {
		app.httpParams.Set(key, value)
	}
}

// SetP12Cert 设置证书
func (app *App) SetP12Cert(content *tls.Certificate) {
	app.p12Cert = content
}

// Get 发起GET请求
func (app *App) Get(ctx context.Context, uri ...string) (httpResponse Response, err error) {
	if len(uri) == 1 {
		app.Uri = uri[0]
	}
	// 设置请求方法
	app.httpMethod = http.MethodGet
	return request(app, ctx)
}

// Post 发起POST请求
func (app *App) Post(ctx context.Context, uri ...string) (httpResponse Response, err error) {
	if len(uri) == 1 {
		app.Uri = uri[0]
	}
	// 设置请求方法
	app.httpMethod = http.MethodPost
	return request(app, ctx)
}

// Request 发起请求
func (app *App) Request(ctx context.Context) (httpResponse Response, err error) {
	return request(app, ctx)
}

// 请求接口
func request(app *App, ctx context.Context) (httpResponse Response, err error) {

	// 赋值
	httpResponse.RequestTime = gotime.Current().Time
	httpResponse.RequestUri = app.httpUri
	httpResponse.RequestMethod = app.httpMethod
	httpResponse.RequestParams = app.httpParams.DeepCopy()
	httpResponse.RequestHeader = app.httpHeader.DeepCopy()

	// 判断网址
	if httpResponse.RequestUri == "" {
		httpResponse.RequestUri = app.Uri
	}
	if httpResponse.RequestUri == "" {
		app.Error = errors.New("没有设置Uri")
		return httpResponse, app.Error
	}

	// 创建 http 客户端
	client := &http.Client{}

	if app.p12Cert != nil {
		transport := &http.Transport{
			TLSClientConfig: &tls.Config{
				Certificates: []tls.Certificate{*app.p12Cert},
			},
			DisableCompression: true,
		}
		client = &http.Client{
			Transport: transport,
		}
	}

	// SDK版本
	httpResponse.RequestHeader.Set("Sdk-User-Agent", fmt.Sprintf(userAgentFormat, runtime.GOOS, runtime.Version(), Version))

	// 请求类型
	switch app.httpContentType {
	case httpParamsModeJson:
		httpResponse.RequestHeader.Set("Content-Type", "application/json")
	case httpParamsModeForm:
		httpResponse.RequestHeader.Set("Content-Type", "application/x-www-form-urlencoded")
	case httpParamsModeXml:
		httpResponse.RequestHeader.Set("Content-Type", "text/xml")
	}

	// 跟踪编号
	traceId := gotrace_id.GetTraceIdContext(ctx)
	if traceId == "" {
		traceId = gostring.GetUuId()
	}
	httpResponse.RequestHeader.Set("X-Request-Id", traceId)

	// 请求内容
	var reqBody io.Reader

	if httpResponse.RequestMethod == http.MethodPost && app.httpContentType == httpParamsModeJson {
		jsonStr, err := json.Marshal(httpResponse.RequestParams)
		if err != nil {
			app.Error = errors.New(fmt.Sprintf("解析出错 %s", err))
			return httpResponse, app.Error
		}
		// 赋值
		reqBody = bytes.NewBuffer(jsonStr)
	}

	if httpResponse.RequestMethod == http.MethodPost && app.httpContentType == httpParamsModeForm {
		// 携带 form 参数
		form := url.Values{}
		if len(httpResponse.RequestParams) > 0 {
			for k, v := range httpResponse.RequestParams {
				form.Add(k, GetParamsString(v))
			}
		}
		// 赋值
		reqBody = strings.NewReader(form.Encode())
	}

	if app.httpContentType == httpParamsModeXml {
		reqBody, err = httpResponse.RequestParams.MarshalXML()
		if err != nil {
			app.Error = errors.New(fmt.Sprintf("解析XML出错 %s", err))
			return httpResponse, app.Error
		}
	}

	// 创建请求
	req, err := http.NewRequest(httpResponse.RequestMethod, httpResponse.RequestUri, reqBody)
	if err != nil {
		app.Error = errors.New(fmt.Sprintf("创建请求出错 %s", err))
		return httpResponse, app.Error
	}

	// GET 请求携带查询参数
	if httpResponse.RequestMethod == http.MethodGet {
		if len(httpResponse.RequestParams) > 0 {
			q := req.URL.Query()
			for k, v := range httpResponse.RequestParams {
				q.Add(k, GetParamsString(v))
			}
			req.URL.RawQuery = q.Encode()
		}
	}

	// 设置请求头
	if len(httpResponse.RequestHeader) > 0 {
		for key, value := range httpResponse.RequestHeader {
			req.Header.Set(key, value)
		}
	}

	// 发送请求
	resp, err := client.Do(req)
	if err != nil {
		app.Error = errors.New(fmt.Sprintf("请求出错 %s", err))
		return httpResponse, app.Error
	}

	// 最后关闭连接
	defer resp.Body.Close()

	// 读取内容
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		app.Error = errors.New(fmt.Sprintf("解析内容出错 %s", err))
		return httpResponse, app.Error
	}

	// 赋值
	httpResponse.ResponseTime = gotime.Current().Time
	httpResponse.ResponseStatus = resp.Status
	httpResponse.ResponseStatusCode = resp.StatusCode
	httpResponse.ResponseHeader = resp.Header
	httpResponse.ResponseBody = body
	httpResponse.ResponseContentLength = resp.ContentLength

	if app.debug == true {
		fmt.Printf("gorequest：%+v\n", httpResponse)
		fmt.Printf("gorequest.body：%s\n", httpResponse.ResponseBody)
	}

	return httpResponse, err
}
