package card_redeem_order

import (
	"context"
	"fmt"
	"kami/internal/consts"
	"kami/internal/dao"
	"kami/internal/model"
	"kami/internal/model/entity"
	"kami/internal/service"
	"kami/utility/config"
	"kami/utility/integration/redeem"
	redeem2 "kami/utility/integration/redeem/client"
	"kami/utility/pool"
	"kami/utility/utils"
	"time"

	"github.com/gogf/gf/v2/net/gtrace"
	"github.com/gogf/gf/v2/os/gmlock"
	"github.com/gogf/gf/v2/util/gconv"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"

	"github.com/duke-git/lancet/v2/pointer"
	"github.com/duke-git/lancet/v2/slice"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/os/glog"
)

// GetEarlyWaitingOrder 查找等待核销的订单
func (h *sCardRedeemOrder) GetEarlyWaitingOrder(ctx context.Context, category consts.CardRedeemAccountCategory, tx gdb.TX) (data *entity.V1CardRedeemOrderInfo, err error) {
	m := dao.V1CardRedeemOrderInfo.Ctx(ctx).DB(config.GetDatabaseV1())
	if tx != nil {
		m = m.TX(tx)
	}

	m = m.Where(dao.V1CardRedeemOrderInfo.Columns().Status, consts.RedeemOrderPending).
		Where(dao.V1CardRedeemOrderInfo.Columns().Category, category)

	count, err := m.Count()
	if err != nil {
		return
	}
	for range count {
		data = &entity.V1CardRedeemOrderInfo{}
		err = m.Scan(&data)
		if !pointer.IsNil(data) && data.OrderNo != "" {
			if data.CallbackCount <= 5 {
				break
			}
			_ = h.UpdateStatus(ctx, data.OrderNo, consts.RedeemOrderForbiddenByMaxLimit, "调度次数超过限制", tx)
		}
	}
	return data, utils.HandleNoRowsError(err)
}

// GetOrderCount 查询当前待充值订单数量
func (h *sCardRedeemOrder) GetOrderCount(ctx context.Context, status consts.RedeemOrderStatus) (count int, err error) {
	count = 0
	count, err = dao.V1CardRedeemOrderInfo.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1CardRedeemOrderInfo.Columns().Status, status).Count()
	return count, utils.HandleNoRowsError(err)
}

const (
	LockKey = "card_redeem_order_consume"
)

