package com.bonus.canteen.core.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.crypto.digest.DigestUtil;
import com.bonus.canteen.core.account.constants.AccTradeTypeEnum;
import com.bonus.canteen.core.account.constants.AccWalletIdEnum;
import com.bonus.canteen.core.account.domain.AccWalletInfo;
import com.bonus.canteen.core.account.domain.WalletUpdateDTO;
import com.bonus.canteen.core.account.domain.bo.TradeAndWallerInfo;
import com.bonus.canteen.core.account.domain.vo.AccInfoDetailsVO;
import com.bonus.canteen.core.account.service.IAccInfoService;
import com.bonus.canteen.core.account.service.IAccTradeService;
import com.bonus.canteen.core.account.service.IAccWalletInfoService;
import com.bonus.canteen.core.account.utils.AccRedisUtils;
import com.bonus.canteen.core.alloc.domain.AllocStall;
import com.bonus.canteen.core.alloc.service.IAllocStallService;
import com.bonus.canteen.core.common.utils.MqUtil;
import com.bonus.canteen.core.common.utils.RedisUtil;
import com.bonus.canteen.core.menu.enums.AllocMealtimeTypeEnum;
import com.bonus.canteen.core.order.business.OrderBusiness;
import com.bonus.canteen.core.order.constants.OrderDetailStateEnum;
import com.bonus.canteen.core.order.constants.OrderRefundStateEnum;
import com.bonus.canteen.core.order.constants.OrderStateEnum;
import com.bonus.canteen.core.order.domain.*;
import com.bonus.canteen.core.order.domain.param.*;
import com.bonus.canteen.core.order.mapper.OrderInfoMapper;
import com.bonus.canteen.core.order.service.IOrderDetailService;
import com.bonus.canteen.core.order.service.IOrderInfoService;
import com.bonus.canteen.core.pay.constants.PayChannelEnum;
import com.bonus.canteen.core.pay.constants.PayStateEnum;
import com.bonus.canteen.core.pay.constants.PayTypeEnum;
import com.bonus.canteen.core.user.domain.DeviceMqPersonalUpdateMessageDTO;
import com.bonus.common.core.exception.ServiceException;
import com.bonus.common.core.utils.DateUtils;
import com.bonus.common.core.utils.StringUtils;
import com.bonus.common.houqin.constant.DelFlagEnum;
import com.bonus.common.houqin.constant.SourceTypeEnum;
import com.bonus.common.houqin.mq.constant.LeMqConstant;
import com.bonus.common.houqin.utils.JacksonUtil;
import com.bonus.common.houqin.utils.SM4EncryptUtils;
import com.bonus.common.security.utils.SecurityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 订单Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-04-14
 */
@Service
public class OrderInfoServiceImpl implements IOrderInfoService
{
    private static final Logger log = LoggerFactory.getLogger(OrderInfoServiceImpl.class);
    @Autowired
    private OrderInfoMapper orderInfoMapper;
    @Autowired
    private IAccTradeService accTradeService;
    @Autowired
    private IAccInfoService accInfoService;
    @Autowired
    private IOrderDetailService orderDetailService;
    @Autowired
    private OrderBusiness orderBusiness;
    @Autowired
    private IAccWalletInfoService accWalletInfoService;
    @Autowired
    private IAllocStallService allocStallService;

    /**
     * 查询订单
     * 
     * @param orderId 订单主键
     * @return 订单
     */
    @Override
    public OrderInfo selectOrderInfoByOrderId(Long orderId)
    {
        OrderInfo orderInfo = orderInfoMapper.selectOrderInfoByOrderId(orderId);
        if(Objects.isNull(orderInfo)) {
            throw new ServiceException("订单不存在");
        }
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderId(orderId);
        List<OrderDetail> orderDetailList = orderDetailService.selectOrderDetailList(orderDetail);
        orderInfo.setOrderDetailList(orderDetailList);
        return orderInfo;
    }

    @Override
    public OrderInfo selectOrderInfoBydeviceOrderId(String deviceOrderId)
    {
        OrderInfo orderInfo = orderInfoMapper.selectOrderInfoByDeviceOrderId(deviceOrderId);
        if(Objects.isNull(orderInfo)) {
            throw new ServiceException("订单不存在");
        }
        OrderDetail orderDetail = new OrderDetail();
        orderDetail.setOrderId(orderInfo.getOrderId());
        List<OrderDetail> orderDetailList = orderDetailService.selectOrderDetailList(orderDetail);
        orderInfo.setOrderDetailList(orderDetailList);
        return orderInfo;
    }

