package lakala

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"git.rosy.net.cn/baseapi/platformapi"
	"git.rosy.net.cn/baseapi/utils"
	"net/http"
	"strings"
	"time"
)

type API struct {
	appID          string
	serialNo       string
	sM4Key         string
	clientId       string
	clientSecret   string
	incomingToken  string // 进件相关token
	incomingExpire int64  // 进件token过期时间
	modifiedToken  string // 改件token
	modifiedExpire int64  // 改件过期时间
	orgCode        string // 机构代码
	client         *http.Client
	config         *platformapi.APIConfig
}

func New(appID, serialNo, sM4Key, clientId, clientSecret, incomingToken, changeToken, orgCode string, config ...*platformapi.APIConfig) *API {
	curConfig := platformapi.DefAPIConfig
	if len(config) > 0 {
		curConfig = *config[0]
	}
	return &API{
		appID:          appID,
		serialNo:       serialNo,
		sM4Key:         sM4Key,
		clientId:       clientId,
		clientSecret:   clientSecret,
		incomingToken:  incomingToken,
		incomingExpire: 0,
		modifiedToken:  changeToken,
		modifiedExpire: 0,
		orgCode:        orgCode,
		client:         &http.Client{Timeout: curConfig.ClientTimeout},
		config:         &curConfig,
	}
}

// AccessAPI form表单格式
func (a *API) AccessAPI(baseUrl, action, method string, pathParam string, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) {
	if action != TokenActive {
		a.CheckToken()
	}
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			var request *http.Request
			if http.MethodPost == method {
				// 全路径请求参数s
				fullURL := utils.GenerateGetURL(baseUrl, action, nil)
				request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(utils.Map2URLValues(bizParams).Encode()))
				request.Header.Set("charset", "UTF-8")
				request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
			} else {
				getUrl := utils.GenerateGetURL(baseUrl, action, nil)
				if pathParam != "" {
					getUrl = getUrl + fmt.Sprintf("/%s", pathParam)
				}
				if bizParams != nil {
					getUrl = utils.GenerateGetURL(getUrl, "", bizParams)
				}
				request, _ = http.NewRequest(http.MethodGet, getUrl, nil)
			}
			switch IncomingTokenActive[action] {
			case IncomingToken:
				request.Header.Set("Authorization", fmt.Sprintf("bearer %s", a.incomingToken))
			case ModifiedToken:
				request.Header.Set("Authorization", fmt.Sprintf("bearer %s", a.modifiedToken))
			case NotToken:
				if bizParams["grant_type"].(string) == "password" {
					base64Unicode := fmt.Sprintf("%s:%s", a.clientId, a.clientSecret)
					request.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(base64Unicode))))
				}
			}
			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 {
				//returnCode := utils.Interface2String(jsonResult1["trxstatus"])
				//if returnCode != "200" {
				//	errLevel = platformapi.ErrLevelGeneralFail
				//	err = utils.NewErrorCode(utils.Interface2String(jsonResult1["errmsg"])+utils.Interface2String(jsonResult1["retmsg"]), returnCode)
				//}
				retVal = jsonResult1
			}
			return errLevel, err
		})
	return retVal, err
}

// AccessAPI2 json格式
func (a *API) AccessAPI2(baseUrl, action, method string, pathParam string, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) {
	if action != TokenActive {
		a.CheckToken()
	}
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			var request *http.Request
			if http.MethodPost == method {
				// 全路径请求参数
				data, _ := json.Marshal(bizParams)
				fullURL := utils.GenerateGetURL(baseUrl, action, nil)
				if pathParam != "" {
					fullURL = fullURL + fmt.Sprintf("/%s", pathParam)
				}
				request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(string(data)))
			} else {
				getUrl := utils.GenerateGetURL(baseUrl, action, nil)
				if pathParam != "" {
					getUrl = getUrl + fmt.Sprintf("/%s", pathParam)
				}
				if bizParams != nil {
					getUrl = utils.GenerateGetURL(getUrl, "", bizParams)
				}
				request, _ = http.NewRequest(http.MethodGet, getUrl, nil)
			}
			request.Header.Set("Content-Type", "application/json")
			switch IncomingTokenActive[action] {
			case IncomingToken:
				request.Header.Set("Authorization", fmt.Sprintf("bearer %s", a.incomingToken))
			case ModifiedToken:
				request.Header.Set("Authorization", fmt.Sprintf("bearer %s", a.modifiedToken))
			case SignToken:
				request.Header.Set("Authorization", fmt.Sprintf("LKLAPI-SHA256withRSA %s", a.modifiedToken))
			case NotToken:
				if bizParams["grant_type"].(string) == "password" {
					base64Unicode := fmt.Sprintf("%s:%s", a.clientId, a.clientSecret)
					request.Header.Set("Authorization", fmt.Sprintf("Basic %s", base64.StdEncoding.EncodeToString([]byte(base64Unicode))))
				}
			}
			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
}

// AccessAPISign 支付相关需要签名
func (a *API) AccessAPISign(baseUrl, action, method string, pathParam string, bizParams map[string]interface{}) (retVal map[string]interface{}, err error) {
	if action != TokenActive {
		a.CheckToken()
	}
	Authorization, err := a.signParamPrivateKey(bizParams, LaKaLaPrivateKey)
	if err != nil {
		return nil, err
	}
	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			var request *http.Request
			if http.MethodPost == method {
				// 全路径请求参数
				data, _ := json.Marshal(bizParams)
				fullURL := utils.GenerateGetURL(baseUrl, action, nil)
				if pathParam != "" {
					fullURL = fullURL + fmt.Sprintf("/%s", pathParam)
				}
				request, _ = http.NewRequest(http.MethodPost, fullURL, strings.NewReader(string(data)))
			} else {
				getUrl := utils.GenerateGetURL(baseUrl, action, nil)
				if pathParam != "" {
					getUrl = getUrl + fmt.Sprintf("/%s", pathParam)
				}
				if bizParams != nil {
					getUrl = utils.GenerateGetURL(getUrl, "", bizParams)
				}
				request, _ = http.NewRequest(http.MethodGet, getUrl, nil)
			}
			request.Header.Set("Content-Type", "application/json")
			request.Header.Set("Authorization", Authorization)
			request.Header.Set("Accept", "application/json")
			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) CheckToken() {
	if a.incomingToken == "" || a.incomingExpire-time.Now().Unix() < 0 {
		a.IncomingToken()
	}
	if a.modifiedToken == "" || a.modifiedExpire-time.Now().Unix() < 0 {
		a.ModifiedToken(UserName, Password)
	}
}