func (h *sCardRedeemOrder) ConsumeOne(ctx context.Context, category consts.CardRedeemAccountCategory) (err error) {
	ctx, span := gtrace.NewSpan(ctx, "核销订单")
	defer span.End()
	orderInfo := &entity.V1CardRedeemOrderInfo{}
	cookieInfo := &entity.V1CardRedeemAccountInfo{}
	isOrderNormal := false
	gmlock.LockFunc(LockKey, func() {
		//判断是否解封，触发解封账号
		service.CardRedeemAccount().CheckForbiddenByTooFast(ctx)
		err = config.GetDatabaseV1().Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			// 判断当前是否有账号可用
			// 查找有无合适的订单
			orderInfo, err = h.GetEarlyWaitingOrder(ctx, category, tx)
			if err != nil || pointer.IsNil(orderInfo) || orderInfo.OrderNo == "" {
				return nil
			}
			//判断卡号是否符合规则
			if ok, remark := h.isValidOrder(ctx, orderInfo); !ok {
				if err2 := h.ProcessOrderCallback(ctx, &model.RedeemCardOrderAddHistory{
					Amount:          0,
					OrderNo:         orderInfo.OrderNo,
					Remark:          fmt.Sprintf("订单号：%s %s", orderInfo.Attach, remark),
					CardAmount:      orderInfo.OrderAmount,
					ResponseRawData: "",
					AccountUsername: "",
					AccountNickname: "",
				}, consts.RedeemOrderFailRuleError, tx); err2 != nil {
					glog.Error(ctx, "处理失败", err2)
					return err2
				}
				return nil
			}

			//开始处理订单
			if err = h.StartProcessOrder(ctx, orderInfo, tx); err != nil {
				glog.Error(ctx, "开始处理订单失败", err)
				return err
			}

			//判断是否是金额异议二次进入的订单(成功)
			if ok := h.isAmountDifferentSucceed(ctx, orderInfo, tx); ok {
				if err2 := h.ProcessOrderCallback(ctx, &model.RedeemCardOrderAddHistory{
					Amount:          0,
					OrderNo:         orderInfo.OrderNo,
					Remark:          fmt.Sprintf("订单号：%s", orderInfo.Attach),
					CardAmount:      orderInfo.OrderAmount,
					ResponseRawData: "",
					AccountUsername: "",
					AccountNickname: "",
				}, consts.RedeemOrderSuccessWithCompensated, tx); err2 != nil {
					glog.Error(ctx, "处理失败", err2)
					return err2
				}
				return nil
			}

			//判断是否是金额异议二次进入的订单(失败)
			if ok := h.isAmountDifferentFail(ctx, orderInfo, tx); !ok {
				if !service.SysConfigDict().GetIsAllowedRepeated(ctx, category) {
					if ok2 := h.IsCardPassRepeated(ctx, orderInfo.CardNo, orderInfo.GiftCardPwd, tx); ok2 {
						if err2 := h.ProcessOrderCallback(ctx, &model.RedeemCardOrderAddHistory{
							Amount:          0,
							OrderNo:         orderInfo.OrderNo,
							Remark:          fmt.Sprintf("订单号：%s", orderInfo.Attach),
							CardAmount:      orderInfo.OrderAmount,
							ResponseRawData: "",
							AccountUsername: "",
							AccountNickname: "",
						}, consts.RedeemOrderFailRepeated, tx); err2 != nil {
							glog.Error(ctx, "处理失败", err2)
							return err2
						}
						return nil
					}
				}
			}

			cookieInfo, err = service.CardRedeemAccount().GetAvailableAccount(ctx, consts.CardRedeemAccountCategory(orderInfo.Category), orderInfo.ActualAmount, tx)
			if err != nil || pointer.IsNil(cookieInfo) || cookieInfo.Id == "" {
				if err != nil {
					glog.Error(ctx, "没有合适的账号", err)
				}
				return gerror.New("没有合适的账号")
			}

			// 分配账号给订单
			if err = h.AllocateAccountToOrder(ctx, cookieInfo, orderInfo, tx); err != nil {
				glog.Error(ctx, "分配账号失败", err)
				return err
			}

			//预扣款
			if err = service.CardRedeemAccount().ReduceAmountWithReduction(ctx, &model.RedeemAccountAddHistory{
				AccountId:       cookieInfo.Id,
				AccountName:     cookieInfo.Name,
				Amount:          orderInfo.OrderAmount,
				OrderNo:         orderInfo.OrderNo,
				Remark:          "",
				Cookie:          "",
				Category:        consts.CardRedeemAccountCategory(cookieInfo.Category),
				OperationStatus: consts.RedeemAccountOperationDeduct,
			}, tx); err != nil {
				glog.Error(ctx, "预扣款失败", err)
				return err
			}

			isOrderNormal = true
			return nil
		})
		if err != nil {
			return
		}
	})

	if pointer.IsNil(orderInfo) || orderInfo.OrderNo == "" {
		glog.Info(ctx, "没有合适的订单", orderInfo)
		return
	}

	if isOrderNormal {
		glog.Info(ctx, "开始消费订单", orderInfo.OrderNo, "账号", cookieInfo.Id)
		client, err2 := redeem2.GetClient(consts.CardRedeemAccountCategory(orderInfo.Category))
		callBackData := &redeem.BindCardOutput{
			Amount:  0,
			Status:  consts.RedeemOrderFail,
			RawData: "",
		}
		isDifferentAllow, _ := service.SysConfigDict().GetIsAllowRedeemCardDifferentAmount(ctx, consts.CardRedeemAccountCategory(cookieInfo.Category))
		result, err3 := client.BindCard(ctx, &redeem.CommonSendCardReq{
			GiftCardPwd:            orderInfo.GiftCardPwd,
			OrderNo:                orderInfo.OrderNo,
			CardNo:                 orderInfo.CardNo,
			Cookie:                 cookieInfo.Cookie,
			OrderAmount:            orderInfo.OrderAmount,
			IsAllowDifferentAmount: isDifferentAllow,
		})
		if err3 != nil {
			result.Status = consts.RedeemOrderFailWithCardUnknown
			glog.Error(ctx, "发送卡密失败", err3)
		}
		if result.Status == consts.RedeemOrderSuccess {
			_ = h.UpdateCardNoAndCardType(ctx, orderInfo.OrderNo, orderInfo.CardNo, result.CardTypeName, nil)
		}

		callBackData = result
		err2 = config.GetDatabaseV1().Ctx(ctx).Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
			if err2 = h.ProcessOrderCallback(ctx, &model.RedeemCardOrderAddHistory{
				AccountId:       cookieInfo.Id,
				AccountName:     cookieInfo.Name,
				Amount:          callBackData.Amount,
				OrderNo:         orderInfo.OrderNo,
				Remark:          fmt.Sprintf("订单号：%s %s", orderInfo.Attach, callBackData.Remarks), // 前端返回数据
				CardAmount:      orderInfo.OrderAmount,
				ResponseRawData: callBackData.RawData,
				AccountUsername: cookieInfo.Username,
				AccountNickname: cookieInfo.Nickname,
			}, callBackData.Status, tx); err2 != nil {
				return err2
			}

			//处理退款相关
			err2 = h.ProcessOrderReduction(ctx, orderInfo.OrderNo, tx)
			return err2
		})
		if err2 != nil {
			glog.Error(ctx, "订单编号："+orderInfo.OrderNo, err2)
		}
	}
	// 核销订单
	if err3 := h.CallBackOrderToUpstream(ctx, orderInfo.OrderNo, false); err3 != nil {
		glog.Error(ctx, "回调上游失败", err3)
	}
	return
}

