package card_redeem_order

import (
	"context"
	"fmt"
	"kami/internal/consts"
	"kami/internal/dao"
	"kami/internal/model"
	"kami/internal/model/do"
	"kami/internal/model/entity"
	"kami/internal/service"
	"kami/utility/config"

	"github.com/duke-git/lancet/v2/pointer"
	"github.com/duke-git/lancet/v2/slice"
	"github.com/duke-git/lancet/v2/strutil"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/net/gtrace"
	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/util/gconv"
	"go.opentelemetry.io/otel/attribute"
	"go.opentelemetry.io/otel/trace"
)

// StartProcessOrder 开始受理订单
func (h *sCardRedeemOrder) StartProcessOrder(ctx context.Context, orderInfo *entity.V1CardRedeemOrderInfo, tx gdb.TX) (err error) {
	m := dao.V1CardRedeemOrderInfo.Ctx(ctx).DB(config.GetDatabaseV1())
	if tx != nil {
		m = m.TX(tx)
	}
	result, err := m.Where(dao.V1CardRedeemOrderInfo.Columns().OrderNo, orderInfo.OrderNo).
		Where(dao.V1CardRedeemOrderInfo.Columns().Status, consts.RedeemOrderPending).Update(do.V1CardRedeemOrderInfo{
		Status: consts.RedeemOrderStart,
	})
	if err != nil || pointer.IsNil(result) {
		_ = h.AddHistory(ctx, &model.RedeemCardOrderAddHistory{
			Amount:          0,
			OrderNo:         orderInfo.OrderNo,
			Remark:          fmt.Sprintf("开始处理订单失败"),
			OperationStatus: consts.RedeemOrderOperationFailWithServerError,
			AccountUsername: "",
			AccountNickname: "",
		}, tx)
	}
	row, _ := result.RowsAffected()
	if row == 0 {
		_ = h.AddHistory(ctx, &model.RedeemCardOrderAddHistory{
			Amount:          0,
			OrderNo:         orderInfo.OrderNo,
			Remark:          fmt.Sprintf("当前订单状态不是待处理"),
			OperationStatus: consts.RedeemOrderOperationFailWithServerError,
			AccountUsername: "",
			AccountNickname: "",
		}, tx)
	}
	_ = h.AddHistory(ctx, &model.RedeemCardOrderAddHistory{
		Amount:          0,
		OrderNo:         orderInfo.OrderNo,
		Remark:          fmt.Sprintf("订单号：%s，卡号：%s，卡密：%s", orderInfo.Attach, orderInfo.CardNo, orderInfo.GiftCardPwd),
		OperationStatus: consts.RedeemOrderOperationStart,
		AccountUsername: "",
		AccountNickname: "",
	}, tx)
	return
}

