package mtpsapi

import (
	"crypto/sha1"
	"errors"
	"fmt"
	"git.rosy.net.cn/baseapi/platformapi"
	"git.rosy.net.cn/baseapi/utils"
	"net/http"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"
)

const (
	mtpsAPIURL = "https://peisongopen.meituan.com/api"
	signKey    = "sign"
)

const (
	OrderStatusWaitingForSchedule = 0   // 0：待调度
	OrderStatusAccepted           = 20  // 20：已接单
	OrderStatusPickedUp           = 30  // 30：已取货
	OrderStatusDeliverred         = 50  // 50：已送达
	OrderStatusCanceled           = 99  // 99：已取消
	OrderStatusFakeToStore        = 200 // 自定义假的到店
)

const (
	OrderExceptCodePhonePoweroff       = 10001
	OrderExceptCodePhoneSuspended      = 10002
	OrderExceptCodePhoneNobody         = 10003
	OrderExceptCodePhoneEmpty          = 10004
	OrderExceptCodePhoneWrong          = 10005
	OrderExceptCodePhoneOther          = 10006
	OrderExceptCodeAddressWrong        = 10101
	OrderExceptCodeAddressOutOfService = 10201
	OrderExceptCodeUserReject          = 10202
	OrderExceptCodeUserRequestDelay    = 10203
	OrderExceptCodeStoreClosed         = 10401
)

var (
	OrderStatusName = map[int]string{
		OrderExceptCodePhonePoweroff:       "电话关机",
		OrderExceptCodePhoneSuspended:      "电话停机",
		OrderExceptCodePhoneNobody:         "无人接听",
		OrderExceptCodePhoneEmpty:          "电话空号",
		OrderExceptCodePhoneWrong:          "电话错误",
		OrderExceptCodePhoneOther:          "电话其它原因",
		OrderExceptCodeAddressWrong:        "地址错误",
		OrderExceptCodeAddressOutOfService: "地址超区",
		OrderExceptCodeUserReject:          "用户拒收",
		OrderExceptCodeUserRequestDelay:    "要求延迟配送",
		OrderExceptCodeStoreClosed:         "门店关店",
	}
)

const (
	DeliveryServiceCodeSuperRapid = 4002   // 飞速达
	DeliveryServiceCodeRapid      = 100029 // 快速达,时效型
	DeliveryServiceLocalDa        = 100009 // 同城达
	DeliveryServiceCodeIntime     = 100006 // 及时达(这个暂时没用)
	DeliveryServiceCodeTogether   = 4013   // 集中送
	//DeliveryServiceCodeSuperRapid = 4002   // 飞速达
	//DeliveryServiceCodeRapid      = 4011   // 快速达
	//DeliveryServiceCodeIntime     = 100006 // 及时达
	//DeliveryServiceCodeTogether   = 4013   // 集中送

)

const (
	PickupTypeClientSendToStation = 1
	PickupTypeMtPick              = 2
)

const (
	OrderTypeASAP = 0
	OrderTypeBook = 1
)

const (
	CoordinateTypeMars  = 0 // 火星坐标（高德，腾讯地图均采用火星坐标），此为默认项
	CoordinateTypeBaidu = 1 // 百度坐标
)

// 错误码
const (
	ResponseCodeSuccess      = 0
	ResponseCodeInvalidParam = 4   // 参数格式错误
	ResponseCodeFlowControl  = 11  // 接口流控
	ResponseCodeShopNotExist = 112 // 门店不存在
)

// 取消原因
const (
	CancelReasonClientActive              = 101
	CancelReasonClientChangeTimeOrAddress = 102
	CancelReasonGoodsRelated              = 103
	CancelReasonMerchantOther             = 199

	CancelReasonMtpsAttitude           = 201
	CancelReasonRidderSendNotIntime    = 202
	CancelReasonRideerGetGoodNotIntime = 203
	CancelReasonRideerMtpsOther        = 299

	CancelReasonRideerOther = 399
)

type OrderInfoCommon struct {
	DeliveryID   int64
	MtPeisongID  string
	OrderID      string
	CourierName  string
	CourierPhone string
}

