package com.zm.order.tool.server.service.impl;

import com.alibaba.fastjson.JSON;
import com.github.dozermapper.core.Mapper;
import com.google.common.base.Throwables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.zm.order.tool.server.constants.CommonConstants;
import com.zm.order.tool.server.dao.OrderDao;
import com.zm.order.tool.server.dao.OrderRefundDetailsDao;
import com.zm.order.tool.server.dao.RefundDao;
import com.zm.order.tool.server.dto.RefundDTO;
import com.zm.order.tool.server.dto.RefundPayDetailDTO;
import com.zm.order.tool.server.dto.SearchOrderDTO;
import com.zm.order.tool.server.manager.RedisManager;
import com.zm.order.tool.server.manager.SearchManager;
import com.zm.order.tool.server.manager.SearchRefundManager;
import com.zm.order.tool.server.model.*;
import com.zm.order.tool.server.query.OrderQuery;
import com.zm.order.tool.server.query.RefundQuery;
import com.zm.order.tool.server.query.SearchOrderQuery;
import com.zm.order.tool.server.service.ExceptionConstants;
import com.zm.order.tool.server.service.RefundService;
import com.zm.order.tool.server.utils.ConvertUtils;
import com.zm.util.dto.Result;
import org.apache.lucene.util.NamedThreadFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.*;
import java.util.stream.Collectors;

/**
 * @author zhuxiaofeng
 * @packageName: com.zm.order.tool.server.service.impl
 * @className: RefundServiceImpl
 * @date 2022/11/22 10:47
 */
@Service
@SuppressWarnings("all")
public class RefundServiceImpl implements RefundService, InitializingBean {

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

    @Autowired
    private RefundDao refundDao;

    @Autowired
    private SearchManager searchManager;

    @Autowired
    private OrderRefundDetailsDao refundDetailsDao;

    @Autowired
    private Mapper mapper;

    @Autowired
    private OrderDao orderDao;

    @Autowired
    private SearchRefundManager refundManager;

    private BlockingQueue workQueue;

    private ExecutorService executorService;

    @Autowired
    private RedisManager redisManager;

    @Override
    public void syncEs() {
        try {
            int size = 200;
            int count = 0;
            RefundQuery query = new RefundQuery();
            query.setSize(size);
            query.setUpdatedAtStart(new Date(1713196200000L));
            query.setUpdatedAtEnd(new Date(1713196800000L));
            Long maxId = null;
            Long lastId = null;
            for (; ; ) {
                query.setLastId(lastId);
                List<Refund> refundList = refundDao.selectRespIdList(query);
                if (CollectionUtils.isEmpty(refundList)) {
                    break;
                }

                exec(refundList);

                count += refundList.size();
                logger.info("syncRefundEs[running], count:{}, lastId:{}", count, lastId);
                if (refundList.size() < size) {
                    break;
                }
                Result<String> redisData = redisManager.get("refund_sync_es_001");
                if (redisData.isStatus() && Objects.nonNull(redisData.getData())) {
                    logger.info("[refundSyncEs] shut down！");
                    return;
                }
                lastId = refundList.get(refundList.size() - 1).getId();
            }
            logger.info("syncRefundEs[end], count:{}, maxId:{}", count, maxId);
        } catch (Throwable e) {
            logger.error("syncRefundEs[error] failed!, cause by:{}", e);
        }
    }