// AllocateAccountToOrder 分配账号给订单
func (h *sCardRedeemOrder) AllocateAccountToOrder(ctx context.Context, accountInfo *entity.V1CardRedeemAccountInfo, orderInfo *entity.V1CardRedeemOrderInfo, tx gdb.TX) (err error) {
	ctx, span := gtrace.NewSpan(ctx, "分配订单", trace.WithAttributes(attribute.String("订单信息", gconv.String(orderInfo)), attribute.String("账号信息", gconv.String(accountInfo))))
	defer span.End()
	glog.Info(ctx, "分配账号给订单", "账户id", accountInfo.Id, "订单id", orderInfo.OrderNo)

	m := dao.V1CardRedeemOrderInfo.Ctx(ctx).DB(config.GetDatabaseV1())
	if tx != nil {
		m = m.TX(tx)
	}
	//添加一次分配次数
	_, err = m.Where(dao.V1CardRedeemOrderInfo.Columns().OrderNo, orderInfo.OrderNo).Increment(dao.V1CardRedeemOrderInfo.Columns().CallbackCount, 1)
	_, err = m.Where(dao.V1CardRedeemOrderInfo.Columns().OrderNo, orderInfo.OrderNo).
		Update(do.V1CardRedeemOrderInfo{
			AccountId:     accountInfo.Id,
			CreatedUserId: accountInfo.CreateUserId,
			AccountName:   accountInfo.Name,
			Status:        consts.RedeemOrderProcessing,
		})
	if err != nil {
		_ = h.AddHistory(ctx, &model.RedeemCardOrderAddHistory{
			AccountId:       accountInfo.Id,
			AccountName:     accountInfo.Name,
			Amount:          orderInfo.OrderAmount,
			OrderNo:         orderInfo.OrderNo,
			Remark:          fmt.Sprintf("分配账号：%s 失败", accountInfo.Name),
			OperationStatus: consts.RedeemOrderOperationPendingFail,
			AccountUsername: accountInfo.Username,
			AccountNickname: accountInfo.Nickname,
		}, tx)
		return
	}
	//err = service.SysUserPayment().Deduction(ctx, &model.DeductionInput{
	//	OrderNo:       orderInfo.OrderNo,
	//	OrderCategory: consts.CardRedeemAccountCategory(orderInfo.Category),
	//	UserId:        accountInfo.Id,
	//	Balance:       orderInfo.OrderAmount,
	//}, tx)
	//if err != nil {
	//	return
	//}
	_ = h.AddHistory(ctx, &model.RedeemCardOrderAddHistory{
		AccountId:       accountInfo.Id,
		AccountName:     accountInfo.Name,
		Amount:          orderInfo.OrderAmount,
		OrderNo:         orderInfo.OrderNo,
		Remark:          fmt.Sprintf("分配账号：%s 成功，ck：%s", accountInfo.Name, strutil.HideString(accountInfo.Cookie, 4, strutil.WordCount(accountInfo.Cookie)-4, "*")),
		OperationStatus: consts.RedeemOrderOperationPending,
		AccountUsername: accountInfo.Username,
		AccountNickname: accountInfo.Nickname,
	}, tx)
	return
}

