package zhongwuapi

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

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

const (
	prodURL = "http://api.zhongwuyun.com"

	signKey      = "sign"
	timestampKey = "timestamp"
)

const (
	ResponseCodeSuccess = 0 // 操作成功
)

const (
	PrinterStatusOffline   = 0
	PrinterStatusOnline    = 1
	PrinterStatusLackPaper = 2
)

var (
	exceedLimitCodes = map[int]int{}
	canRetryCodes    = map[int]int{}
)

type API struct {
	appID     int
	appSecret string
	client    *http.Client
	config    *platformapi.APIConfig
}

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

func (a *API) signParams(apiParams map[string]interface{}) string {
	keys := utils.MapKeys(apiParams)
	sort.Sort(sort.StringSlice(keys))
	valueList := make([]string, len(keys)+1)
	for index, key := range keys {
		valueList[index] = fmt.Sprintf("%s%v", key, apiParams[key])
	}
	valueList[len(keys)] = a.appSecret
	return fmt.Sprintf("%x", md5.Sum([]byte(strings.Join(valueList, ""))))
}

func (a *API) AccessAPI(apiName string, apiParams map[string]interface{}, isGet bool) (retVal map[string]interface{}, err error) {
	params := utils.MergeMaps(map[string]interface{}{
		"appid": a.appID,
	}, apiParams)

	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() (request *http.Request) {
			params[timestampKey] = time.Now().Unix()
			params[signKey] = a.signParams(params)
			if isGet {
				fullURL := utils.GenerateGetURL(prodURL, apiName, params)
				// baseapi.SugarLogger.Debug(fullURL)
				request, _ = http.NewRequest(http.MethodGet, fullURL, nil)
			} else {
				request, _ = http.NewRequest(http.MethodPost, utils.GenerateGetURL(prodURL, apiName, nil), strings.NewReader(utils.Map2URLValues(params).Encode()))
				request.Header.Set("charset", "UTF-8")
				request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
			}
			// request.Close = true //todo 为了性能考虑还是不要关闭
			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")
			}
			code := int(utils.MustInterface2Int64(jsonResult1["errNum"]))
			if code == ResponseCodeSuccess {
				retVal, _ = jsonResult1["retData"].(map[string]interface{})
				return platformapi.ErrLevelSuccess, nil
			}
			newErr := utils.NewErrorIntCode(jsonResult1["errMsg"].(string), code)
			if _, ok := exceedLimitCodes[code]; ok {
				return platformapi.ErrLevelExceedLimit, newErr
			} else if _, ok := canRetryCodes[code]; ok {
				return platformapi.ErrLevelRecoverableErr, newErr
			} else {
				return platformapi.ErrLevelCodeIsNotOK, newErr
			}
		})
	return retVal, err
}

func (a *API) PrintMsg(deviceID, deviceSecret, printData string) (dataID string, status int, err error) {
	retData, err := a.AccessAPI("", map[string]interface{}{
		"deviceid":     deviceID,
		"devicesecret": deviceSecret,
		"printdata":    printData,
	}, false)
	if err == nil {
		return utils.Interface2String(retData["id"]), int(utils.Str2Int64(utils.Interface2String(retData["status"]))), nil
	}
	return "", 0, err
}

func (a *API) GetPrinterStatus(deviceID, deviceSecret string) (status int, err error) {
	retData, err := a.AccessAPI("status", map[string]interface{}{
		"deviceid":     deviceID,
		"devicesecret": deviceSecret,
	}, true)
	if err == nil {
		return int(utils.Str2Int64(utils.Interface2String(retData["status"]))), nil
	}
	return 0, err
}

func (a *API) GetPrintStatus(deviceID, deviceSecret, dataID string) (isPrinted bool, err error) {
	retData, err := a.AccessAPI("printstatus", map[string]interface{}{
		"deviceid":     deviceID,
		"devicesecret": deviceSecret,
		"dataid":       dataID,
	}, true)
	if err == nil {
		return int(utils.Str2Int64(utils.Interface2String(retData["status"]))) == 1, nil
	}
	return false, err
}

func (a *API) EmptyPrintQueue(deviceID, deviceSecret string) (status int, err error) {
	retData, err := a.AccessAPI("emptyprintqueue", map[string]interface{}{
		"deviceid":     deviceID,
		"devicesecret": deviceSecret,
	}, true)
	if err == nil {
		return int(utils.Str2Int64(utils.Interface2String(retData["status"]))), nil
	}
	return 0, err
}
