package SunnyProxy

import (
	"Sunny/src/net/http"
	"bytes"
	"crypto/tls"
	"errors"
	"net"
	"net/url"
	"strings"
	"sync"
	"time"
)

type Winhttp struct {
	timesConnect time.Duration
	timesSend    time.Duration
	timesReceive time.Duration
	proxyip      string //代理IP
	method       string //提交方法
	url          string
	head         map[string]string
	head2        map[string]string
	head3        http.Header
	Redirect     func(req *http.Request, via []*http.Request) error
}

//SetRedirect 设置禁止重定向 请在Send之前调用
func (qt *Winhttp) SetRedirect(Redirect bool) {
	if Redirect {
		qt.Redirect = RedirectFunc
	} else {
		qt.Redirect = nil
	}

}

// SetProxyIP 设置或取消代理IP,IP为空则取消代理，user，pass 如果没有则填空文本
//
//请在Send之前调用
func (qt *Winhttp) SetProxyIP(ip string) {
	qt.proxyip = ip
}

// SetProxyBasicAuth 设置代理IP 账号密码 账号密码为空则取消 use:pass
//
//请在Send之前调用
func (qt *Winhttp) SetProxyBasicAuth(Auth string) {
	if Auth == "" {
		delete(qt.head2, "Proxy-Authorization")
		delete(qt.head3, "Proxy-Authorization")
		return
	}
	qt.head3.Add("Proxy-Authorization", "Basic "+Auth)
	qt.head2["Proxy-Authorization"] = "Basic " + Auth
}

// SetHeader 设置要提交的协议头 HeaderValue 若为空 则 表示删除 HeaderValue 协议头。
//
//请在Send之前调用
//
//Send之后本次设置的Head将会被清空
func (qt *Winhttp) SetHeader(Name, Val string) {

	if Name == "" {
		return
	} else if Val == "" || strings.ToLower(Name) == "content-length" {
		delete(qt.head, Name)
		return
	}
	qt.head[Name] = Val
}

// SetTimeouts 设置超时设置单位秒,不能小于1秒
//
// Connect 连接超时
//
// Send    发送超时
//
// Receive 相应超时
//
//请在Send之前调用
func (qt *Winhttp) SetTimeouts(Connect, Send, Receive time.Duration) {
	if Connect < 1*time.Second {
		qt.timesConnect = 1 * time.Second
	} else {
		qt.timesConnect = Connect
	}
	//=====================================
	if Send < 1*time.Second {
		qt.timesSend = 1 * time.Second
	} else {
		qt.timesSend = Send
	}
	//=====================================
	if Receive < 1*time.Second {
		qt.timesReceive = 1 * time.Second
	} else {
		qt.timesReceive = Receive
	}
}

// Open 设置要提交的类型（POST or GET ...） 以及URL
//
//请在Send之前调用
func (qt *Winhttp) Open(method, url string) {

	qt.method = strings.ToUpper(method)
	qt.url = strings.ReplaceAll(strings.ReplaceAll(url, "https://%20", ""), "http://%20", "")
}

// sendBin .
// 开始访问
func (qt *Winhttp) SendBin(bin []byte) (*http.Response, *net.Conn, error) {
	databin := bin
	u, _ := url.Parse(qt.url)
	var ClientCertificate *tls.Certificate
	if u != nil {
		s := LoadP12Certificate(u.Host)
		if s != nil {
			ClientCertificate = s.CryptoTLSCert
		}
	}
	KeepAlive := false
	req, _ := http.NewRequest(qt.method, qt.url, bytes.NewBuffer(databin))
	for k, v := range qt.head2 {
		if KeepAlive == false && strings.ToUpper(k) == "CONNECTION" && strings.ToUpper(v) == "KEEP-ALIVE" {
			KeepAlive = true
		}
		req.Header[k] = []string{v}
	}
	for k, v := range qt.head {
		if KeepAlive == false && strings.ToUpper(k) == "CONNECTION" && strings.ToUpper(v) == "KEEP-ALIVE" {
			KeepAlive = true
		}
		req.Header[k] = []string{v}
	}
	qt.head = make(map[string]string) //清空用户设置的协议头
	transport := &http.Transport{}
	transport.DisableCompression = true
	var cn *net.Conn
	Dial := func(netw, addr string) (net.Conn, error) {
		r := ResolveIPAddr(addr)
		if r == "" {
			return nil, ERRResolve
		}
		conn, err := net.DialTimeout(netw, r, qt.timesConnect) //设置建立连接超时
		cn = &conn
		if err != nil {
			return nil, err
		}
		if KeepAlive {
			conn.SetWriteDeadline(time.Now().Add(qt.timesSend))                  //设置发送接受数据超时
			conn.SetReadDeadline(time.Now().Add(time.Duration(999) * time.Hour)) //设置发送接受数据超时
		} else {
			conn.SetDeadline(time.Now().Add(qt.timesSend)) //设置发送接受数据超时
		}
		return conn, nil
	}
	if qt.proxyip == "" {
		transport = &http.Transport{
			Dial:                  Dial,
			TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
			ResponseHeaderTimeout: qt.timesReceive}

	} else {
		proxy := func(_ *http.Request) (*url.URL, error) {
			return url.Parse("http://" + qt.proxyip)
		}
		if qt.head3.Get("Proxy-Authorization") == "" {
			transport = &http.Transport{Proxy: proxy,
				Dial:                  Dial,
				ResponseHeaderTimeout: qt.timesReceive,
				TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
			}

		} else {
			transport = &http.Transport{Proxy: proxy,
				Dial:                  Dial,
				ProxyConnectHeader:    qt.head3,
				ResponseHeaderTimeout: qt.timesReceive,
				TLSClientConfig:       &tls.Config{InsecureSkipVerify: true},
			}
		}

	}

	if ClientCertificate != nil {
		transport.TLSClientConfig.Certificates = []tls.Certificate{*ClientCertificate}
	}

	transport.DisableCompression = false
	client := &http.Client{
		Transport:     transport,
		CheckRedirect: qt.Redirect}

	resp, err := client.Do(req) //提交请求
	return resp, cn, err

}

var ERRResolve = errors.New("Address resolution error")
var IPLock sync.Mutex
var IPMap = make(map[string]string)

func ResolveIPAddr(r string) string {
	u, _ := url.Parse("http://" + r)
	IPLock.Lock()
	defer IPLock.Unlock()
	host := u.Hostname()
	s := IPMap[host]
	if s != "" {
		return s
	}
	ip, e := net.ResolveIPAddr("ip", host)
	if e != nil {
		return ""
	}
	Port := u.Port()
	s = ip.IP.String() + ":" + Port
	IPMap[host] = s
	return s
}

// New .创建一个新的实例
func NewWinhttp() *Winhttp {
	tmp := new(Winhttp)
	tmp.timesConnect = 20 * time.Second //默认连接超时15秒
	tmp.timesSend = 20 * time.Second    //默认发送超时15秒
	tmp.timesReceive = 40 * time.Second //默认响应时间
	tmp.head = make(map[string]string)
	tmp.head2 = make(map[string]string)
	tmp.head3 = http.Header{}
	tmp.Redirect = RedirectFunc
	return tmp
}
func RedirectFunc(req *http.Request, via []*http.Request) error {
	return http.ErrUseLastResponse
}