// ProcessOrderCallback 操作处理后的订单
func (h *sCardRedeemOrder) ProcessOrderCallback(ctx context.Context, input *model.RedeemCardOrderAddHistory, status consts.RedeemOrderStatus, tx gdb.TX) (err error) {
	var orderStatus consts.RedeemOrderStatus
	var orderOperationStatus consts.RedeemOrderOperationStatus
	operationHistoryRemark := ""

	if tx == nil {
		tx, _ = config.GetDatabaseV1().Begin(ctx)
		defer func(tx gdb.TX) {
			if err != nil {
				_ = tx.Rollback()
				return
			}
			_ = tx.Commit()
		}(tx)
	}

	if input.Amount > 0 {
		//更新订单实际金额
		m := dao.V1CardRedeemOrderInfo.Ctx(ctx).DB(config.GetDatabaseV1())
		if tx != nil {
			m = m.TX(tx)
		}
		_, err = m.Where(dao.V1CardRedeemOrderInfo.Columns().OrderNo, input.OrderNo).Update(do.V1CardRedeemOrderInfo{
			ActualAmount: input.Amount,
		})
		if err != nil {
			glog.Error(ctx, "更新实际金额失败", err)
			return
		}
	}

	switch status {
	case consts.RedeemOrderSuccess, consts.RedeemOrderSuccessWithDifferentAmount:
		orderStatus = consts.RedeemOrderSuccess
		orderOperationStatus = consts.RedeemOrderOperationSuccess
		if input.CardAmount != input.Amount {
			//更新原本的订单状态
			// TODO: 金额异议强行改为失败
			orderStatus = consts.RedeemOrderFail
			status = consts.RedeemOrderSuccessWithDifferentAmount
			operationHistoryRemark = fmt.Sprintf("充值成功(充值金额与订单金额不一致)，卡面金额：%.2f，实际充值金额：%.2f", input.CardAmount, input.Amount)
			input.Remark = operationHistoryRemark
		} else {
			input.Remark = ""
			operationHistoryRemark = fmt.Sprintf("充值成功，卡面金额：%.2f，实际充值金额：%.2f", input.CardAmount, input.Amount)
		}
	case consts.RedeemOrderSuccessWithCompensated:
		orderStatus = consts.RedeemOrderSuccess
		orderOperationStatus = consts.RedeemOrderOperationSuccess
		operationHistoryRemark = fmt.Sprintf("补卡充值成功(补卡充值金额与订单金额不一致)，补卡金额：%.2f，实际金额：%.2f", input.CardAmount, input.Amount)
	case consts.RedeemOrderFail, consts.RedeemOrderFailWithWrongCardType, consts.RedeemOrderFailWithDifferentAmount,
		consts.RedeemOrderFailWithOtherBind, consts.RedeemOrderFailWithRepeat, consts.RedeemOrderFailCardNotExist,
		consts.RedeemOrderFailWithCardOverDue, consts.RedeemOrderFailRuleError,
		consts.RedeemOrderFailRepeated, consts.RedeemOrderFailWithFrozen:
		//	订单相关错误

		orderStatus = consts.RedeemOrderFail
		orderOperationStatus = consts.RedeemOrderOperationFail
		switch status {
		case consts.RedeemOrderFailWithWrongCardType:
			operationHistoryRemark = "失败原因：【CK类型错误】"
		case consts.RedeemOrderFailWithDifferentAmount:
			operationHistoryRemark = "失败原因：【暂停回调(订单金额与卡片金额不一致（金额异议）)】"
		case consts.RedeemOrderFailWithOtherBind:
			operationHistoryRemark = "失败原因：【账号绑定其他CK】"
		case consts.RedeemOrderFailWithRepeat:
			operationHistoryRemark = "失败原因：【账号重复】"
		case consts.RedeemOrderFailCardNotExist:
			operationHistoryRemark = "失败原因：【卡片不存在】"
		case consts.RedeemOrderFailWithCardOverDue:
			operationHistoryRemark = "失败原因：【卡片已过期】"
		case consts.RedeemOrderFailRuleError:
			operationHistoryRemark = "失败原因：【卡号或卡密错误】"
		case consts.RedeemOrderFailRepeated:
			operationHistoryRemark = "失败原因：【订单重复】"
		case consts.RedeemOrderFailWithFrozen:
			operationHistoryRemark = "失败原因：【卡密冻结】"
		}
	case consts.RedeemOrderFailWithCKFail, consts.RedeemOrderFailWithDailyAccountLimited, consts.RedeemOrderFailWithFrequent:
		//账号相关失败错误

		//这三个是账号导致的失败，属于临时状态
		//_, _ = dao.V1CardRedeemOrderInfo.Ctx(ctx).DB(config.GetDatabaseV1()).Where(dao.V1CardRedeemOrderInfo.Columns().OrderNo, input.OrderNo).TX(tx).
		//	Decrement(dao.V1CardRedeemOrderInfo.Columns().CallbackCount, 1)
		accountStatus := consts.RedeemAccountForbidden
		operationHistoryRemark = "Ck验证失败，切换CK"
		if status == consts.RedeemOrderFailWithDailyAccountLimited {
			accountStatus = consts.RedeemAccountForbiddenDaily
			operationHistoryRemark = "账号今日充值次数已满，冷却当前CK并切换"
		}
		if status == consts.RedeemOrderFailWithFrequent {
			accountStatus = consts.RedeemAccountForbiddenByTooFast
			operationHistoryRemark = "账号充值过快，冷却当前CK并切换"
		}
		if err2 := service.CardRedeemAccount().UpdateStatus(ctx, input.AccountId, accountStatus, tx); err2 != nil {
			glog.Error(ctx, "更新账号状态失败", err2)
		}
		orderStatus = consts.RedeemOrderPending
		orderOperationStatus = consts.RedeemOrderOperationRefund
	case consts.RedeemOrderRefundWithValidatedFail:
		orderStatus = consts.RedeemOrderPending
		orderOperationStatus = consts.RedeemOrderOperationRefund
		operationHistoryRemark = "充值验证失败，退回重试~"

	case consts.RedeemOrderFailWithCardUnknown:
		//未知错误
		//if err2 := service.CardRedeemAccount().UpdateStatus(ctx, input.AccountId, consts.RedeemAccountForbidden, tx); err2 != nil {
		//	glog.Error(ctx, "更新账号状态失败", err2)
		//}
		//orderStatus = consts.RedeemOrderFailWithCardUnknown
		//orderOperationStatus = consts.RedeemOrderOperationUnknown
		orderStatus = consts.RedeemOrderPending
		orderOperationStatus = consts.RedeemOrderOperationRefund
		operationHistoryRemark = "充值验证失败，退回重试~"
		//退回重试
	default:
		//默认是错误
		orderStatus = consts.RedeemOrderPending
		orderOperationStatus = consts.RedeemOrderOperationRefund
		operationHistoryRemark = "订单未知状态，退回重试~"
		//退回重试
	}

	//	更新订单状态
	if err = h.UpdateStatus(ctx, input.OrderNo, orderStatus, fmt.Sprintf("%s %s", input.Remark, operationHistoryRemark), tx); err != nil {
		glog.Error(ctx, "更新订单状态失败", err)
		return
	}

	//只有处在最终状态的订单，才需要更新订单明细
	if slice.Contain([]consts.RedeemOrderStatus{consts.RedeemOrderSuccess, consts.RedeemOrderFail}, orderStatus) {
		//	更新订单原本状态
		if err = h.UpdateOrderStatus(ctx, input.OrderNo, status, tx); err != nil {
			glog.Error(ctx, "更新订单状态失败", err)
			return
		}
	}

	//更新操作历史
	_ = h.AddHistory(ctx, &model.RedeemCardOrderAddHistory{
		AccountId:       input.AccountId,
		AccountName:     input.AccountName,
		Amount:          input.Amount,
		OrderNo:         input.OrderNo,
		Remark:          operationHistoryRemark + "\t【原始数据】" + input.Remark,
		ResponseRawData: input.ResponseRawData,
		OperationStatus: orderOperationStatus,
		AccountUsername: input.AccountUsername,
		AccountNickname: input.AccountNickname,
	}, tx)
	return
}

