package thirdpay_notify

import (
	"PaymentCenter/app/constants/common"
	"PaymentCenter/app/constants/errorcode"
	"PaymentCenter/app/data"
	"PaymentCenter/app/http/entities"
	"PaymentCenter/app/models/appmodel"
	"PaymentCenter/app/models/ordercallbacklogmodel"
	"PaymentCenter/app/models/ordersmodel"
	"PaymentCenter/app/services"
	"PaymentCenter/app/utils"
	"PaymentCenter/app/utils/httpclient"
	"github.com/bytedance/sonic"
	"time"
	"xorm.io/builder"
)

type OrderNotify struct {
	order *ordersmodel.Orders
	Code  int
	app   *appmodel.App

	OrderId      int64
	Status       int //订单状态
	ActualAmount int
	Msg          string
	CompleteTime time.Time
}

type OrderNotifyResp struct {
	OrderId  int64
	Send     bool
	ErrCode  int
	SendTime time.Time
	Content  *OrderNotify
}

type OrderNotifySendContent struct {
	OrderId      int64     `json:"order_id"`
	OutTradeNo   string    `json:"out_trade_no"`
	CompleteTime time.Time `json:"complete_time"`
	OrderType    int       `json:"order-type"`
	Status       int       `json:"status"`
	Msg          string    `json:"msg"`
	ErrCode      int       `json:"err_code"`
	AppId        int64     `json:"app_id"`
	ChannelId    int64     `json:"channel_id"`
	MerchantId   int64     `json:"merchant_id"`
}

func NewOrderNotifyWithHandle(orderId int64, Status int, actualAmount int, msg string) *OrderNotifyResp {
	orderNotify := &OrderNotify{
		OrderId:      orderId,
		Status:       Status,
		ActualAmount: actualAmount,
		Msg:          msg,
		Code:         errorcode.Success,
	}
	return orderNotify.Handle()
}

func (o *OrderNotify) NotifyRespFail(errorCode int) *OrderNotifyResp {
	return &OrderNotifyResp{
		OrderId: o.OrderId,
		Send:    false,
		ErrCode: errorCode,
		Content: o,
	}
}

func (o *OrderNotify) Handle() (res *OrderNotifyResp) {
	o.checkOrder()
	if o.Code != errorcode.Success {
		return o.NotifyRespFail(o.Code)
	}
	o.checkApp()
	if o.Code != errorcode.Success {
		return o.NotifyRespFail(o.Code)
	}
	o.updateOrder()
	if o.Code != errorcode.Success {
		return o.NotifyRespFail(o.Code)
	}
	o.sendNotify(o.setBody())
	if o.Code != errorcode.Success {
		return o.NotifyRespFail(o.Code)
	}
	return &OrderNotifyResp{
		OrderId: o.OrderId,
		Send:    true,
		ErrCode: o.Code,
		Content: o,
	}
}

// 发送下游回调通知
func (o *OrderNotify) sendNotify(body *OrderNotifySendContent) {
	if o.app.NotifyUrl == "" {
		o.Code = errorcode.AppNotifyUrlNotFound
		return
	}
	var callbackStatus = common.STATUS_ENABLE
	var response string

	bodyByte, _ := sonic.Marshal(&body)
	headers := make(map[string]string, 1)
	headers["Content-Type"] = "application/json"
	resByte, err := httpclient.FastHttpPost(o.app.NotifyUrl, headers, bodyByte, 0)
	if err != nil || string(resByte) != "success" {
		o.Code = errorcode.NotifySendFail
		callbackStatus = common.STATUS_DISABLED
		response = "url=" + o.app.NotifyUrl + "|error=" + err.Error()
	}
	response = string(resByte) + response
	// 记录回调日志
	go func(orderId int64, status int, request, response string) {
		repo := data.NewOrderCallbackLogRepo(ordercallbacklogmodel.GetInstance().GetDb())
		log := ordercallbacklogmodel.OrderCallbackLog{
			OrderId:          orderId,
			MerchantRequest:  string(bodyByte),
			Status:           status,
			MerchantResponse: response,
		}
		_, insertErr := repo.OrderCallbackLogInsertOne(&log)
		if insertErr != nil {
			utils.Log(nil, "回调写入日志error", insertErr)
		}
	}(o.OrderId, callbackStatus, string(bodyByte), response)
	return
}

func (o *OrderNotify) setBody() *OrderNotifySendContent {
	return &OrderNotifySendContent{
		OrderId:      o.OrderId,
		OutTradeNo:   o.order.OutTradeNo,
		CompleteTime: o.CompleteTime,
		Status:       o.order.Status,
		OrderType:    o.order.OrderType,
		Msg:          o.Msg,
		ErrCode:      o.Status,
		AppId:        o.order.AppId,
		ChannelId:    o.order.PayChannelId,
		MerchantId:   o.order.MerchantId,
	}
}

func (o *OrderNotify) updateOrder() {
	if _, ok := common.OrderStatusMap[o.Status]; !ok {
		o.Code = errorcode.OrderStatusErr
		return
	}
	o.order.Status = o.Status
	o.order.PayerTotal = o.ActualAmount
	o.Code = services.OrderUpdate(o.order, "status")
	return
}

func (o *OrderNotify) checkApp() {
	o.app, o.Code = services.AppFindOne(entities.IdRequest{Id: o.order.AppId})
	if o.Code != errorcode.Success {
		return
	}

	//if o.app.NotifyUrl == "" {
	//	o.Code = errorcode.AppNotifyUrlNotFound
	//	return
	//}

	return
}

func (o *OrderNotify) checkOrder() {
	cond := builder.NewCond()
	cond = cond.And(builder.Eq{"id": o.OrderId})
	o.order, o.Code = services.OrderFindOne(&ordersmodel.Orders{}, cond)
	if o.Code != errorcode.OrdersExist {
		return
	}

	if o.order.Status != common.ORDER_STATUS_PAYING {
		o.Code = errorcode.OrderStatusErr
		return
	}
	return
}