// TriggerValidateAndConsume 触发器，被动验证订单，如果订单有效，就消费订单
func (h *sCardRedeemOrder) TriggerValidateAndConsume() (err error) {
	tracer := gtrace.NewTracer("核销触发器")
	ctx, span := tracer.Start(context.Background(), "核销触发器")
	defer span.End()
	if err = h.TriggerConsumeWithContext(ctx); err != nil {
		glog.Error(ctx, "触发器执行失败", err)
	}
	return
}

func (h *sCardRedeemOrder) TriggerConsumeWithContext(ctx context.Context) (err error) {

	poolKey := pool.RedeemCardConsume

	ctx, span := gtrace.NewTracer("核销触发器").Start(ctx, "核销触发器", trace.WithNewRoot(), trace.WithTimestamp(time.Now()))
	defer span.End()

	for _, category := range consts.ChannelTypeList {

		orderInfo, err2 := h.GetEarlyWaitingOrder(ctx, category, nil)
		if err2 != nil {
			continue
		}
		if pointer.IsNil(orderInfo) || orderInfo.OrderNo == "" {
			continue
		}

		glog.Info(ctx, fmt.Sprintf("核销触发器：%s，订单号：%s", category, orderInfo.OrderNo))

		if err3 := pool.New(poolKey.Key(string(category)), 30).Add(ctx, func(ctx2 context.Context) {

			glog.Info(ctx2, fmt.Sprintf("核销触发器：%s，订单号：%s", category, orderInfo.OrderNo))

			gtrace.NewSpan(ctx2, fmt.Sprintf("核销触发器：%s", category), trace.WithAttributes(
				attribute.String("category", gconv.String(category)),
				attribute.String("orderNo", orderInfo.OrderNo),
			),
			)

			if err4 := h.ConsumeOne(ctx2, category); err4 != nil && err4.Error() != "没有合适的账号" {
				glog.Error(ctx, "消费订单失败", err4)
			}
		}); err3 != nil {
			glog.Error(ctx, "触发器执行失败", err)
		}
	}
	return
}