func (h *sCardRedeemOrder) ProcessOrderReduction(ctx context.Context, orderNo string, tx gdb.TX) (err error) {
	orderInfo, err := h.GetOrderOneByOrderNo(ctx, orderNo, tx)
	if pointer.IsNil(orderInfo) || orderInfo.OrderNo == "" {
		return
	}

	if consts.RedeemOrderStatus(orderInfo.Status) == consts.RedeemOrderSuccess {
		//订单成功，要向核销扣款
		err = service.CardRedeemAccount().ReduceAmountWithReductionSucceed(ctx, &model.RedeemAccountReductionSucceed{
			OrderNo:   orderInfo.OrderNo,
			AccountId: orderInfo.AccountId,
			Amount:    orderInfo.ActualAmount,
			Remark:    "充值成功",
			Category:  consts.CardRedeemAccountCategory(orderInfo.Category),
		}, tx)
		if err != nil {
			glog.Error(ctx, "扣款成功，但退款失败", err)
			return err
		}
	}

	if consts.RedeemOrderStatus(orderInfo.OrderStatus) == consts.RedeemOrderSuccessWithDifferentAmount {
		err = service.CardRedeemAccount().ReducePartialAmountWithFail(ctx, &model.RedeemAccountReductionSucceed{
			OrderNo:   orderInfo.OrderNo,
			AccountId: orderInfo.AccountId,
			Amount:    orderInfo.OrderAmount - orderInfo.ActualAmount,
			Remark:    "部分退款(金额异议)",
			Category:  consts.CardRedeemAccountCategory(orderInfo.Category),
		}, tx)
		if err != nil {
			glog.Error(ctx, "扣款成功，但退款失败", err)
			return err
		}
	}

	if consts.RedeemOrderStatus(orderInfo.OrderStatus) != consts.RedeemOrderSuccessWithDifferentAmount &&
		(consts.RedeemOrderStatus(orderInfo.Status) == consts.RedeemOrderFail ||
			consts.RedeemOrderStatus(orderInfo.Status) == consts.RedeemOrderFailWithCardUnknown ||
			consts.RedeemOrderStatus(orderInfo.Status) == consts.RedeemOrderPending) {
		// 退款
		err = service.CardRedeemAccount().ReduceAmountWithReductionFail(ctx, &model.RedeemAccountReductionSucceed{
			OrderNo:   orderInfo.OrderNo,
			AccountId: orderInfo.AccountId,
			Amount:    orderInfo.OrderAmount,
			Remark:    "充值失败退款",
			Category:  consts.CardRedeemAccountCategory(orderInfo.Category),
		}, tx)
		if err != nil {
			glog.Error(ctx, "扣款成功，但退款失败", err)
			return err
		}
	}
	return
}