    /**
     * 查询订单列表
     * 
     * @return 订单
     */
    @Override
    public List<OrderInfo> selectOrderInfoList(OrderQueryParam orderQueryParam)
    {
        List<OrderInfo> orderInfoList = orderInfoMapper.selectOrderInfoList(orderQueryParam);
        if(CollUtil.isNotEmpty(orderInfoList)) {
            orderInfoList.forEach(order -> {
                OrderDetail orderDetail = new OrderDetail();
                orderDetail.setOrderId(order.getOrderId());
                List<OrderDetail> orderDetailList = orderDetailService.selectOrderDetailList(orderDetail);
                order.setOrderDetailList(orderDetailList);
                order.setPhoneNumber(SM4EncryptUtils.sm4Decrypt(order.getPhoneNumber()));
                order.setMealtimeName(AllocMealtimeTypeEnum.getDescByKey(order.getMealtimeType()));
            });
        }
        return orderInfoList;
    }

    @Override
    public int insertCanteenOrderInfo(OrderAddParam orderAddParam) {
        log.info("[食堂h5下单]入参：{}", JacksonUtil.writeValueAsString(orderAddParam));
        String paramMd5 = "sc:h5_order_"+DigestUtil.md5Hex(JacksonUtil.writeValueAsString(orderAddParam));
        if (!RedisUtil.setNx(paramMd5, 1, 2)) {
            log.info("[食堂h5下单]重复提交：{}", paramMd5);
            throw new ServiceException("请勿重复提交订单");
        }
        AccInfoDetailsVO accInfoVO = this.accInfoService.queryAccInfoByUserId(orderAddParam.getUserId());
        accInfoService.checkAccStatus(accInfoVO);
        List<OrderInfo> canteenOrderInfoList = new OrderInfo().of(orderAddParam);
        checkStallStatus(canteenOrderInfoList);
        checkOrdersTotalAmount(canteenOrderInfoList, orderAddParam.getRealAmount());
        List<OrderInfo> orderInfoList = orderBusiness.orderPlaceHandler(canteenOrderInfoList);
        try {
            AccRedisUtils.lockUpdateAccWalletBalance(orderAddParam.getUserId());
            orderBusiness.orderPay(orderInfoList);
        }catch (Exception ex) {
            orderBusiness.updateOrderPayFailed(orderInfoList);
            throw new ServiceException(ex.getMessage(), 500001);
        }finally {
            AccRedisUtils.unlockUpdateAccWalletBalance(orderAddParam.getUserId());
        }
        return 1;
    }
    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int insertDeviceOrderInfo(DeviceOrderAddParam orderAddParam) {
        log.info("[食堂设备下单]入参：{}", JacksonUtil.writeValueAsString(orderAddParam));
        String paramMd5 = "sc:device_order_"+DigestUtil.md5Hex(JacksonUtil.writeValueAsString(orderAddParam));
        if (!RedisUtil.setNx(paramMd5, 1, 2)) {
            log.info("[食堂设备下单]重复提交：{}", paramMd5);
            throw new ServiceException("请勿重复提交订单");
        }
        AccInfoDetailsVO accInfoVO = this.accInfoService.queryAccInfoByUserId(orderAddParam.getUserId());
        accInfoService.checkAccStatus(accInfoVO);
        List<OrderInfo> canteenOrderInfoList = new OrderInfo().of(orderAddParam);
        checkStallStatus(canteenOrderInfoList);
        checkDeviceOrderExisting(canteenOrderInfoList);
        checkOrdersTotalAmount(canteenOrderInfoList, orderAddParam.getRealAmount());
        List<OrderInfo> orderInfoList = orderBusiness.orderPlaceHandler(canteenOrderInfoList);
        try {
            AccRedisUtils.lockUpdateAccWalletBalance(orderAddParam.getUserId());
            orderBusiness.orderPay(orderInfoList);
        }catch (Exception ex) {
            orderBusiness.updateOrderPayFailed(orderInfoList);
            throw new ServiceException(ex.getMessage());
        }finally {
            AccRedisUtils.unlockUpdateAccWalletBalance(orderAddParam.getUserId());
        }
        return 1;
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public int insertShopOrderInfo(ShopOrderAddParam orderAddParam) {
        log.info("[超市下单]入参：{}", JacksonUtil.writeValueAsString(orderAddParam));
        String paramMd5 = "sc:shop_order_"+DigestUtil.md5Hex(JacksonUtil.writeValueAsString(orderAddParam));
        if (!RedisUtil.setNx(paramMd5, 1, 2)) {
            log.info("[超市下单]重复提交：{}", paramMd5);
            throw new ServiceException("请勿重复提交订单");
        }
        AccInfoDetailsVO accInfoVO = this.accInfoService.queryAccInfoByUserId(orderAddParam.getUserId());
        accInfoService.checkAccStatus(accInfoVO);
        List<OrderInfo> canteenOrderInfoList = new ShopOrderAddParam().of(orderAddParam);
        checkDeviceOrderExisting(canteenOrderInfoList);
        List<OrderInfo> orderInfoList = orderBusiness.orderPlaceHandler(canteenOrderInfoList);
        try {
            AccRedisUtils.lockUpdateAccWalletBalance(orderAddParam.getUserId());
            orderBusiness.orderPay(orderInfoList);
        }catch (Exception ex) {
            orderBusiness.updateOrderPayFailed(orderInfoList);
            throw new ServiceException(ex.getMessage());
        }finally {
            AccRedisUtils.unlockUpdateAccWalletBalance(orderAddParam.getUserId());
        }
        return 1;
    }

    private void checkDeviceOrderExisting(List<OrderInfo> orderInfoList) {
        if(CollUtil.isEmpty(orderInfoList)) {
            throw new ServiceException("订单不能为空");
        }
        for (OrderInfo orderInfo : orderInfoList) {
            OrderInfo orderInfoByOrderId = orderInfoMapper.selectOrderInfoByDeviceOrderId(orderInfo.getDeviceOrderId());
            if(Objects.nonNull(orderInfoByOrderId)) {
                throw new ServiceException("该设备订单已存在");
            }
        }
    }

    private void checkStallStatus(List<OrderInfo> orderInfoList) {
        if(CollUtil.isEmpty(orderInfoList)) {
            throw new ServiceException("订单不能为空");
        }
        for(OrderInfo orderInfo : orderInfoList) {
            if(orderInfo.getIsOnline() == 1) {
                AllocStall allocStall = allocStallService.selectAllocStallByStallId(orderInfo.getStallId());
                if(Objects.isNull(allocStall) || DelFlagEnum.DEL_TRUE.key().toString().equals(allocStall.getDelFlag())) {
                    throw new ServiceException("档口不存在");
                }
                if(1 == allocStall.getBusinessState()) {
                    throw new ServiceException("档口已休息");
                }

            }
        }

    }

    @Override
    public void pay(Long orderId) {
        if(Objects.isNull(orderId) || orderId <= 0) {
            throw new ServiceException("订单ID不能为空");
        }
        OrderInfo orderInfo = selectOrderInfoByOrderId(orderId);
        if(Objects.isNull(orderInfo)) {
            throw new ServiceException("订单不存在");
        }
        String payOrderKey = String.format("sc:order_pay_orderId_%s", orderId);
        if (!RedisUtil.setNx(payOrderKey, 1, 2)) {
            log.info("支付处理中：{}", payOrderKey);
            throw new ServiceException("支付处理中");
        }
        if(orderInfo.getOrderState().equals(OrderRefundStateEnum.FINISH.getKey())) {
            throw new ServiceException("订单已退");
        }
        if(PayStateEnum.isUnPay(orderInfo.getPayState())) {
            List<OrderInfo> orderInfoList = new ArrayList<>();
            orderInfoList.add(orderInfo);
            try {
                AccRedisUtils.lockUpdateAccWalletBalance(orderInfo.getUserId());
                orderBusiness.orderPay(orderInfoList);
            }catch (Exception ex) {
                orderBusiness.updateOrderPayFailed(orderInfoList);
                throw new ServiceException(ex.getMessage());
            }finally {
                AccRedisUtils.unlockUpdateAccWalletBalance(orderInfo.getUserId());
            }
        }
    }

    @Override
    public List<OrderRefundHistoryVO> orderRefundHistory(OrderRefundHistoryParam param) {
        List<OrderRefundHistoryVO> list = orderInfoMapper.orderRefundHistory(param);
        if(CollUtil.isNotEmpty(list)) {
            list.forEach(orderRefundHistoryVO -> {
                orderRefundHistoryVO.setPayTypeName(PayTypeEnum.getDescByKey(orderRefundHistoryVO.getPayType()));
                orderRefundHistoryVO.setTradeTypeName(AccTradeTypeEnum.getDescByKey(orderRefundHistoryVO.getTradeType()));
            });
        }
        return list;
    }

    @Override
    public void writeOffOrderByOrderIds(OrderWriteOffParam param) {
        OrderQueryParam orderQueryParam = new OrderQueryParam();
        orderQueryParam.setOrderIdList(param.getOrderIdList());
        orderQueryParam.setStartDateTime(null);
        orderQueryParam.setEndDateTime(null);
        List<OrderInfo> orderInfoList = orderInfoMapper.selectOrderInfoList(orderQueryParam);
        if(CollUtil.isNotEmpty(orderInfoList)) {
            orderInfoList.forEach(orderInfo -> {
                if(!OrderStateEnum.PLACE.getKey().equals(orderInfo.getOrderState())) {
                    throw new ServiceException("仅已下单订单才能核销");
                }
            });
        }
        orderInfoMapper.writeOffOrderByOrderIds(param);
    }

    private void checkOrdersTotalAmount(List<OrderInfo> orderInfoList ,BigDecimal totalAmountParam) {
        BigDecimal totalAmount = BigDecimal.ZERO;
       if(CollUtil.isNotEmpty(orderInfoList)) {
           totalAmount = orderInfoList.stream()
                   .map(OrderInfo::getRealAmount)
                   .reduce(BigDecimal.ZERO, BigDecimal::add);
       }
       if(totalAmount.compareTo(totalAmountParam) != 0) {
           throw new ServiceException("订单金额有误");
       }
    }


    /**
     * 修改订单
     * 
     * @param orderInfo 订单
     * @return 结果
     */
    @Override
    public int updateOrderInfo(OrderInfo orderInfo)
    {
        orderInfo.setUpdateTime(DateUtils.getNowDate());
        return orderInfoMapper.updateOrderInfo(orderInfo);
    }

    /**
     * 批量删除订单
     * 
     * @param orderIds 需要删除的订单主键
     * @return 结果
     */
    @Override
    public int deleteOrderInfoByOrderIds(List<Long> orderIds)
    {
        return orderInfoMapper.deleteOrderInfoByOrderIds(orderIds);
    }

    /**
     * 删除订单信息
     * 
     * @param orderId 订单主键
     * @return 结果
     */
    @Override
    public int deleteOrderInfoByOrderId(Long orderId)
    {
        return orderInfoMapper.deleteOrderInfoByOrderId(orderId);
    }

    @Override
    @Transactional(rollbackFor = {Exception.class})
    public void refund(Long orderId, String operationUser) {
        if(Objects.isNull(orderId) || orderId <= 0) {
            throw new ServiceException("订单ID不能为空");
        }
        OrderInfo orderInfo = selectOrderInfoByOrderId(orderId);
        if(Objects.isNull(orderInfo)) {
            throw new ServiceException("订单不存在");
        }
        String refundKey = String.format("sc:order_refund_orderId_%s", orderId);
        if (!RedisUtil.setNx(refundKey, 1, 2)) {
            log.info("退单处理中：{}", refundKey);
            throw new ServiceException("退单处理中");
        }
        if(orderInfo.getOrderRefundState().equals(OrderRefundStateEnum.FINISH.getKey())) {
            throw new ServiceException("订单已退");
        }
        if(orderInfo.getOrderState().equals(OrderStateEnum.CANCEL.getKey())) {
            throw new ServiceException("订单已取消");
        }
        if(orderInfo.getOrderState().equals(OrderStateEnum.FINISH.getKey())) {
            throw new ServiceException("订单已核销");
        }
        if(PayStateEnum.PAY_SUCC.getKey().equals(orderInfo.getPayState())) {
            List<TradeAndWallerInfo> accTradeList = accTradeService.queryTradeAndWallerInfoByOrderNo
                    (String.valueOf(orderId), AccTradeTypeEnum.CONSUME);
            if(CollUtil.isEmpty(accTradeList)) {
                throw new ServiceException("订单交易记录不存在");
            }
            BigDecimal refundAmount = BigDecimal.ZERO;
            for(TradeAndWallerInfo accTrade : accTradeList) {
                if(PayStateEnum.PAY_SUCC.getKey().equals(accTrade.getPayState())) {
                    WalletUpdateDTO walletUpdateDTO = new WalletUpdateDTO();
                    walletUpdateDTO.setAmount(accTrade.getActualAmount());
                    walletUpdateDTO.setUserId(orderInfo.getUserId());
                    walletUpdateDTO.setAccWalletIdEnum(AccWalletIdEnum.getEnum(accTrade.getWalletId()));
                    walletUpdateDTO.setAccTradeTypeEnum(AccTradeTypeEnum.CONSUME_REFUND);
                    walletUpdateDTO.setPayChannelEnum(PayChannelEnum.ACC);
                    walletUpdateDTO.setPayTypeEnum(PayTypeEnum.MEAL_CARD);
                    walletUpdateDTO.setOrderNo(orderId);
                    walletUpdateDTO.setDeviceSn(orderInfo.getDeviceSn());
                    walletUpdateDTO.setOperationUser(operationUser);
                    accWalletInfoService.addAccWalletBalance(walletUpdateDTO);
                    refundAmount = refundAmount.add(accTrade.getActualAmount());
                }
            }
            OrderInfo refundOrderInfo = new OrderInfo();
            refundOrderInfo.setOrderId(orderId);
            refundOrderInfo.setRefundAmount(refundAmount);
            refundOrderInfo.setOrderRefundState(OrderRefundStateEnum.FINISH.getKey());
            refundOrderInfo.setOrderState(OrderStateEnum.CANCEL.getKey());
            refundOrderInfo.setUpdateBy(StringUtils.isBlank(operationUser) ? SecurityUtils.getUsername() : operationUser);
            refundOrderInfo.setUpdateTime(DateUtils.getNowDate());
            orderInfoMapper.updateOrderInfo(refundOrderInfo);
            OrderDetail orderDetailQuery = new OrderDetail();
            orderDetailQuery.setOrderId(orderId);
            List<OrderDetail> orderDetailList = orderDetailService.selectOrderDetailList(orderDetailQuery);
            for(OrderDetail orderDetail : orderDetailList) {
                orderDetail.setRefundAmount(orderDetail.getRealAmount());
                orderDetail.setRefundNum(orderDetail.getQuantity());
                orderDetail.setDetailState(OrderDetailStateEnum.REFUNDED.getKey());
                orderDetail.setUpdateBy(StringUtils.isBlank(operationUser) ? SecurityUtils.getUsername() : operationUser);
                orderDetailService.updateOrderDetail(orderDetail);
            }
            orderBusiness.addMenuDishSupplyNum(orderDetailList);
            try {
                DeviceMqPersonalUpdateMessageDTO bean = new DeviceMqPersonalUpdateMessageDTO().setUpdatePerson(Math.toIntExact(orderInfo.getUserId()),"update");
                String jsonString = JacksonUtil.writeValueAsString(bean);
                log.info("账户变动发送mq内容：{}", jsonString);
                MqUtil.pushToTenantAllDevice(bean, LeMqConstant.Topic.DEVICE_UPDATE_PERSONAL_CONFIG_V4);
            } catch (Exception e) {
                log.error("发送MQ消息失败", e);
            }
        }else {
            OrderInfo refundOrderInfo = new OrderInfo();
            refundOrderInfo.setOrderId(orderId);
            refundOrderInfo.setOrderRefundState(OrderRefundStateEnum.FINISH.getKey());
            refundOrderInfo.setOrderState(OrderStateEnum.CANCEL.getKey());
            refundOrderInfo.setUpdateBy(StringUtils.isBlank(operationUser) ? SecurityUtils.getUsername() : operationUser);
            refundOrderInfo.setUpdateTime(DateUtils.getNowDate());
            orderInfoMapper.updateOrderInfo(refundOrderInfo);
        }
    }
}
