package jd

import (
	"bytes"
	"crypto/md5"
	"crypto/tls"
	"encoding/hex"
	"encoding/json"
	"errors"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/bitly/go-simplejson"
	"github.com/nilorg/sdk/convert"
)

var (
	/**公用方法**/
	// Session 用户登录授权成功后,TOP颁发给应用的授权信息。当此API的标签上注明:“需要授权”,则此参数必传;“不需要授权”,则此参数不需要传;“可选授权”,则此参数为可选
	Session string
	// Timeout ...
	Timeout time.Duration
	// CacheExpiration 缓存过期时间
	CacheExpiration = time.Hour

	/**淘宝平台信息**/
	TaobaoAppKey    string
	TaobaoAppSecret string
	TaobaoRouter    string
	TaobaoVersion   = "2.0"

	/**京东平台信息**/
	JDAppKey    string
	JDAppSecret string
	JDRouter    string
	JDVersion   = "2.0"

	/**拼多多平台信息**/
	PDDAppKey    string
	PDDAppSecret string
	PDDRouter    string
	PDDVersion   = "v1.0"

	/**考拉海购平台信息**/
	KaolaAppKey    string
	KaolaAppSecret string
	KaolaRouter    string
	KaolaVersion   = "1.0"
)

// Arg 参数
type Arg map[string]interface{}

// copyArg 复制参数
func copyArg(srcArgs Arg) Arg {
	newArgs := make(Arg)
	for key, value := range srcArgs {
		newArgs[key] = value
	}
	return newArgs
}

// newCacheKey 创建缓存Key
func newCacheKey(p Arg) string {
	cpArgs := copyArg(p)
	delete(cpArgs, "session")
	delete(cpArgs, "timestamp")
	delete(cpArgs, "sign")

	keys := []string{}
	for k := range cpArgs {
		keys = append(keys, k)
	}
	// 排序asc
	sort.Strings(keys)
	// 把所有参数名和参数值串在一起
	cacheKeyBuf := new(bytes.Buffer)
	for _, k := range keys {
		cacheKeyBuf.WriteString(k)
		cacheKeyBuf.WriteString("=")
		cacheKeyBuf.WriteString(interfaceToString(cpArgs[k]))
	}
	h := md5.New()
	io.Copy(h, cacheKeyBuf)
	return hex.EncodeToString(h.Sum(nil))
}

// execute 执行API接口
func execute(p Arg, router string) (bytes []byte, err error) {
	err = checkConfig()
	if err != nil {
		return
	}

	var req *http.Request
	req, err = http.NewRequest("POST", router, strings.NewReader(p.getReqData()))
	if err != nil {
		return
	}
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8")
	httpClient := &http.Client{Transport: tr}
	httpClient.Timeout = Timeout
	var resp *http.Response
	resp, err = httpClient.Do(req)
	if err != nil {
		return
	}

	if resp.StatusCode != 200 {
		err = errors.New("请求错误:" + strconv.Itoa(resp.StatusCode))
		return
	}

	defer resp.Body.Close()
	bytes, err = ioutil.ReadAll(resp.Body)
	// fmt.Printf("\n\n\nreq: %v\n\nresp: %v\n\n\n", p.getReqData(), string(bytes))
	return
}

// Execute 执行API接口
func Execute(method string, p Arg) (res *simplejson.Json, err error) {
	p, r := setReqData(p, method)
	var bodyBytes []byte
	bodyBytes, err = execute(p, r)
	if err != nil {
		return
	}

	return bytesToResult(bodyBytes)
}

func bytesToResult(bytes []byte) (res *simplejson.Json, err error) {
	res, err = simplejson.NewJson(bytes)
	if err != nil {
		return
	}
	if responseError, ok := res.CheckGet("error_response"); ok {
		if subMsg, subOk := responseError.CheckGet("sub_msg"); subOk {
			err = errors.New(subMsg.MustString())
		} else if zhDesc, descOk := responseError.CheckGet("zh_desc"); descOk {
			err = errors.New(zhDesc.MustString())
		} else {
			err = errors.New(responseError.Get("msg").MustString())
		}
		res = nil
	}
	return
}