type OrderInfo struct {
	OrderInfoCommon
	Status         int
	OperateTime    int
	CancelReasonID int
	CancelReason   string
}

type OrderResponse struct {
	MtPeisongID string  `json:"mt_peisong_id"`
	DeliveryID  int64   `json:"delivery_id"`
	OrderID     string  `json:"order_id"`
	DeliveryFee float64 `json:"delivery_fee"`
}

type ResponseResult struct {
	Code    int                    `json:"code"`
	Message string                 `json:"message"`
	Data    map[string]interface{} `json:"data"`
}

type CreateOrderByShopParam struct {
	// 以下为必要参数

	DeliveryID          int64   `json:"delivery_id"`
	OrderID             string  `json:"order_id"`
	ShopID              string  `json:"shop_id"`
	DeliveryServiceCode int     `json:"delivery_service_code"`
	ReceiverName        string  `json:"receiver_name"`
	ReceiverAddress     string  `json:"receiver_address"`
	ReceiverPhone       string  `json:"receiver_phone"`
	ReceiverLng         int     `json:"receiver_lng"`
	ReceiverLat         int     `json:"receiver_lat"`
	GoodsValue          float64 `json:"goods_value"`
	GoodsWeight         float64 `json:"goods_weight"`

	// 以下为可选参数

	CoordinateType     int     `json:"coordinate_type"`
	GoodsHeight        float64 `json:"goods_height,omitempty"`
	GoodsWidth         float64 `json:"goods_width,omitempty"`
	GoodsLength        float64 `json:"goods_length,omitempty"`
	GoodsDetail        string  `json:"goods_detail,omitempty"`
	GoodsPickupInfo    string  `json:"goods_pickup_info,omitempty"`
	GoodsDeliveryInfo  string  `json:"goods_delivery_info,omitempty"`
	ExpectedPickupTime int64   `json:"expected_pickup_time,omitempty"` // 期望取货时间，时区为GMT+8，当前距离Epoch（1970年1月1日) 以秒计算的时间，即unix-timestamp。
	// 期望送达时间，时区为GMT+8，当前距离Epoch（1970年1月1日) 以秒计算的时间，即unix-timestamp
	// 即时单：以发单时间 + 服务包时效作为期望送达时间（当天送服务包需客户指定期望送达时间）
	// 预约单：以客户传参数据为准（预约时间必须大于当前下单时间+服务包时效+3分钟）
	ExpectedDeliveryTime int64   `json:"expected_delivery_time,omitempty"`
	OrderType            int     `json:"order_type,omitempty"`
	PoiSeq               string  `json:"poi_seq,omitempty"`
	Note                 string  `json:"note,omitempty"`
	CashOnDelivery       float64 `json:"cash_on_delivery,omitempty"`
	CashOnPickup         float64 `json:"cash_on_pickup,omitempty"`
	InvoiceTitle         string  `json:"invoice_title,omitempty"`
}

type GoodsItem struct {
	GoodCount int     `json:"goodCount"`
	GoodName  string  `json:"goodName"`
	GoodPrice float64 `json:"goodPrice"`
	GoodUnit  string  `json:"goodUnit"`
}

type GoodsDetail struct {
	Goods []*GoodsItem `json:"goods"`
}

type API struct {
	platformapi.APICookie

	appKey string
	secret string
	token  string
	client *http.Client
	config *platformapi.APIConfig
}

var (
	exceedLimitCodes = map[int]int{
		ResponseCodeFlowControl: 1,
	}
)

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

func (a *API) signParams(params url.Values) string {
	keys := make([]string, 0)
	for k := range params {
		if k != signKey {
			keys = append(keys, k)
		}
	}

	sort.Strings(keys)
	finalStr := a.secret
	for _, key := range keys {
		valStr := strings.Join(params[key], "")
		if valStr != "" {
			finalStr += key + valStr
		}
	}

	// baseapi.SugarLogger.Debug(finalStr)
	return fmt.Sprintf("%x", sha1.Sum([]byte(finalStr)))
}

