package tls

import (
	"bytes"
	"compress/flate"
	"compress/gzip"
	"crypto/rand"
	"encoding/binary"
	"errors"
	"github.com/andybalholm/brotli"
	"github.com/bogdanfinn/tls-client/profiles"
	"github.com/sirupsen/logrus"
	"github.com/wmm1996528/cronet-go"
	"io/ioutil"
	"math/big"
	"net/http"
	"net/http/cookiejar"
	"net/url"
	"strings"
)

type Client struct {
	Client     *http.Client
	Jar        *cookiejar.Jar
	Item       ForwardItem
	Response   *http.Response
	uri        *url.URL
	TlsVersion string
}

func (c *Client) Start() error {
	var req *http.Request
	var err error
	uri, err := url.Parse(c.Item.Url)
	c.uri = uri
	cookie := c.Item.Headers["cookie"]
	delete(c.Item.Headers, "cookie")

	if cookie == "" {
		cookie = c.Item.Headers["Cookie"]
		delete(c.Item.Headers, "Cookie")

	}
	if c.Item.Debug {
		logrus.Infof("body: %s\ncookie %s", c.Item.Data, cookie)
	}

	if c.Item.Debug == false && cookie == "" {
		return errors.New("未设置cookie")
	}
	var cookies []*http.Cookie
	cks := strings.Split(cookie, "; ")
	for _, ck := range cks {
		t := strings.Split(strings.TrimSpace(ck), "=")
		cookies = append(cookies, &http.Cookie{
			Name:  t[0],
			Value: strings.ReplaceAll(strings.Join(t[1:], "="), ";", ""),
		})
	}
	c.Jar.SetCookies(uri, cookies)
	if err != nil {
		return err
	}
	if strings.ToUpper(c.Item.Method) == "GET" {
		req, err = http.NewRequest(http.MethodGet, c.Item.Url, nil)
	} else {
		req, err = http.NewRequest(http.MethodPost, c.Item.Url, strings.NewReader(c.Item.Data))
	}
	if err != nil {
		logrus.Error("url初始化失败 ", err.Error())
		return err
	}
	for k, v := range c.Item.Headers {
		req.Header.Set(k, v)
	}
	//c.Client.SetProxy("http://user-unifflcc-region-us:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600")
	resp, err := c.Client.Do(req)
	if err != nil {
		logrus.Error("请求失败 ", err.Error())
		return err
	}
	c.Response = resp
	return nil
}

func (c *Client) GetResponseHeaders() map[string]string {
	res := make(map[string]string)
	for k, v := range c.Response.Header {
		res[k] = v[0]
	}
	return res
}

func (c *Client) GetSessionCookies() map[string]string {
	res := make(map[string]string)
	cks := c.Jar.Cookies(c.uri)
	for _, ck := range cks {
		res[ck.Name] = ck.Value
	}
	return res
}

func (c *Client) GetText() string {

	//http.DecompressBody(c.Response)
	//rb, _ := io.ReadAll(c.Response.Body)
	content, _ := ioutil.ReadAll(c.Response.Body)
	encoding := c.Response.Header.Get("Content-Encoding")
	DecompressBody(&content, encoding)
	return string(content)
}

func (c *Client) GetRespUrl() string {
	location, err := c.Response.Location()
	if err != nil {
		return c.uri.String()
	}

	return location.RequestURI()
}

func (c *Client) GetStatusCode() int {
	return c.Response.StatusCode

}

var chromes = []profiles.ClientProfile{
	//profiles.Chrome_110,
	//profiles.Chrome_107,
	//profiles.Chrome_108,
	profiles.Chrome_117,
	//profiles.Firefox_120,
	//profiles.Safari_15_6_1,
	//profiles.Safari_16_0,
	//tls_client.Safari_Ipad_15_6,
}

func RandRandom(a int64) int64 {
	res, _ := rand.Int(rand.Reader, big.NewInt(a))
	return res.Int64()
}
func NewClient(forWard ForwardItem, ios bool) (Client, error) {
	engineParams := cronet.NewEngineParams()
	//engineParams.SetProxyServer("http://127.0.0.1:8081")
	//engineParams.SetProxyServer("http://user-uni003-region-de-sessid-1125-sesstime-5-keep-true:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600")
	//engineParams.SetProxyServer("http://user-uni003-region-de-sessid-1231-sesstime-5-keep-true:q39CEBTs5A5YQXor@pr.roxlabs.cn:4600")
	engineParams.SetProxyServer(forWard.Proxy)

	engineParams.SetEnableHTTP2(true)
	engineParams.SetEnableQuic(false)
	engineParams.SetEnableBrotli(true)
	engineParams.SetEnableCheckResult(false)
	t := cronet.NewCronetTransport(engineParams, true)
	//t.Engine.StartNetLogToFile("./1.log", true)
	//ConfigureClientCertificate(&t.Engine, certPath, keyPath, []string{urlArg.Host, proxyArg.Host})
	jar, _ := cookiejar.New(nil)

	client := &http.Client{
		Transport: t,
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return http.ErrUseLastResponse
		},
		Jar: jar,
	}

	return Client{Client: client, Item: forWard, Jar: jar}, nil
}

// 解码Body数据
func DecompressBody(content *[]byte, encoding string) {
	if encoding != "" {
		if strings.ToLower(encoding) == "gzip" {
			decodeGZip(content)
		} else if strings.ToLower(encoding) == "deflate" {
			decodeDeflate(content)
		} else if strings.ToLower(encoding) == "br" {
			decodeBrotli(content)
		}
	}
}

// 解码GZip编码
func decodeGZip(content *[]byte) error {
	if content == nil {
		return nil
	}
	b := new(bytes.Buffer)
	binary.Write(b, binary.LittleEndian, content)
	r, err := gzip.NewReader(b)
	if err != nil {
		return err
	}
	defer r.Close()
	*content, err = ioutil.ReadAll(r)
	if err != nil {
		return err
	}
	return nil
}

// 解码deflate编码
func decodeDeflate(content *[]byte) error {
	var err error
	if content == nil {
		return err
	}
	r := flate.NewReader(bytes.NewReader(*content))
	defer r.Close()
	*content, err = ioutil.ReadAll(r)
	if err != nil {
		return err
	}
	return nil
}

// 解码br编码
func decodeBrotli(content *[]byte) error {
	var err error
	if content == nil {
		return err
	}
	r := brotli.NewReader(bytes.NewReader(*content))
	*content, err = ioutil.ReadAll(r)
	if err != nil {
		return err
	}
	return nil
}
