package xiaowmapi

import (
	"bytes"
	"fmt"
	"net/http"

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

const (
	prodURL = "http://open.xiaowm.com"
)

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

const (
	RunningStateNormalStr  = "正常运行"
	RunningStateNormalStr2 = "正常工作"

	RunningStateOfflineStr = "掉线"
	RunningStateOffline    = 0
	RunningStateNormal     = 1

	PaperStateLackPaperStr = "缺纸"
	PaperStateNormalStr    = "正常"
	PaperStateLackPaper    = 0
	PaperStateNormal       = 1
)

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

	stateMap = map[string]int{
		RunningStateOfflineStr: RunningStateOffline,
		RunningStateNormalStr:  RunningStateNormal,
		RunningStateNormalStr2: RunningStateNormal,
		PaperStateLackPaperStr: PaperStateLackPaper,
		PaperStateNormalStr:    PaperStateNormal,
	}
)

type API struct {
	appID  int    // 应用ID
	appKey string // 应用编号
	client *http.Client
	config *platformapi.APIConfig
}

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

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

	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			request, _ := http.NewRequest(http.MethodPost, utils.GenerateGetURL(prodURL, apiName, nil), bytes.NewReader(utils.MustMarshal(params)))
			request.Header.Set("charset", "UTF-8")
			request.Header.Set("Content-Type", "application/json")

			// 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.Interface2Int64WithDefault(jsonResult1["errno"], ResponseCodeSuccess))
			if code == ResponseCodeSuccess {
				retVal = jsonResult1
				return platformapi.ErrLevelSuccess, nil
			}
			newErr := utils.NewErrorIntCode(jsonResult1["msg"].(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) AuthPrinter(printerNumber, pwd, extends string) (printerToken string, err error) {
	if pwd == "" {
		pwd = "888888" // 缺省密码
	}
	params := map[string]interface{}{
		"number": printerNumber,
		"app_id": a.appID,
		"pwd":    pwd,
	}
	if extends != "" {
		params["extends"] = extends
	}
	result, err := a.AccessAPI("Auth_api/auth", params)
	if err == nil {
		return utils.Interface2String(result["data"].(map[string]interface{})["token"]), nil
	}
	return "", err
}

func (a *API) DelPrinter(printerNumber, printerToken string) (err error) {
	_, err = a.AccessAPI("Auth_api/delete", map[string]interface{}{
		"number": printerNumber,
		"token":  printerToken,
	})
	return err
}

func (a *API) SendMsg(printerNumber, printerToken, msg string) (msgID string, err error) {
	result, err := a.AccessAPI("Api/send_msg", map[string]interface{}{
		"number": printerNumber,
		"token":  printerToken,
		"msg":    msg,
	})
	if err == nil {
		return utils.Interface2String(result["msg_id"]), nil
	}
	return "", err
}

func (a *API) GetPrinterStatus(printerNumber, printerToken string) (runningState, paperState int, err error) {
	result, err := a.AccessAPI("Api/get_printer_status", map[string]interface{}{
		"number": printerNumber,
		"token":  printerToken,
	})
	if err == nil {
		data := result["data"].(map[string]interface{})
		return stateMap[utils.Interface2String(data["running_state"])], stateMap[utils.Interface2String(data["paper_state"])], nil
	}
	return 0, 0, err
}

func (a *API) GetMsgStatus(printerNumber, printerToken, msgID string) (msgStatus string, err error) {
	result, err := a.AccessAPI("Api/get_msg_status", map[string]interface{}{
		"number": printerNumber,
		"token":  printerToken,
		"msg_id": msgID,
	})
	if err == nil {
		return utils.Interface2String(result["msg"]), nil
	}
	return "", err
}