func (a *API) AccessAPI2(baseURL, action string, params map[string]interface{}) (retVal *ResponseResult, err error) {
	params2 := utils.Map2URLValues(params)
	if baseURL == mtpsAPIURL {
		params2.Set("appkey", a.appKey)
		params2.Set("timestamp", utils.Int64ToStr(utils.GetCurTimestamp()))
		params2.Set("version", "1.0")
		params2.Set(signKey, a.signParams(params2))
	} else if a.GetCookieCount() == 0 {
		return nil, fmt.Errorf("需要设置Store Cookie才能使用此方法")
	}

	err = platformapi.AccessPlatformAPIWithRetry(a.client,
		func() *http.Request {
			request, _ := http.NewRequest(http.MethodPost, baseURL+"/"+action, strings.NewReader(params2.Encode()))
			request.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
			request.Header.Set("Referer", "https://page.peisong.meituan.com/open/admin/poilist")
			if baseURL != mtpsAPIURL {
				request.Header.Set("csrfToken", a.token)
				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")
			}
			code := int(utils.MustInterface2Int64(jsonResult1["code"]))
			retVal = &ResponseResult{
				Code: code,
			}
			if code == ResponseCodeSuccess {
				if innerData, ok := jsonResult1["data"]; ok {
					retVal.Data, _ = innerData.(map[string]interface{})
				}
				return platformapi.ErrLevelSuccess, nil
			}
			msgKey := "message"
			if baseURL != mtpsAPIURL {
				msgKey = "msg"
			}
			retVal.Message, _ = jsonResult1[msgKey].(string)
			newErr := utils.NewErrorIntCode(retVal.Message, code)
			if _, ok := exceedLimitCodes[code]; ok {
				//return platformapi.ErrLevelExceedLimit, newErr
				return platformapi.ErrLevelRecoverableErr, newErr
			}
			return platformapi.ErrLevelCodeIsNotOK, newErr
		})
	return retVal, err
}

func (a *API) AccessAPI(action string, params map[string]interface{}) (retVal *ResponseResult, err error) {
	return a.AccessAPI2(mtpsAPIURL, action, params)
}

// 获取美团配送内部订单id，外部系统订单id，美团配送费用(创建订单)
func (a *API) result2OrderResponse(result *ResponseResult) (order *OrderResponse) {
	order = new(OrderResponse)
	order.MtPeisongID = result.Data["mt_peisong_id"].(string)
	order.DeliveryID = utils.MustInterface2Int64(result.Data["delivery_id"])
	order.OrderID = result.Data["order_id"].(string)
	order.DeliveryFee = utils.MustInterface2Float64(result.Data["delivery_fee"]) // 原价
	return order
}

// 美团取消订单
func (a *API) result2OrderCancelOrderRes(result *ResponseResult) (order *OrderResponse) {
	order = new(OrderResponse)
	order.MtPeisongID = result.Data["mt_peisong_id"].(string)
	order.DeliveryID = utils.MustInterface2Int64(result.Data["delivery_id"])
	order.OrderID = result.Data["order_id"].(string)
	return order
}

func (a *API) CreateOrderByShop(basicParams *CreateOrderByShopParam, addParams map[string]interface{}) (order *OrderResponse, err error) {
	params := utils.Struct2MapByJson(basicParams)
	params["goods_value"] = strconv.FormatFloat(basicParams.GoodsValue, 'f', 2, 64)
	params["goods_weight"] = strconv.FormatFloat(basicParams.GoodsWeight, 'f', 2, 64)
	allParams := utils.MergeMaps(params, addParams)

	if params["order_type"] != utils.Int2Str(OrderTypeBook) {
		delete(allParams, "expected_delivery_time")
	}
	if result, err := a.AccessAPI("order/createByShop", allParams); err != nil {
		return nil, err
	} else {
		return a.result2OrderResponse(result), nil
	}
}

// 创建订单
func (a *API) CreateOrderByShop2(basicParams *CreateOrderByShopParam) (order *OrderResponse, err error) {
	params := utils.Struct2MapByJson(basicParams)
	if basicParams.OrderType != OrderTypeBook {
		delete(params, "expected_delivery_time")
	}
	if result, err := a.AccessAPI("order/createByShop", params); err != nil {
		return nil, err
	} else {
		return a.result2OrderResponse(result), nil
	}
}

