
package com.tmerclub.cloud.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.tmerclub.cloud.api.delivery.vo.StationVO;
import com.tmerclub.cloud.api.multishop.feign.ShopDetailFeignClient;
import com.tmerclub.cloud.api.multishop.vo.ShopDetailVO;
import com.tmerclub.cloud.api.order.constant.OrderStatus;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.util.PageUtil;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.leaf.manager.SegmentManager;
import com.tmerclub.cloud.common.order.constant.RefundStatusEnum;
import com.tmerclub.cloud.common.order.dto.OrderVirtualInfoDTO;
import com.tmerclub.cloud.common.order.vo.OrderVirtualInfoVO;
import com.tmerclub.cloud.common.response.ResponseEnum;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.rocketmq.config.RocketMqConstant;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.constant.DistributedIdKey;
import com.tmerclub.cloud.delivery.manager.DeliveryManager;
import com.tmerclub.cloud.delivery.service.StationService;
import com.tmerclub.cloud.order.bo.OrderVirtualInfoLogBO;
import com.tmerclub.cloud.order.bo.mongo.MongoOrderVirtualInfoLogBO;
import com.tmerclub.cloud.order.mapper.OrderMapper;
import com.tmerclub.cloud.order.mapper.OrderRefundMapper;
import com.tmerclub.cloud.order.mapper.OrderVirtualInfoMapper;
import com.tmerclub.cloud.order.model.Order;
import com.tmerclub.cloud.order.model.OrderVirtualInfo;
import com.tmerclub.cloud.order.service.OrderVirtualInfoLogService;
import com.tmerclub.cloud.order.service.OrderVirtualInfoService;
import com.tmerclub.cloud.order.vo.OrderRefundVO;
import com.tmerclub.cloud.order.vo.OrderShopVO;
import com.tmerclub.cloud.order.vo.OrderVirtualInfoLogVO;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.GenericMessage;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 订单虚拟商品信息
 *
 * @author tmerclub
 * @date 2023-02-24 13:55:55
 */
@Service
public class OrderVirtualInfoServiceImpl implements OrderVirtualInfoService {

    @Autowired
    private OrderVirtualInfoMapper orderVirtualInfoMapper;
    @Autowired
    private DeliveryManager deliveryManager;
    @Autowired
    private OrderMapper orderMapper;
    @Autowired
    private RocketMQTemplate orderReceiptTemplate;
    @Autowired
    private OrderRefundMapper orderRefundMapper;
    @Autowired
    private ShopDetailFeignClient shopDetailFeignClient;
    @Autowired
    private StationService stationService;
    @Autowired
    private SegmentManager segmentManager;
    @Autowired
    private OrderVirtualInfoLogService orderVirtualInfoLogService;

    @Override
    public PageVO<OrderVirtualInfo> page(PageDTO pageDTO) {
        return PageUtil.doPage(pageDTO, () -> orderVirtualInfoMapper.list());
    }

    @Override
    public OrderVirtualInfo getById(Long id) {
        return orderVirtualInfoMapper.getById(id);
    }

    @Override
    public void save(OrderVirtualInfo orderVirtualInfo) {
        orderVirtualInfoMapper.save(orderVirtualInfo);
    }

    @Override
    public void update(OrderVirtualInfoVO orderVirtualInfo) {
        orderVirtualInfoMapper.update(orderVirtualInfo);
    }

    @Override
    public void deleteById(Long id) {
        orderVirtualInfoMapper.deleteById(id);
    }

    @Override
    public void saveBatch(List<OrderVirtualInfo> orderVirtualInfoList) {
        orderVirtualInfoMapper.saveBatch(orderVirtualInfoList);
    }

    /**
     * 添加下虚拟商品的信息
     *
     * @param order       订单信息
     * @param orderShopVO 用于前端展示的订单信息
     * @param stationId   门店id
     */
    @Override
    public void handlerVirtualProdOrder(Order order, OrderShopVO orderShopVO, Long stationId) {
        orderShopVO.setOrderMold(order.getOrderMold());
        orderShopVO.setWriteOffStart(order.getWriteOffStart());
        orderShopVO.setWriteOffEnd(order.getWriteOffEnd());
        orderShopVO.setWriteOffNum(order.getWriteOffNum());
        orderShopVO.setWriteOffMultipleCount(order.getWriteOffMultipleCount());
        if (Objects.equals(order.getOrderItems().get(0).getIsRefund(), 0)) {
            orderShopVO.setCanAllRefund(false);
            orderShopVO.setCanRefund(false);
        }

        // 卡券信息
        List<OrderVirtualInfoVO> virtualInfoList = getByOrderIdAndStationId(order.getOrderId(), stationId);
        boolean flag = Objects.equals(order.getStatus(), OrderStatus.WAIT_GROUP.value()) || Objects.equals(order.getStatus(), OrderStatus.UNPAY.value());
        if (CollectionUtil.isNotEmpty(virtualInfoList) && !flag) {
            orderShopVO.setVirtualInfoList(virtualInfoList);
            orderShopVO.setTotalNum(virtualInfoList.size());
        }
    }