    private void exec0(List<Refund> refundList) {
        while (workQueue.remainingCapacity() < 10) {
            logger.info("workQueue size:{}", workQueue.size());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        executorService.submit(() -> {
            try {
                refundDao.updateVersion(refundList);
            } catch (Exception e) {
                logger.error("[refundExec]failed！cause by:{}", e);
            }
        });
    }

    private void exec(List<Refund> refundList) {
        while (workQueue.remainingCapacity() < 10) {
            logger.info("workQueue size:{}", workQueue.size());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        executorService.submit(() -> {
            try {
                List<RefundIndex> l = new ArrayList<>();
                for (Refund refund : refundList) {
                    RefundIndex index = mapper.map(refund, RefundIndex.class);
                    index.setDocId(refund.getId());
                    if (!CollectionUtils.isEmpty(refund.getExtraMap())) {
                        Map<String, Object> extraMap = refund.getExtraMap();
                        if (Objects.nonNull(extraMap.get("from"))) {
                            index.setGoodsOrderSource(Integer.valueOf(extraMap.get("from").toString()));
                        }
                    }
                    l.add(index);
                }
                searchManager.indexRefundMsg(l);
            } catch (Exception e) {
                logger.error("[refundExec]failed！cause by:{}", e);
            }
        });
    }


    @Override
    public Result<List<RefundDTO>> listRefund(RefundQuery refundQuery) {
        Result<List<RefundDTO>> result = new Result<>();
        try {
            // 1.参数校验
            // 2.查询数据
            List<Refund> refundList = refundDao.selectList(refundQuery);
            List<RefundDTO> refundDtoList = this.convertRefundList(refundList, refundQuery);
            // 3.组装返回
            result.setData(refundDtoList);
            result.setStatus(true);
        } catch (Throwable throwable) {
            logger.error("RefundReadServiceImpl[listRefund] failed, request:{}, cause by:{}",
                    refundQuery.toString(), Throwables.getStackTraceAsString(throwable));
            result.setException(ExceptionConstants.SYSTEM_ERROR);
        }
        return result;
    }


    public List<RefundDTO> convertRefundList(List<Refund> refundList, RefundQuery refundQuery) throws IOException {
        if (CollectionUtils.isEmpty(refundList)) {
            return Collections.emptyList();
        }
        List<RefundDTO> list = Lists.newArrayListWithCapacity(refundList.size());
        List<String> orderNoList = refundList.stream().map(Refund::getOrderNo).collect(Collectors.toList());
        // 传了userId就走数据库，否则走搜索
        if (Objects.nonNull(refundQuery.getUserId())) {
            OrderQuery orderQuery = new OrderQuery();
            orderQuery.setUserId(refundQuery.getUserId());
            orderQuery.setOrderNoList(orderNoList);
            List<Order> orderList = orderDao.selectList(orderQuery);
            Map<String, Order> orderMap = Maps.newHashMapWithExpectedSize(orderList.size());
            orderList.forEach(order -> orderMap.put(order.getOrderNo(), order));
            refundList.forEach(refund -> {
                RefundDTO refundDTO = mapper.map(refund, RefundDTO.class);
                Order order = orderMap.get(refund.getOrderNo());
                if (Objects.nonNull(order)) {
                    refundDTO.setPayStatus(order.getPayStatus());
                    refundDTO.setOrderStatus(order.getStatus());
                    refundDTO.setAfterSaleStatus(order.getAfterSaleStatus());
                    refundDTO.setReceiveStatus(order.getReceiveStatus());
                    refundDTO.setPerformanceStatus(order.getPerformanceStatus());
                    refundDTO.setTotalAmount(order.getTotalAmount());
                    refundDTO.setActualAmount(order.getActualAmount());
                    refundDTO.setFreightAmount(order.getFreightAmount());
                    refundDTO.setDiscountAmount(order.getDiscountAmount());
                    refundDTO.setPackAmount(order.getPackAmount());
                    refundDTO.setIsAlone(order.getIsAlone());
                }
                rebuildRefundDetail(refundDTO, refund);
                list.add(refundDTO);
            });
        } else {
            SearchOrderQuery searchOrderQuery = new SearchOrderQuery();
            searchOrderQuery.setSellerId(refundQuery.getSellerId());
            searchOrderQuery.setOrderNoList(orderNoList);
            SearchOrderDTO searchOrderDTO = searchManager.searchOrder(searchOrderQuery);
            List<BaseIndexOrder> orderList = searchOrderDTO.getOrderList();
            Map<String, BaseIndexOrder> orderMap = Maps.newHashMapWithExpectedSize(orderList.size());
            orderList.forEach(indexOrder -> orderMap.put(indexOrder.getOrderNo(), indexOrder));
            refundList.forEach(refund -> {
                RefundDTO refundDTO = mapper.map(refund, RefundDTO.class);
                BaseIndexOrder indexOrder = orderMap.get(refund.getOrderNo());
                if (Objects.nonNull(indexOrder)) {
                    refundDTO.setPayStatus(indexOrder.getPayStatus());
                    refundDTO.setOrderStatus(indexOrder.getStatus());
                    refundDTO.setAfterSaleStatus(indexOrder.getAfterSaleStatus());
                    refundDTO.setReceiveStatus(indexOrder.getReceiveStatus());
                    refundDTO.setPerformanceStatus(indexOrder.getPerformanceStatus());
                    refundDTO.setTotalAmount(indexOrder.getTotalAmount());
                    refundDTO.setActualAmount(indexOrder.getActualAmount());
                    refundDTO.setFreightAmount(indexOrder.getFreightAmount());
                    refundDTO.setDiscountAmount(indexOrder.getDiscountAmount());
                    refundDTO.setPackAmount(indexOrder.getPackAmount());
                    refundDTO.setIsAlone(indexOrder.getIsAlone());
                }
                rebuildRefundDetail(refundDTO, refund);
                list.add(refundDTO);
            });

        }
        return list;
    }

    private void rebuildRefundDetail(RefundDTO refundDTO, Refund refund) {
        String refundDetailInfo = refund.getRefundDetailInfo();
        if (Objects.equals(CommonConstants.ITEM_FLAG, refundDetailInfo)) {
            List<OrderRefundDetails> refundDetailList = refundDetailsDao.selectByRefundNo(refund.getRefundNo());
            if (!CollectionUtils.isEmpty(refundDetailList)) {
                refundDTO.setRefundDetailsList(ConvertUtils.convertList(refundDetailList, RefundPayDetailDTO.class));
            }
        } else {
            List<RefundPayDetailDTO> rpdList = JSON.parseArray(refundDetailInfo, RefundPayDetailDTO.class);
            refundDTO.setRefundDetailsList(rpdList);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        workQueue = new ArrayBlockingQueue(128);
        executorService = new ThreadPoolExecutor(
                4,
                10,
                1,
                TimeUnit.MINUTES,
                workQueue,
                new NamedThreadFactory("update-order-item-thread"), new ThreadPoolExecutor.AbortPolicy());
    }
}