func (a *API) QueryOrderStatus(deliveryId int64, mtPeiSongId string) (retVal map[string]interface{}, err error) {
	params := map[string]interface{}{
		"delivery_id":   deliveryId,
		"mt_peisong_id": mtPeiSongId,
	}
	result, err := a.AccessAPI("order/status/query", params)
	if err != nil {
		return nil, err
	}
	result.Data["delivery_fee"] = utils.MustInterface2Float64(result.Data["delivery_fee"]) + (float64(utils.WayBillDeliveryMarkUp) / 100)
	result.Data["pay_amount"] = utils.MustInterface2Float64(result.Data["pay_amount"])
	return result.Data, nil
}

func (a *API) CancelOrder(deliveryId int64, mtPeiSongId string, cancelReasonId int, cancelReason string) (result *OrderResponse, err error) {
	params := map[string]interface{}{
		"delivery_id":      deliveryId,
		"mt_peisong_id":    mtPeiSongId,
		"cancel_reason_id": cancelReasonId,
		"cancel_reason":    cancelReason,
	}
	if result, err := a.AccessAPI("order/delete", params); err != nil {
		return nil, err
	} else {
		return a.result2OrderCancelOrderRes(result), nil
	}
}

func (a *API) simulateOrderBehavior(action string, deliveryId int64, mtPeiSongId string) (err error) {
	params := map[string]interface{}{
		"delivery_id":   deliveryId,
		"mt_peisong_id": mtPeiSongId,
	}
	_, err = a.AccessAPI("test/order/"+action, params)
	return err
}

func (a *API) SimulateArrange(deliveryId int64, mtPeiSongId string) (err error) {
	return a.simulateOrderBehavior("arrange", deliveryId, mtPeiSongId)
}

func (a *API) SimulatePickup(deliveryId int64, mtPeiSongId string) (err error) {
	return a.simulateOrderBehavior("pickup", deliveryId, mtPeiSongId)
}

func (a *API) SimulateDeliver(deliveryId int64, mtPeiSongId string) (err error) {
	return a.simulateOrderBehavior("deliver", deliveryId, mtPeiSongId)
}

func (a *API) SimulateRearrange(deliveryId int64, mtPeiSongId string) (err error) {
	return a.simulateOrderBehavior("rearrange", deliveryId, mtPeiSongId)
}

func (a *API) SimulateReportException(deliveryId int64, mtPeiSongId string) (err error) {
	return a.simulateOrderBehavior("reportException", deliveryId, mtPeiSongId)
}

//评价骑手
func (a *API) EvaluateRider(deliveryId int64, mtPeiSongId string, score int, commentContent string) (err error) {
	params := map[string]interface{}{
		"delivery_id":     deliveryId,
		"mt_peisong_id":   mtPeiSongId,
		"score":           score,
		"comment_content": commentContent,
	}
	_, err = a.AccessAPI("order/evaluate", params)
	return err
}

// 获取骑手当前位置
// https://peisong.meituan.com/open/doc#section2-9
func (a *API) RiderLocation(deliveryId int64, mtPeiSongId string) (lng, lat int, err error) {
	params := map[string]interface{}{
		"delivery_id":   deliveryId,
		"mt_peisong_id": mtPeiSongId,
	}
	result, err := a.AccessAPI("order/rider/location", params)
	if err == nil {
		lng = int(utils.ForceInterface2Int64(result.Data["lng"]))
		lat = int(utils.ForceInterface2Int64(result.Data["lat"]))
	}
	return lng, lat, err
}