// ExecuteCache 执行API接口,缓存

// 检查配置
func checkConfig() error {
	if TaobaoAppKey == "" && JDAppKey == "" && KaolaAppKey == "" && PDDAppKey == "" {
		return errors.New("至少需要设置一个平台参数")
	}
	return nil
}

//组装参数及添加公共参数
func setReqData(p Arg, method string) (Arg, string) {
	platform := strings.Split(method, ".")[0]
	router := ""
	hh, _ := time.ParseDuration("8h")
	loc := time.Now().UTC().Add(hh)
	if platform == "taobao" {
		//淘宝
		p["timestamp"] = strconv.FormatInt(loc.Unix(), 10)
		p["partner_id"] = "Blant"
		p["app_key"] = TaobaoAppKey
		p["v"] = TaobaoVersion
		if Session != "" {
			p["session"] = Session
		}
		p["method"] = method
		p["format"] = "json"
		p["sign_method"] = "md5"
		// 设置签名
		p["sign"] = getSign(p, TaobaoAppSecret)
		router = TaobaoRouter
	} else if platform == "jd" {
		//京东
		param := p
		p = Arg{}
		p["param_json"] = param
		p["app_key"] = JDAppKey
		p["v"] = JDVersion
		p["timestamp"] = loc.Format("2006-01-02 15:04:05")
		p["method"] = method
		p["format"] = "json"
		p["sign_method"] = "md5"
		// 设置签名
		p["sign"] = getSign(p, JDAppSecret)
		router = JDRouter
	} else if platform == "pdd" {
		//拼多多
		p["type"] = method
		p["data_type"] = "json"
		p["version"] = PDDVersion
		p["client_id"] = PDDAppKey
		p["timestamp"] = strconv.FormatInt(loc.Unix(), 10)
		// 设置签名
		p["sign"] = getSign(p, PDDAppSecret)
		router = PDDRouter
	} else if platform == "kaola" {
		//考拉海购
		p["method"] = method
		p["v"] = KaolaVersion
		p["signMethod"] = "md5"
		p["unionId"] = KaolaAppKey
		p["timestamp"] = loc.Format("2006-01-02 15:04:05")
		// 设置签名
		p["sign"] = getSign(p, KaolaAppSecret)
		router = KaolaRouter
	} else if platform == "suning" {
		// TODO 苏宁

	} else if platform == "vip" {
		// TODO 唯品会
	}

	return p, router
}

// 获取请求数据
func (p Arg) getReqData() string {
	// 公共参数
	args := url.Values{}
	// 请求参数
	for key, val := range p {
		args.Set(key, interfaceToString(val))
	}
	return args.Encode()
}

// 获取签名
func getSign(p Arg, secret string) string {
	// 获取Key
	var keys []string
	for k := range p {
		keys = append(keys, k)
	}
	// 排序asc
	sort.Strings(keys)
	// 把所有参数名和参数值串在一起
	query := bytes.NewBufferString(secret)
	for _, k := range keys {
		query.WriteString(k)
		query.WriteString(interfaceToString(p[k]))
	}
	query.WriteString(secret)
	// 使用MD5加密
	h := md5.New()
	_, _ = io.Copy(h, query)
	// 把二进制转化为大写的十六进制
	return strings.ToUpper(hex.EncodeToString(h.Sum(nil)))
}

func interfaceToString(src interface{}) string {
	if src == nil {
		panic(ErrTypeIsNil)
	}
	switch src.(type) {
	case string:
		return src.(string)
	case int, int8, int32, int64:
	case uint8, uint16, uint32, uint64:
	case float32, float64:
		return convert.ToString(src)
	}
	data, err := json.Marshal(src)
	if err != nil {
		panic(err)
	}
	return string(data)
}