// 额外流程处理
func (h *sCardRedeemOrder) isValidOrder(ctx context.Context, orderInfo *entity.V1CardRedeemOrderInfo) (ok bool, remark string) {
	//	当前订单是不是重复
	client, err2 := redeem2.GetClient(consts.CardRedeemAccountCategory(orderInfo.Category))
	if err2 != nil || pointer.IsNil(client) {
		glog.Error(ctx, "获取客户端失败", err2)
		return false, ""
	}
	output := client.CheckCard(ctx, &redeem.CommonCardTrimAndCheckerInput{
		CardNo:      orderInfo.CardNo,
		GiftCardPwd: orderInfo.GiftCardPwd,
	})
	if !output.IsOk {
		return false, "卡号不符合规则"
	}
	if !client.CheckAllowedAmount(ctx, orderInfo.OrderAmount) {
		return false, fmt.Sprintf("管理员设置只充值100元，实际充值%.2f元", orderInfo.OrderAmount)
	}
	return true, ""
}

// 金额异议/成功
func (h *sCardRedeemOrder) isAmountDifferentSucceed(ctx context.Context, orderInfo *entity.V1CardRedeemOrderInfo, tx gdb.TX) bool {
	readyOrderInfos := make([]*entity.V1CardRedeemOrderInfo, 0)
	m := dao.V1CardRedeemOrderInfo.Ctx(ctx).DB(config.GetDatabaseV1())
	if tx != nil {
		m = m.TX(tx)
	}
	//	根据卡号和卡密查询所有订单
	err := m.Where(dao.V1CardRedeemOrderInfo.Columns().CardNo, orderInfo.CardNo).
		Where(dao.V1CardRedeemOrderInfo.Columns().GiftCardPwd, orderInfo.GiftCardPwd).Scan(&readyOrderInfos)
	err = utils.HandleNoRowsError(err)
	//存在卡片异议的订单，并且新进的订单金额跟金额异议订单金额一直
	isDifferent := slice.ContainBy(readyOrderInfos, func(item *entity.V1CardRedeemOrderInfo) bool {
		return consts.RedeemOrderStatus(item.OrderStatus) == consts.RedeemOrderSuccessWithDifferentAmount && item.ActualAmount == orderInfo.OrderAmount
	})
	//判断是不是存在补卡记录
	isRepeated := slice.ContainBy(readyOrderInfos, func(item *entity.V1CardRedeemOrderInfo) bool {
		return consts.RedeemOrderStatus(item.OrderStatus) == consts.RedeemOrderSuccessWithCompensated
	})
	return isDifferent && !isRepeated
}

// 金额异议/成功
func (h *sCardRedeemOrder) isAmountDifferentFail(ctx context.Context, orderInfo *entity.V1CardRedeemOrderInfo, tx gdb.TX) bool {
	readyOrderInfos := make([]*entity.V1CardRedeemOrderInfo, 0)
	m := dao.V1CardRedeemOrderInfo.Ctx(ctx).DB(config.GetDatabaseV1())
	if tx != nil {
		m = m.TX(tx)
	}
	//	根据卡号和卡密查询所有订单
	err := m.Where(dao.V1CardRedeemOrderInfo.Columns().CardNo, orderInfo.CardNo).Where(dao.V1CardRedeemOrderInfo.Columns().Category, orderInfo.Category).
		Where(dao.V1CardRedeemOrderInfo.Columns().GiftCardPwd, orderInfo.GiftCardPwd).Scan(&readyOrderInfos)
	err = utils.HandleNoRowsError(err)
	//存在卡片异议的订单，并且新进的订单金额跟金额异议订单金额一致
	isDifferent := slice.ContainBy(readyOrderInfos, func(item *entity.V1CardRedeemOrderInfo) bool {
		return consts.RedeemOrderStatus(item.OrderStatus) == consts.RedeemOrderFailWithDifferentAmount && item.ActualAmount == orderInfo.OrderAmount
	})
	//判断是不是存在补卡记录
	isRepeated := slice.ContainBy(readyOrderInfos, func(item *entity.V1CardRedeemOrderInfo) bool {
		return consts.RedeemOrderStatus(item.OrderStatus) == consts.RedeemOrderSuccessWithCompensated
	})
	return isDifferent && !isRepeated
}
