package com.zm.order.tool.server.manager;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Throwables;
import com.zm.order.tool.server.component.ShardingKeyGenerator;
import com.zm.order.tool.server.constants.CommonConstants;
import com.zm.order.tool.server.dao.*;
import com.zm.order.tool.server.dto.BaseCreateOrderDTO;
import com.zm.order.tool.server.enums.OperateTypeEnum;
import com.zm.order.tool.server.hint.HintOrderDao;
import com.zm.order.tool.server.model.*;
import com.zm.order.tool.server.newdatasource.NewDataSourceOrderDao;
import com.zm.order.tool.server.newdatasource.NewDataSourceOrderPayDetailsDao;
import com.zm.order.tool.server.query.OrderQuery;
import com.zm.order.tool.server.service.ExceptionConstants;
import com.zm.order.tool.server.sharding.OrderHintManager;
import com.zm.order.tool.server.tempdb.OrderNoUserDao;
import com.zm.util.dto.Result;
import org.apache.shardingsphere.api.hint.HintManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Objects;

/**
 * @Description 订单manager
 * @Author: ph
 * @Date: 2019/10/31 10:17
 */
@Component
public class OrderManager {

    private static final Logger logger = LoggerFactory.getLogger(OrderManager.class);

    @Autowired
    private HintOrderDao hintOrderDao;
    @Autowired
    private OrderDao orderDao;
    @Autowired
    private OrderPayDetailsDao orderPayDetailsDao;
    @Autowired
    private DistributedCacheManager cacheManager;
    @Autowired
    private OrderNoIndexDao orderNoIndexDao;
    @Autowired
    private OrderItemDao orderItemDao;
    @Autowired
    private OrderDiscountDao orderDiscountDao;
    @Autowired
    private OrderAddrDao orderAddrDao;
    @Autowired
    private SourceNoIndexDao sourceNoIndexDao;
    @Autowired
    private OrderUserRelationDao relationDao;
    @Autowired
    private RefundDao refundDao;
    @Autowired
    private RefundItemDao refundItemDao;
    @Autowired
    private OrderRefundDetailsDao refundDetailsDao;
    @Autowired
    private AsyncMessageManager asyncMessageManager;
    @Autowired
    private OrderPayDetailsDao payDetailsDao;
    @Autowired
    private SearchManager searchManager;

    @Autowired
    private OrderItemBatchingDao itemBatchingDao;

    @Autowired
    private OrderIndexDao indexDao;

    @Autowired
    private PerformanceSelfTakeDao takeDao;
    @Autowired
    private ShardingKeyGenerator keyGenerator;

    private static final String RETURN_NUM = "refund_goods_nums";

    /**
     * 扫描订单
     */
    public List<Order> scanOrder(OrderQuery orderQuery, int dbIndex, int tbIndex) {
        HintManager hintManager = null;
        try {
            hintManager = OrderHintManager.initScanHint(CommonConstants.LOGIC_TABLE_ORDER, dbIndex, tbIndex);
            return hintOrderDao.syncScan(orderQuery);
        } catch (Throwable throwable) {
            logger.error("OrderManager[scanOrder] failed, cause by:{}", Throwables.getStackTraceAsString(throwable));
            throw new RuntimeException(throwable.getMessage());
        } finally {
            OrderHintManager.closeHint(hintManager);
        }
    }

    /**
     * 扫描订单
     */
    public List<Order> selectSync(OrderQuery orderQuery, int dbIndex, int tbIndex) {
        HintManager hintManager = null;
        try {
            hintManager = OrderHintManager.initScanHint(CommonConstants.LOGIC_TABLE_ORDER, dbIndex, tbIndex);
            return hintOrderDao.selectSync(orderQuery);
        } catch (Throwable throwable) {
            logger.error("OrderManager[selectSync] failed, cause by:{}", Throwables.getStackTraceAsString(throwable));
            throw new RuntimeException(throwable.getMessage());
        } finally {
            OrderHintManager.closeHint(hintManager);
        }
    }

    /**
     * 删除订单
     */
    public void delOrderBySellerId(Long sellerId, int dbIndex, int tbIndex) {
        HintManager hintManager = null;
        try {
            hintManager = OrderHintManager.initScanHint(CommonConstants.LOGIC_TABLE_ORDER, dbIndex, tbIndex);
            hintOrderDao.realDeleteBySellerId(sellerId);
        } catch (Throwable throwable) {
            logger.error("OrderManager[delOrderBySellerId] failed, cause by:{}", Throwables.getStackTraceAsString(throwable));
            throw new RuntimeException(throwable.getMessage());
        } finally {
            OrderHintManager.closeHint(hintManager);
        }
    }

    @Autowired
    private NewDataSourceOrderDao newOrderDao;

    @Autowired
    private NewDataSourceOrderPayDetailsDao newPayDao;