    @Override
    public List<OrderVirtualInfoVO> getByOrderIdAndStationId(Long orderId, Long stationId) {
        return orderVirtualInfoMapper.getByOrderIdAndStationId(orderId, stationId);
    }

    @Override
    public List<OrderVirtualInfoVO> getByOrderIdAndShopId(Long orderId, Long shopId, Integer isWriteOff) {
        return orderVirtualInfoMapper.getByOrderIdAndShopId(orderId, shopId, isWriteOff);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void orderWriteOffByOrderId(OrderVirtualInfoDTO orderVirtualInfoDTO, Order order, Long stationId) {
        List<OrderRefundVO> processOrderRefunds = orderRefundMapper.getProcessingRefundByOrderId(order.getOrderId());
        if (CollectionUtil.isNotEmpty(processOrderRefunds)) {
            // 订单退款中，无法自提
            throw new LuckException(order.getOrderId() + "订单退款中，无法核销");
        }
        Date date = new Date();
        List<OrderVirtualInfoVO> orderVirtualInfoList = checkInfoAndGetVirtualInfo(orderVirtualInfoDTO, order, stationId, date);
        List<OrderVirtualInfoVO> updateList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        Order updateOrder = new Order();
        updateOrder.setOrderId(order.getOrderId());
        updateOrder.setUpdateTime(date);
        // 如果是多次核销且无限次则无需进行核销
        boolean unWrite = Objects.equals(order.getWriteOffNum(), -1) &&
                (Objects.equals(order.getWriteOffMultipleCount(), -1) || Objects.isNull(order.getWriteOffMultipleCount()));
        if (unWrite) {
            // 多次核销且无限次的添加被核销次数
            updateOrder.setWriteOffCount((updateOrder.getWriteOffCount() == null ? 0 : updateOrder.getWriteOffCount()) + 1);
            orderMapper.update(updateOrder);
            // 添加日志
            handleSaveOrderVirtualInfoLog(orderVirtualInfoDTO, order, stationId, orderVirtualInfoList);
            return;
        }
        // 是否全部核销完成
        boolean isAllWriteOff;
        // 核销码存在时表示直接是核销码核销，否则直接是整单核销
        if (Objects.nonNull(orderVirtualInfoDTO) && StrUtil.isNotBlank(orderVirtualInfoDTO.getWriteOffCode())) {
            isAllWriteOff = orderVirtualInfoList.size() < 2;
            for (OrderVirtualInfoVO orderVirtualInfo : orderVirtualInfoList) {
                if (StrUtil.equals(orderVirtualInfoDTO.getWriteOffCode(), orderVirtualInfo.getWriteOffCode())) {
                    orderVirtualInfo.setWriteOffTime(date);
                    orderVirtualInfo.setStationId(stationId);
                    // 多次核销次数还没到
                    if (Objects.equals(order.getWriteOffNum(), -1) && 0 < orderVirtualInfo.getWriteOffMultipleCount() - 1) {
                        isAllWriteOff = false;
                        orderVirtualInfo.setWriteOffMultipleCount(orderVirtualInfo.getWriteOffMultipleCount() - 1);
                        updateList.add(orderVirtualInfo);
                        continue;
                    }
                    orderVirtualInfo.setIsWriteOff(1);
                    updateList.add(orderVirtualInfo);
                    break;
                }
            }
        } else {
            isAllWriteOff = true;
            for (OrderVirtualInfoVO orderVirtualInfo : orderVirtualInfoList) {
                orderVirtualInfo.setStationId(stationId);
                orderVirtualInfo.setWriteOffTime(date);
                // 多次核销次数还没到
                if (Objects.equals(order.getWriteOffNum(), -1) && 0 < orderVirtualInfo.getWriteOffMultipleCount() - 1) {
                    orderVirtualInfo.setWriteOffMultipleCount(orderVirtualInfo.getWriteOffMultipleCount() - 1);
                    isAllWriteOff = false;
                    updateList.add(orderVirtualInfo);
                    continue;
                }
                orderVirtualInfo.setIsWriteOff(1);
                updateList.add(orderVirtualInfo);
            }
        }
        updateList.forEach(this::update);
        // 全部提货完成就直接确认收货
        if (isAllWriteOff) {
            updateOrder.setWriteOffStatus(1);
            if (Objects.equals(order.getStatus(), OrderStatus.CONSIGNMENT.value())) {
                updateOrder.setStatus(OrderStatus.SUCCESS.value());
                updateOrder.setFinallyTime(new Date());
                // 开启事务消息，添加积分
                TransactionSendResult transactionSendResult = orderReceiptTemplate.sendMessageInTransaction(RocketMqConstant.ORDER_RECEIPT_TOPIC, new GenericMessage<>(updateOrder.getOrderId()), null);
                if (!Objects.equals(transactionSendResult.getLocalTransactionState(), LocalTransactionState.COMMIT_MESSAGE)) {
                    throw new LuckException(ResponseEnum.EXCEPTION);
                }
            }
        }
        updateOrder.setWriteOffCount((updateOrder.getWriteOffCount() == null ? 0 : updateOrder.getWriteOffCount()) + 1);
        orderMapper.update(updateOrder);
        // 添加日志
        handleSaveOrderVirtualInfoLog(orderVirtualInfoDTO, order, stationId, orderVirtualInfoList);
    }


    private List<OrderVirtualInfoVO> checkInfoAndGetVirtualInfo(OrderVirtualInfoDTO orderVirtualInfoDTO, Order orderDb, Long stationId, Date date) {
        if (Objects.nonNull(stationId)) {
            StationVO station = deliveryManager.getStation(stationId);
            if (Objects.isNull(station) || !Objects.equals(station.getStatus(), 1)) {
                // 该自提点未在营业状态
                throw new LuckException("该自提点未在营业状态");
            }
        }
        boolean isRefund = Objects.equals(orderDb.getRefundStatus(), RefundStatusEnum.APPLY.value())
                || Objects.equals(orderDb.getRefundStatus(), RefundStatusEnum.SUCCEED.value())
                || Objects.equals(orderDb.getRefundStatus(), RefundStatusEnum.PARTIAL_SUCCESS.value());
        if (Objects.nonNull(orderDb.getRefundStatus()) && isRefund) {
            // 订单退款中，无法核销
            throw new LuckException("订单" + orderDb.getOrderId() + "退款中，无法核销");
        }
        if (Objects.equals(orderDb.getWriteOffNum(), 0)) {
            // 订单无需核销
            throw new LuckException("订单无需核销");
        }
        // 查询出未核销的卡券
        List<OrderVirtualInfoVO> orderVirtualInfoList = getByOrderIdAndShopId(orderDb.getOrderId(), orderDb.getShopId(), 0);
        if (CollectionUtils.isEmpty(orderVirtualInfoList)) {
            // 卡券不存在或者已核销
            throw new LuckException("卡券不存在或者已核销");
        }
        if (DateUtil.compare(orderDb.getWriteOffStart(), date) > 0) {
            // 卡券未到使用时间或已过期
            throw new LuckException("卡券未到使用时间或已过期");
        }
        if (Objects.nonNull(orderDb.getWriteOffEnd()) && DateUtil.compare(date, orderDb.getWriteOffEnd()) > 0) {
            // 卡券未到使用时间或已过期
            throw new LuckException("卡券未到使用时间或已过期");
        }
        // 核销码是否存在
        if (Objects.nonNull(orderVirtualInfoDTO) && StrUtil.isNotBlank(orderVirtualInfoDTO.getWriteOffCode())) {
            boolean isExist = false;
            for (OrderVirtualInfoVO orderVirtualInfo : orderVirtualInfoList) {
                if (StrUtil.equals(orderVirtualInfoDTO.getWriteOffCode(), orderVirtualInfo.getWriteOffCode())) {
                    isExist = true;
                    break;
                }
            }
            if (!isExist) {
                // 卡券不存在或者已核销
                throw new LuckException("核销码错误");
            }
        }
        return orderVirtualInfoList;
    }

    private void handleSaveOrderVirtualInfoLog(OrderVirtualInfoDTO orderVirtualInfoDTO, Order order, Long stationId, List<OrderVirtualInfoVO> orderVirtualInfoList) {
        Long shopId = order.getShopId();
        Long orderId = order.getOrderId();
        List<MongoOrderVirtualInfoLogBO> saveList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        if (Objects.isNull(orderVirtualInfoDTO) || StrUtil.isBlank(orderVirtualInfoDTO.getWriteOffCode())) {
            // 批量核销
            List<Long> idList = segmentManager.listSegmentId(DistributedIdKey.tmerclub_ORDER_VIRTUAL_INFO_LOG, orderVirtualInfoList.size());
            int i = 0;
            for (OrderVirtualInfoVO orderVirtualInfoVO : orderVirtualInfoList) {
                MongoOrderVirtualInfoLogBO mongoOrderVirtualInfoLogBO = new MongoOrderVirtualInfoLogBO();
                mongoOrderVirtualInfoLogBO.setOrderVirutalInfoLogId(idList.get(i));
                mongoOrderVirtualInfoLogBO.setCreateTime(LocalDateTime.now());
                mongoOrderVirtualInfoLogBO.setShopId(shopId);
                mongoOrderVirtualInfoLogBO.setOrderId(orderId);
                mongoOrderVirtualInfoLogBO.setStationId(stationId);
                mongoOrderVirtualInfoLogBO.setWriteOffCode(orderVirtualInfoVO.getWriteOffCode());
                saveList.add(mongoOrderVirtualInfoLogBO);
                i++;
            }
        } else {
            MongoOrderVirtualInfoLogBO mongoOrderVirtualInfoLogBO = new MongoOrderVirtualInfoLogBO();
            mongoOrderVirtualInfoLogBO.setOrderVirutalInfoLogId(segmentManager.getSegmentId(DistributedIdKey.tmerclub_ORDER_VIRTUAL_INFO_LOG));
            mongoOrderVirtualInfoLogBO.setCreateTime(LocalDateTime.now());
            mongoOrderVirtualInfoLogBO.setShopId(shopId);
            mongoOrderVirtualInfoLogBO.setOrderId(orderId);
            mongoOrderVirtualInfoLogBO.setStationId(stationId);
            mongoOrderVirtualInfoLogBO.setWriteOffCode(orderVirtualInfoDTO.getWriteOffCode());
            saveList.add(mongoOrderVirtualInfoLogBO);
        }
        if (CollUtil.isNotEmpty(saveList)) {
            orderVirtualInfoLogService.saveBatch(saveList);
        }
    }

    @Override
    public List<OrderVirtualInfoVO> getByOrderIdAndCode(Long orderId, String code) {
        return orderVirtualInfoMapper.getByOrderIdAndCode(orderId, code);
    }

    @Override
    public int countWriteOffByOrderId(Long orderId) {
        return orderVirtualInfoMapper.countWriteOffByOrderId(orderId);
    }

    @Override
    public OrderVirtualInfoLogVO getByOrderId(Long orderId) {
        OrderVirtualInfoLogVO res = new OrderVirtualInfoLogVO();
        res.setOrderId(orderId);
        List<OrderVirtualInfoLogBO> mongoOrderVirtualInfoLogBOList = orderVirtualInfoLogService.getByOrderId(orderId);
        if (CollUtil.isEmpty(mongoOrderVirtualInfoLogBOList)) {
            return res;
        }
        List<OrderVirtualInfoVO> orderVirtualInfoList = orderVirtualInfoMapper.getByOrderId(orderId);
        if (CollUtil.isEmpty(orderVirtualInfoList)) {
            return res;
        }
        Map<String, OrderVirtualInfoVO> orderVirtualInfoMap = orderVirtualInfoList.stream().collect(Collectors.toMap(OrderVirtualInfoVO::getWriteOffCode, o -> o));
        Map<Long, String> shopMap = getShopMap(mongoOrderVirtualInfoLogBOList);
        Map<Long, String> stationMap = getStationMap(mongoOrderVirtualInfoLogBOList);
        for (OrderVirtualInfoLogBO mongoOrderVirtualInfoLogBO : mongoOrderVirtualInfoLogBOList) {
            OrderVirtualInfoVO orderVirtualInfoVO = orderVirtualInfoMap.get(mongoOrderVirtualInfoLogBO.getWriteOffCode());
            if (Objects.nonNull(orderVirtualInfoVO)) {
                mongoOrderVirtualInfoLogBO.setIsExpired(orderVirtualInfoVO.getIsExpired());
                mongoOrderVirtualInfoLogBO.setIsWriteOff(orderVirtualInfoVO.getIsWriteOff());
            }
            if (Objects.nonNull(mongoOrderVirtualInfoLogBO.getStationId())) {
                String stationName = stationMap.get(mongoOrderVirtualInfoLogBO.getStationId());
                mongoOrderVirtualInfoLogBO.setStationName(stationName);
                continue;
            }
            String shopName = shopMap.get(mongoOrderVirtualInfoLogBO.getShopId());
            mongoOrderVirtualInfoLogBO.setStationName(shopName);
        }
        res.setOrderVirtualInfoList(mongoOrderVirtualInfoLogBOList);
        return res;
    }

    @Override
    public List<OrderVirtualInfoLogBO> getByWriteOffCode(String writeOffCode) {
        List<OrderVirtualInfoLogBO> mongoOrderVirtualInfoLogBOList = orderVirtualInfoLogService.getByWriteOffCode(writeOffCode);
        if (CollUtil.isEmpty(mongoOrderVirtualInfoLogBOList)) {
            return new ArrayList<>();
        }
        Map<Long, String> shopMap = getShopMap(mongoOrderVirtualInfoLogBOList);
        Map<Long, String> stationMap = getStationMap(mongoOrderVirtualInfoLogBOList);
        for (OrderVirtualInfoLogBO mongoOrderVirtualInfoLogBO : mongoOrderVirtualInfoLogBOList) {
            if (Objects.nonNull(mongoOrderVirtualInfoLogBO.getStationId())) {
                String stationName = stationMap.get(mongoOrderVirtualInfoLogBO.getStationId());
                mongoOrderVirtualInfoLogBO.setStationName(stationName);
                continue;
            }
            String shopName = shopMap.get(mongoOrderVirtualInfoLogBO.getShopId());
            mongoOrderVirtualInfoLogBO.setStationName(shopName);
        }
        return mongoOrderVirtualInfoLogBOList;
    }

    @Override
    public void updateBatchByItemIds(List<OrderVirtualInfoVO> updateVirtualInfos, Long userId, Date date) {
        List<Long> itemIds = updateVirtualInfos.stream().map(OrderVirtualInfoVO::getOrderItemId).toList();
        Map<Long, Long> orderItemMap = updateVirtualInfos.stream().collect(Collectors.toMap(OrderVirtualInfoVO::getOrderItemId, OrderVirtualInfoVO::getOrderId));
        List<OrderVirtualInfoVO> orderVirtualInfoList = orderVirtualInfoMapper.listByOrderItemIds(itemIds);
        if(CollUtil.isEmpty(orderVirtualInfoList)){
            return;
        }
        List<OrderVirtualInfo> orderVirtualInfos = BeanUtil.mapAsList(orderVirtualInfoList, OrderVirtualInfo.class);
        for (OrderVirtualInfo orderVirtualInfoDb : orderVirtualInfos) {
            orderVirtualInfoDb.setOrderVirtualInfoId(segmentManager.getSegmentIdWithDateTime(DistributedIdKey.tmerclub_ORDER_VIRTUAL_INFO, userId));
            orderVirtualInfoDb.setCreateTime(date);
            orderVirtualInfoDb.setOrderId(orderItemMap.get(orderVirtualInfoDb.getOrderItemId()));
        }
        // 删除旧的
        orderVirtualInfoMapper.deleteByOrderItemId(itemIds);
        saveBatch(orderVirtualInfos);
    }

    private Map<Long, String> getStationMap(List<OrderVirtualInfoLogBO> orderVirtualInfoLogBOList) {
        List<Long> stationIds = orderVirtualInfoLogBOList.stream().map(OrderVirtualInfoLogBO::getStationId).filter(stationId -> Objects.nonNull(stationId)).collect(Collectors.toList());
        List<StationVO> stationList = stationService.listByStationId(stationIds);
        Map<Long, String> stationMap = stationList.stream().collect(Collectors.toMap(StationVO::getStationId, StationVO::getStationName));
        return stationMap;
    }

    private Map<Long, String> getShopMap(List<OrderVirtualInfoLogBO> orderVirtualInfoLogBOList) {
        List<Long> shopIds = orderVirtualInfoLogBOList.stream().filter(o -> Objects.isNull(o.getStationId())).map(OrderVirtualInfoLogBO::getShopId).collect(Collectors.toList());
        Map<Long, String> shopMap = new HashMap<>(Constant.INITIAL_CAPACITY);
        if (CollUtil.isNotEmpty(shopIds)) {
            ServerResponseEntity<List<ShopDetailVO>> serverResponse = shopDetailFeignClient.listByShopIds(shopIds);
            if (!serverResponse.isSuccess()) {
                throw new LuckException(serverResponse.getMsg());
            }
            List<ShopDetailVO> shopList = serverResponse.getData();
            shopMap = shopList.stream().collect(Collectors.toMap(ShopDetailVO::getShopId, ShopDetailVO::getShopName));
        }
        return shopMap;
    }

}
