package ebaiapi

import (
	"crypto/md5"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"

	"git.rosy.net.cn/baseapi/platformapi"
	"git.rosy.net.cn/baseapi/utils"
)

const (
	prodURL = "https://api-be.ele.me/"

	signKey   = "sign"
	secretKey = "secret"
)

const (
	ResponseCodeSuccess         = 0
	ResponseCodeSuccess2        = 200
	GetStoreSuccessButUnderfind = 4001 // 拉取网页饿百门店信息，但是显示门店不存在
	ResponseCodeCallElmFailed   = 2101 // 这个可以尝试重试
)

type ResponseResult struct {
	ErrNo int         `json:"errno"`
	Error string      `json:"error"`
	Data  interface{} `json:"data"`
}

type API struct {
	platformapi.APICookie

	source       string
	secret       string
	encrypt      string
	client       *http.Client
	config       *platformapi.APIConfig
	speedLimiter *platformapi.Limiter
	supplierID   int64
}

func New(source, secret string, config ...*platformapi.APIConfig) *API {
	// baseapi.SugarLogger.Debugf("token=%v, pay=%v, secret=%v", token, appKey, secret)
	curConfig := platformapi.DefAPIConfig
	if len(config) > 0 {
		curConfig = *config[0]
	}
	api := &API{
		source:       source,
		secret:       secret,
		client:       &http.Client{Timeout: curConfig.ClientTimeout},
		config:       &curConfig,
		speedLimiter: platformapi.New(apiLimitConfigs, nil), //defaultAPILimitConfig),

		supplierID: -1,
	}
	return api
}

func (a *API) signParams(params url.Values) string {
	keyValues := make([]string, 0)
	for k, v := range params {
		if k != signKey {
			keyValues = append(keyValues, k+"="+v[0])
		}
	}
	keyValues = append(keyValues, secretKey+"="+a.secret)

	sort.Strings(keyValues)
	finalStr := strings.Join(keyValues, "&")
	// baseapi.SugarLogger.Debugf("sign str:%v", finalStr)
	return fmt.Sprintf("%X", md5.Sum([]byte(finalStr)))
}

func (a *API) getShopID(body map[string]interface{}) (shopID string) {
	if body[KeyShopID] != nil {
		return body[KeyShopID].(string)
	} else if body[KeyBaiduShopID] != nil {
		return fmt.Sprint(body[KeyBaiduShopID])
	}
	return ""
}

func (a *API) GetSource() string {
	return a.source
}

func (a *API) AccessAPI2(cmd string, body map[string]interface{}, trackInfo string) (retVal *ResponseResult, err error) {
	a.speedLimiter.AccessAPI(cmd, a.getShopID(body))
	if body == nil {
		body = make(map[string]interface{}, 0)
	}
	params := url.Values{
		"cmd":       []string{cmd},
		"version":   []string{"3"},
		"timestamp": []string{utils.Int64ToStr(utils.GetCurTimestamp())},
		"ticket":    []string{utils.GetUpperUUID()},
		"source":    []string{a.source},
		"body":      []string{string(utils.MustMarshal(body))},
		"encrypt":   []string{a.encrypt},
	}

	params[signKey] = []string{a.signParams(params)}
	encodedParams := params.Encode()
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			request, _ := http.NewRequest(http.MethodPost, prodURL, strings.NewReader(encodedParams))
			request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
			request.Header.Set("User-Agent", "ebai-golang-api")
			if trackInfo != "" {
				request.Header.Set(platformapi.KeyTrackInfo, trackInfo)
			}
			return request
		},
		a.config,
		func(response *http.Response, bodyStr string, jsonResult1 map[string]interface{}) (errLevel string, err error) {
			if jsonResult1 == nil {
				return platformapi.ErrLevelRecoverableErr, fmt.Errorf("mapData is nil")
			}
			Body := jsonResult1["body"].(map[string]interface{})
			retVal = &ResponseResult{
				ErrNo: int(utils.MustInterface2Int64(Body["errno"])),
				Error: utils.Interface2String(Body["error"]),
				Data:  Body["data"],
			}
			if retVal.ErrNo == ResponseCodeSuccess {
				return platformapi.ErrLevelSuccess, nil
			}
			newErr := utils.NewErrorIntCode(retVal.Error, retVal.ErrNo)
			// todo 临时处理超过阈值错...
			if (newErr.IntCode() == 20501 && strings.Index(retVal.Error, "阈值") >= 0) || (newErr.IntCode() == 20502 && strings.Index(retVal.Error, "系统繁忙") >= 0) {
				return platformapi.ErrLevelExceedLimit, newErr
			}
			return platformapi.ErrLevelCodeIsNotOK, newErr
		})
	err = platformapi.RebuildError(err, body, []string{
		KeyBaiduShopID,
		KeyShopID,
		KeySkuID,
		KeyCustomSkuID,
	})
	return retVal, err
}

func (a *API) AccessAPI(cmd string, body map[string]interface{}) (retVal *ResponseResult, err error) {
	return a.AccessAPI2(cmd, body, "")
}

func (a *API) GetSupplierID() (supplierID int64) {
	a.RLock()
	supplierID = a.supplierID
	a.RUnlock()
	if supplierID < 0 {
		a.Lock()
		defer a.Unlock()

		a.supplierID = 0
		if shopList, err := a.ShopList(SysStatusAll); err == nil {
			for _, shop := range shopList {
				if shopDetail, err := a.ShopGet2("", shop.BaiduShopID); err != nil {
					break
				} else if shopDetail.SupplierID > 0 {
					a.supplierID = shopDetail.SupplierID
					break
				}
			}
		}
	}
	return a.supplierID
}