    @Transactional(rollbackFor = RuntimeException.class)
    public void updateOrder(BusinessOrderUpdateModelRequest businessOrderUpdateRequest, List<OrderPayDetails> payDetailsList, Order order) {
        try {
            if (order.getMasterDataSource() == 1) {
                newOrderDao.businessOrderUpdate(businessOrderUpdateRequest);
                //添加支付明细
                if (!CollectionUtils.isEmpty(payDetailsList)) {
                    newPayDao.insertList(payDetailsList);
                }
            } else {
                orderDao.businessOrderUpdate(businessOrderUpdateRequest);
                //添加支付明细
                if (!CollectionUtils.isEmpty(payDetailsList)) {
                    orderPayDetailsDao.insertList(payDetailsList);
                }
            }

        } catch (Exception e) {
            logger.error("OrderManager[updateOrder]failed!cause by:{0}", e);
            throw new RuntimeException(e.getMessage());
        }
    }

    /**
     * 获取订单号索引
     *
     * @param orderNo  订单号
     * @param isCreate 是否创建
     * @return
     */
    public Result<OrderNoIndex> getOrderNoIndex(String orderNo, Boolean isCreate) {
        Result result = new Result();
        try {
            if (StringUtils.isEmpty(orderNo)) {
                result.setException(ExceptionConstants.PARAM_ORDER_NO_IS_EMPTY);
                return result;
            }
            OrderNoIndex orderNoIndex;
            if (isCreate) {
                Result<String> getResult = cacheManager.get(orderNo);
                if (!getResult.isStatus()) {
                    result.setException(getResult.exceptionInfo());
                    return result;
                }
                String value = getResult.getData();
                if (StringUtils.isEmpty(value)) {
                    orderNoIndex = orderNoIndexDao.selectOne(orderNo);
                } else {
                    orderNoIndex = JSON.parseObject(value, OrderNoIndex.class);
                }
            } else {
                orderNoIndex = orderNoIndexDao.selectOne(orderNo);
                if (Objects.isNull(orderNoIndex)) {
                    Result<String> getResult = cacheManager.get(orderNo);
                    if (!getResult.isStatus()) {
                        result.setException(getResult.exceptionInfo());
                        return result;
                    }
                    String value = getResult.getData();
                    if (StringUtils.isEmpty(value)) {
                        orderNoIndex = orderNoIndexDao.selectOne(orderNo);
                    } else {
                        orderNoIndex = JSON.parseObject(value, OrderNoIndex.class);
                    }
                }
            }
            result.setData(orderNoIndex);
            result.setStatus(true);
        } catch (Throwable throwable) {
            logger.error("OrderManager[getOrderNoIndex] failed, orderNo:{}, userId:{}, cause by:{}",
                    orderNo, Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }


    /**
     * 创建订单
     *
     * @param orderDTO
     * @return Date: 2021/8/24 8:11 下午
     */
    @Transactional(rollbackFor = RuntimeException.class)
    public Long createOrder(BaseCreateOrderDTO orderDTO) throws RuntimeException {
        Order order = orderDTO.getOrder();
        try {
            // 1.插入订单主表
            orderDao.insert(order);
            // 2.插入订单商品表
            if (!CollectionUtils.isEmpty(orderDTO.getItemList())) {
                orderItemDao.inserts(orderDTO.getItemList());
            }
            // 3.插入订单折扣表
            if (!CollectionUtils.isEmpty(orderDTO.getDiscountList())) {
                orderDiscountDao.inserts(orderDTO.getDiscountList());
            }

            // 5.记录地址信息
            if (Objects.nonNull(orderDTO.getOrderAddr())) {
                orderAddrDao.insert(orderDTO.getOrderAddr());
            }
            // 7.记录商品加料信息
            if (!CollectionUtils.isEmpty(orderDTO.getItemBatchingList())) {
                itemBatchingDao.insertList(orderDTO.getItemBatchingList());
            }
            // 8.保存支付明细
            if (!CollectionUtils.isEmpty(orderDTO.getPayDetailsList())) {
                payDetailsDao.insertList(orderDTO.getPayDetailsList());
            }
            // 10. 记录订单号索引，这一步非常关键，针对只有订单号（不带分片键）查询的场景，必须要先查到分片键，避免全库全表扫描，如果失败，事务回滚
            Result indexResult = this.orderNoIndex(order);
            if (!indexResult.isStatus()) {
                throw new RuntimeException(indexResult.getMessage());
            }
            // 同步ES
            searchManager.indexOrder(order, null);
            return order.getId();
        } catch (Throwable throwable) {
            logger.error("OrderManager[createOrder]failed! orderNo:{},cause by:{}",
                    order.getOrderNo(), Throwables.getStackTraceAsString(throwable));
            throw new RuntimeException(throwable.getMessage());
        }
    }


    /**
     * 记录订单号索引
     * 因为是异步记录的订单号索引，会出现订单号索引未创建，查询请求就过来的情况，
     * 所以这里先将订单号索引暂存至redis，db订单号索引创建之后会删除redis里的订单号索引，默认过期时间
     *
     * @return
     */
    public Result orderNoIndex(Order order) {
        Result result = new Result();
        try {
            // 订单号和用户id关系暂存到redis，异步记录db索引后删除
            OrderNoIndex orderNoIndex = new OrderNoIndex();
            orderNoIndex.setOrderNo(order.getOrderNo());
            orderNoIndex.setUserId(order.getUserId());
            orderNoIndex.setOrderId(order.getId());
            Result setResult = cacheManager.set(order.getOrderNo(), JSON.toJSONString(orderNoIndex), 129600);
            if (!setResult.isStatus()) {
                logger.error("set cache failed, cause by:{}", setResult.getMessage());
                throw new RuntimeException(setResult.getMessage());
            }
            // 添加来源单号索引
            if (!StringUtils.isEmpty(order.getSourceNo())) {
                Result setSourceNoResult = cacheManager.set(order.getSellerId() + ":" + order.getSourceNo(), order.getOrderNo() + ":" + order.getUserId(), 1296000);
                if (!setSourceNoResult.isStatus()) {
                    logger.error("set cache failed, cause by:{}", setResult.getMessage());
                    throw new RuntimeException(setResult.getMessage());
                }
            }
            // 异步记录索引
            Result sendIndexResult = asyncMessageManager.sendOrderIndexMessage(order, OperateTypeEnum.ADD);
            if (!sendIndexResult.isStatus()) {
                logger.error("OrderIndexFailedWarning send order index message failed, cause by:{}", sendIndexResult.getMessage());
            }
            result.setStatus(true);
        } catch (Throwable throwable) {
            logger.error("OrderManager[orderNoIndex] failed, order:{}, cause by:{}", order.toString(), Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.ADD_ORDER_NO_INDEX_ERROR);
        }
        return result;
    }

    @Autowired
    private OrderNoUserDao userDao;

    @Autowired
    private OrderManager orderManager;

    public void test(OrderNoUser orderNoUser) throws RuntimeException {
        try {
            userDao.insert(orderNoUser);
        } catch (Throwable e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void updateBizType(Integer bizType, Long userId, String orderNo, Order update) {
        try {
            orderDao.updateBizType(update);
            orderItemDao.updateBizType(bizType, userId, orderNo);
            orderDiscountDao.updateBizType(bizType, userId, orderNo);
            orderPayDetailsDao.updateBizType(bizType, userId, orderNo);
            indexDao.updateBizType(bizType, orderNo);
        } catch (Throwable e) {
            logger.error("OrderManager[updateBizType]failed!cause by:{0}", e);
            throw new RuntimeException(e.getMessage());
        }
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void updateRefundBizType(Integer bizType, List<String> orderNoList) {
        try {
            refundDao.updateBizRefundType(bizType, orderNoList);
            refundItemDao.updateBiz(bizType, orderNoList);
            refundDetailsDao.updateBiz(bizType, orderNoList);
        } catch (Throwable e) {
            logger.error("OrderManager[updateRefundBizType]failed!cause by:{0}", e);
            throw new RuntimeException(e.getMessage());
        }
    }

    @Transactional(rollbackFor = RuntimeException.class)
    public void updateOrderByDiff(Order order,
                                  List<OrderItem> addItemList, List<OrderItem> updateItemList, List<OrderItem> delItemList,
                                  List<OrderDiscount> insertDiscountList, List<OrderDiscount> updateDiscountList, List<OrderDiscount> delDiscountList,
                                  List<OrderPayDetails> insertPayList, List<OrderPayDetails> updatePayList, List<OrderPayDetails> delPayList
    ) {
        try {
            orderDao.update(order);
            //添加商品
            if (!CollectionUtils.isEmpty(addItemList)) {
                orderItemDao.inserts(addItemList);
            }
            //更新item
            if (!CollectionUtils.isEmpty(updateItemList)) {
                for (OrderItem item : updateItemList) {
                    orderItemDao.updateItemSign(item);
                }
            }
            //delItemList 删除isDeleted 置为-1
//            if(!CollectionUtils.isEmpty(updateItemList)){
//                orderItemDao.deletes();
//            }
            // discount
            if (!CollectionUtils.isEmpty(insertDiscountList)) {
                orderDiscountDao.inserts(insertDiscountList);
            }
            if (!CollectionUtils.isEmpty(updateDiscountList)) {
                for (OrderDiscount discount : updateDiscountList) {
                    orderDiscountDao.updateTwo(discount);
                }
            }
            //pay
            if (!CollectionUtils.isEmpty(insertPayList)) {
                payDetailsDao.insertList(insertPayList);
            }
            if (!CollectionUtils.isEmpty(updatePayList)) {
                for (OrderPayDetails pay : updatePayList) {
                    payDetailsDao.update(pay);
                }
            }
        } catch (Exception e) {
            logger.error("OrderManager[updateOrder]failed!cause by:{0}", e);
            throw new RuntimeException(e.getMessage());
        }
    }

}
