package mtunionapi

import (
	"crypto/md5"
	"encoding/json"
	"fmt"
	"net/http"
	"sort"
	"strings"

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

const (
	prodURL = "https://runion.meituan.com"
	sigKey  = "sign"

	ActTypeQB = 1 //券包推广
)

type API struct {
	platformapi.APICookie

	appKey    string
	appSecret string
	client    *http.Client
	config    *platformapi.APIConfig
}

func New(appKey, appSecret string, config ...*platformapi.APIConfig) *API {
	curConfig := platformapi.DefAPIConfig
	if len(config) > 0 {
		curConfig = *config[0]
	}
	return &API{
		appKey:    appKey,
		appSecret: appSecret,
		client:    &http.Client{Timeout: curConfig.ClientTimeout},
		config:    &curConfig,
	}
}

func (a *API) signParam(params map[string]interface{}) (sig string) {
	var valueList []string
	for k, v := range params {
		if k != sigKey {
			if str := fmt.Sprint(v); str != "" {
				valueList = append(valueList, fmt.Sprintf("%s%s", k, str))
			}
		}
	}
	sort.Sort(sort.StringSlice(valueList))
	valueList = append(valueList, fmt.Sprintf("%s", a.appSecret))
	var valueList2 = make([]string, len(valueList)+1)
	at := copy(valueList2, valueList[:0])
	at += copy(valueList2[at:], []string{a.appSecret})
	copy(valueList2[at:], valueList[0:])
	sig = strings.Join(valueList2, "")
	binSig := md5.Sum([]byte(sig))
	sig = fmt.Sprintf("%x", binSig)
	return sig
}

func (a *API) AccessAPI(action string, isPost bool, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) {
	params := make(map[string]interface{})
	params["key"] = a.appKey
	params = utils.MergeMaps(params, bizParams)
	signStr := a.signParam(params)
	params[sigKey] = signStr
	fullURL := utils.GenerateGetURL(prodURL, action, nil)
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			var request *http.Request
			if !isPost {
				request, _ = http.NewRequest(http.MethodGet, utils.GenerateGetURL(fullURL, "", params), nil)
			} else {
				request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(params).Encode()))
			}
			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")
			}
			if err == nil {
				if utils.MustInterface2Int64(jsonResult1["status"]) != 0 {
					errLevel = platformapi.ErrLevelGeneralFail
					err = utils.NewErrorCode(jsonResult1["des"].(string), utils.Int64ToStr(utils.MustInterface2Int64(jsonResult1["status"])))
				}
				retVal = jsonResult1
			}
			return errLevel, err
		})
	return retVal, err
}

func (a *API) AccessAPI2(action string, isPost bool, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) {
	params := make(map[string]interface{})
	params["key"] = a.appKey
	params = utils.MergeMaps(params, bizParams)
	signStr := a.signParam(params)
	params[sigKey] = signStr
	fullURL := utils.GenerateGetURL(prodURL, action, nil)
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			var request *http.Request
			if !isPost {
				request, _ = http.NewRequest(http.MethodGet, utils.GenerateGetURL(fullURL, "", params), nil)
			} else {
				request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(params).Encode()))
			}
			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")
			}
			if err == nil {
				retVal = jsonResult1
			}
			return errLevel, err
		})
	return retVal, err
}

func (a *API) AccessStorePage(fullURL string, bizParams map[string]interface{}, isPost bool) (retVal map[string]interface{}, err error) {
	if a.GetCookieCount() == 0 {
		return nil, fmt.Errorf("需要设置Store Cookie才能使用此方法")
	}
	data, _ := json.Marshal(bizParams)
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			var request *http.Request
			if isPost {
				request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(string(data)))
				request.Header.Set("Content-Type", "application/json;charset=utf-8")
			} else {
				request, _ = http.NewRequest(http.MethodGet, utils.GenerateGetURL(fullURL, "", bizParams), nil)
			}
			a.FillRequestCookies(request)
			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")
			}
			if strings.Contains(bodyStr, "登录") || strings.Contains(bodyStr, "访问的内容") {
				return platformapi.ErrLevelRecoverableErr, fmt.Errorf("cookie可能过期了！")
			}
			if err == nil {
				if jsonResult1["error_response"] != nil {
					errLevel = platformapi.ErrLevelGeneralFail
					err = utils.NewErrorCode(jsonResult1["error_response"].(map[string]interface{})["zh_desc"].(string), jsonResult1["error_response"].(map[string]interface{})["code"].(string))
				}
				retVal = jsonResult1
			}
			return errLevel, err
		})
	return retVal, err
}

//https://union.meituan.com/v2/apiDetail?id=8
//https://runion.meituan.com/generateLink
func (a *API) MiniCode(actID int, userID string) (url string, err error) {
	result, err := a.AccessAPI("miniCode", false, map[string]interface{}{
		"sid":   strings.ToLower(userID),
		"actId": actID,
	})
	if err == nil {
		if utils.MustInterface2Int64(result["status"]) != 0 {
			return "", fmt.Errorf(result["des"].(string))
		} else {
			return result["data"].(string), err
		}
	}
	return url, err
}

func (a *API) GenerateLink(actID, linkType int, userID string) (url string, err error) {
	result, err := a.AccessAPI("generateLink", false, map[string]interface{}{
		"sid":      strings.ToLower(userID),
		"actId":    actID,
		"linkType": linkType,
	})
	if err == nil {
		if utils.MustInterface2Int64(result["status"]) != 0 {
			return "", fmt.Errorf(result["des"].(string))
		} else {
			return result["data"].(string), err
		}
	}
	return url, err
}

type ActivityListResult struct {
	ID        int    `json:"id"`
	ActName   string `json:"actName"`
	ActDes    string `json:"actDes"`
	URL       string `json:"url"`
	ActRule   string `json:"actRule"`
	Ratio     string `json:"ratio"`
	DateBound string `json:"dateBound"`
	ActSrc    string `json:"actSrc"`
}

//https://union.meituan.com/api/activity/list
func (a *API) ActivityList(actType, limit, offset int) (activityListResult []*ActivityListResult, err error) {
	result, err := a.AccessStorePage("https://union.meituan.com/api/activity/list", map[string]interface{}{
		"businessLine": 0,
		"limit":        limit,
		"offset":       offset,
	}, true)
	if err == nil {
		utils.Map2StructByJson(result["data"].(map[string]interface{})["dataList"], &activityListResult, false)
	}
	return activityListResult, err
}