type PreCreateByShopParam struct {
	DeliveryID           int64   `json:"delivery_id"`
	OrderID              string  `json:"order_id"`
	OuterOrderSourceDesc string  `json:"outer_order_source_desc"` //101.美团（外卖&闪购） 102.饿了么 103.京东到家
	OuterOrderSourceNo   string  `json:"outer_order_source_no"`
	ShopID               string  `json:"shop_id"`
	DeliveryServiceCode  int     `json:"delivery_service_code"`
	ReceiverName         string  `json:"receiver_name"`
	ReceiverAddress      string  `json:"receiver_address"`
	ReceiverPhone        string  `json:"receiver_phone"`
	ReceiverLng          int     `json:"receiver_lng"`
	ReceiverLat          int     `json:"receiver_lat"`
	PayTypeCode          int     `json:"pay_type_code"`
	GoodsValue           float64 `json:"goods_value"`
	GoodsWeight          float64 `json:"goods_weight"`
	ExpectedDeliveryTime int64   `json:"expected_delivery_time"`

	// 以下为可选参数

	CoordinateType     int     `json:"coordinate_type"`
	GoodsHeight        float64 `json:"goods_height,omitempty"`
	GoodsWidth         float64 `json:"goods_width,omitempty"`
	GoodsLength        float64 `json:"goods_length,omitempty"`
	GoodsDetail        string  `json:"goods_detail,omitempty"`
	GoodsPickupInfo    string  `json:"goods_pickup_info,omitempty"`
	GoodsDeliveryInfo  string  `json:"goods_delivery_info,omitempty"`
	ExpectedPickupTime int64   `json:"expected_pickup_time,omitempty"` // 期望取货时间，时区为GMT+8，当前距离Epoch（1970年1月1日) 以秒计算的时间，即unix-timestamp。
	// 期望送达时间，时区为GMT+8，当前距离Epoch（1970年1月1日) 以秒计算的时间，即unix-timestamp
	// 即时单：以发单时间 + 服务包时效作为期望送达时间（当天送服务包需客户指定期望送达时间）
	// 预约单：以客户传参数据为准（预约时间必须大于当前下单时间+服务包时效+3分钟）
	OrderType      int     `json:"order_type,omitempty"`
	PoiSeq         string  `json:"poi_seq,omitempty"`
	Note           string  `json:"note,omitempty"`
	CashOnDelivery float64 `json:"cash_on_delivery,omitempty"`
	CashOnPickup   float64 `json:"cash_on_pickup,omitempty"`
	InvoiceTitle   string  `json:"invoice_title,omitempty"`
}

// 美团预发单
func (a *API) PreCreateByShop(basicParams *PreCreateByShopParam) (deliveryFee, baseDeliveryFee int64, err error) {
	params := utils.Struct2MapByJson(basicParams)
	result, err := a.AccessAPI("order/preCreateByShop", params)
	if err != nil {
		return 0, 0, err
	}

	deliveryFee = utils.Float64TwoInt64(utils.MustInterface2Float64(result.Data["delivery_fee"]) * 100)     // 订单配送价格
	baseDeliveryFee = utils.Float64TwoInt64(utils.MustInterface2Float64(result.Data["delivery_fee"]) * 100) // 订单优惠价格
	//baseDeliveryFee = utils.Float64TwoInt64(utils.MustInterface2Float64(result.Data["base_delivery_fee"]) * 100) // 订单优惠价格
	return deliveryFee, baseDeliveryFee, nil
}

// 预发单
func (a *API) CheckOrder(shopID, receiver_address string, receiver_lng, receiver_lat, mock_order_time int64) (result *ResponseResult, err error) {
	result, err = a.AccessAPI("order/check", map[string]interface{}{
		"shop_id":               shopID,
		"delivery_service_code": DeliveryServiceCodeRapid,
		"receiver_address":      receiver_address,
		"receiver_lng":          receiver_lng,
		"receiver_lat":          receiver_lat,
		"mock_order_time":       mock_order_time,
		"check_type":            1,
	})
	return result, err
}

// 添加小费
func (a *API) AddTip(orderId, deliveryId string, tip int64) error {
	result, err := a.AccessAPI("order/addTip", map[string]interface{}{
		"delivery_id":   orderId,
		"mt_peisong_id": deliveryId,
		"tip_amount":    tip,
		"serial_number": utils.Int64ToStr(time.Now().Unix()),
	})
	if err != nil {
		return err
	}
	if result.Code != 0 {
		return errors.New(result.Message)
	}
	return nil
}

func IsOpen(err error) bool {
	if err == nil {
		return true
	} else if strings.Contains(err.Error(), "门店非营业状态") {
		return false
	}
	return false
}

func IsExist(err error) bool {
	if err == nil {
		return true
	} else if strings.Contains(err.Error(), "门店不存在") {
		return false
	}
	return false
}
