package feieapi

import (
	"crypto/sha1"
	"fmt"
	"net/http"
	"strings"
	"time"
	"unicode"

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

const (
	prodURL = "https://api.feieyun.cn/Api/Open/"
	signKey = "sig"
)
const (
	// ResponseCodeSuccess 操作成功
	ResponseCodeSuccess = 0
)

const (
	PrinterStatusStrOffline        = "离线。"
	PrinterStatusStrOnlineOK       = "在线，工作状态正常。"
	PrinterStatusStrOnlineAbnormal = "在线，工作状态不正常。"

	PrinterStatusUnknown        = 0
	PrinterStatusOffline        = 1
	PrinterStatusOnlineOK       = 2
	PrinterStatusOnlineAbnormal = 3
)

var (
	printStatusMap = map[string]int{
		PrinterStatusStrOffline:        PrinterStatusOffline,
		PrinterStatusStrOnlineOK:       PrinterStatusOnlineOK,
		PrinterStatusStrOnlineAbnormal: PrinterStatusOnlineAbnormal,
	}
)

const (
	ErrMsgAlredyAdded = "（错误：已被添加过）"
)

var (
	exceedLimitCodes = map[int]int{}

	canRetryCodes = map[int]int{}
)

type API struct {
	platformapi.APICookie

	user   string
	ukey   string
	client *http.Client
	config *platformapi.APIConfig
}

type PrinterInfo struct {
	SN       string
	Key      string
	Name     string
	PhoneNum string
}

type PrinterResultInfo struct {
	SN     string
	ErrMsg string
}

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

func (a *API) signParams(apiParams map[string]interface{}) string {
	return fmt.Sprintf("%x", sha1.Sum([]byte(a.user+a.ukey+apiParams["stime"].(string))))
}

func (a *API) AccessAPI(apiName string, apiParams map[string]interface{}) (retVal interface{}, err error) {
	params := utils.MergeMaps(map[string]interface{}{
		"user":    a.user,
		"apiname": apiName,
		// "debug":   "1",
	}, apiParams)

	userGet := true
	if true {
		userGet = false
	}

	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			params["stime"] = utils.Int64ToStr(time.Now().Unix())
			sign := a.signParams(params)
			params[signKey] = sign
			var request *http.Request
			if userGet {
				fullURL := utils.GenerateGetURL(prodURL, "", params)
				// baseapi.SugarLogger.Debug(fullURL)
				request, _ = http.NewRequest(http.MethodGet, fullURL, nil)
			} else {
				fullURL := prodURL
				// baseapi.SugarLogger.Debug(utils.Map2URLValues(params).Encode())
				request, _ = http.NewRequest(http.MethodPost, fullURL, 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.Interface2Int64WithDefault(jsonResult1["ret"], ResponseCodeSuccess))
			if code == ResponseCodeSuccess {
				retVal = jsonResult1["data"]
				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) PrinterAddList(printerList []*PrinterInfo) (ok, no map[string]string, err error) {
	printerContent := []string{}
	for _, v := range printerList {
		printerContent = append(printerContent, strings.Join([]string{
			v.SN,
			v.Key,
			v.Name,
			v.PhoneNum,
		}, "#"))
	}
	result, err := a.AccessAPI("Open_printerAddlist", map[string]interface{}{
		"printerContent": strings.Join(printerContent, "\n"),
	})
	if err == nil {
		resultMap := result.(map[string]interface{})
		return interface2PrinterResultList4Add(resultMap["ok"], true), interface2PrinterResultList4Add(resultMap["no"], false), nil
	}
	return nil, nil, err
}

func interface2PrinterResultList4Add(value interface{}, isOk bool) (printerResultMap map[string]string) {
	printerResultMap = make(map[string]string)
	for _, v := range value.([]interface{}) {
		strList := strings.Split(v.(string), "#")
		if len(strList) == 4 {
			if isOk {
				strList2 := strings.Split(strList[3], " ")
				if len(strList2) == 1 {
					strList2 = append(strList2, "")
				}
				printerResultMap[strList[0]] = strList2[1]
			} else {
				printerResultMap[strList[0]] = strList[3]
			}
		}
	}
	return printerResultMap
}

func (a *API) PrintMsg(sn, content string, times int) (orderID string, err error) {
	result, err := a.AccessAPI("Open_printMsg", map[string]interface{}{
		"sn":      sn,
		"content": content,
		"times":   times,
	})
	if err == nil {
		if result != nil {
			return result.(string), nil
		}
		err = fmt.Errorf("飞鹅sn:%s非正常的结果返回", sn)
	}
	return "", err
}

func (a *API) PrinterDelList(snList []string) (ok, no map[string]string, err error) {
	result, err := a.AccessAPI("Open_printerDelList", map[string]interface{}{
		"snlist": strings.Join(snList, "-"),
	})
	if err == nil {
		resultMap := result.(map[string]interface{})
		return interface2PrinterResultList4Del(resultMap["ok"]), interface2PrinterResultList4Del(resultMap["no"]), nil
	}
	return nil, nil, err
}

func interface2PrinterResultList4Del(value interface{}) (printerResultMap map[string]string) {
	printerResultMap = make(map[string]string)
	for _, v := range value.([]interface{}) {
		sn, errMsg := splitMsg(v.(string))
		printerResultMap[sn] = errMsg
	}
	return printerResultMap
}

func splitMsg(msg string) (sn, errMsg string) {
	runeList := []rune(msg)
	index := 0
	for ; index < len(runeList); index++ {
		if !unicode.Is(unicode.ASCII_Hex_Digit, runeList[index]) {
			break
		}
	}
	if index > 0 && index < len(runeList) {
		sn = string(runeList[:index])
		errMsg = string(runeList[index:])
	}
	return sn, errMsg
}

func (a *API) PrinterEdit(sn, name, phoneNum string) (err error) {
	params := map[string]interface{}{
		"sn":   sn,
		"name": name,
	}
	if phoneNum != "" {
		params["phonenum"] = phoneNum
	}
	_, err = a.AccessAPI("Open_printerEdit", params)
	return err
}

func (a *API) DelPrinterSqs(sn string) (err error) {
	_, err = a.AccessAPI("Open_delPrinterSqs", map[string]interface{}{
		"sn": sn,
	})
	return err
}

func (a *API) QueryOrderState(orderID string) (isFinished bool, err error) {
	result, err := a.AccessAPI("Open_queryOrderState", map[string]interface{}{
		"orderid": orderID,
	})
	if err == nil {
		return result.(bool), nil
	}
	return false, err
}

func (a *API) QueryOrderInfoByDate(sn string, date time.Time) (printed, waiting int, err error) {
	result, err := a.AccessAPI("Open_queryOrderInfoByDate", map[string]interface{}{
		"sn":   sn,
		"date": utils.Time2DateStr(date),
	})
	if err == nil && result != nil {
		resultMap := result.(map[string]interface{})
		return int(utils.Interface2Int64WithDefault(resultMap["print"], 0)), int(utils.Interface2Int64WithDefault(resultMap["waiting"], 0)), nil
	}
	return 0, 0, err
}

func (a *API) QueryPrinterStatus(sn string) (status int, err error) {
	result, err := a.AccessAPI("Open_queryPrinterStatus", map[string]interface{}{
		"sn": sn,
	})
	if err == nil && result != nil {
		return printStatusMap[result.(string)], nil
	}
	return PrinterStatusUnknown, err
}
