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

import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import com.zm.order.tool.server.manager.DistributedCacheManager;
import com.zm.order.tool.server.model.OrderExt;
import com.zm.order.tool.server.model.OrderPayDetails;
import com.zm.order.tool.server.query.OrderQuery;
import com.zm.order.tool.server.tb.TbPayDetailDao;
import com.zm.order.tool.server.tempdb.OrderExtDao;
import com.zm.order.tool.server.utils.DateUtils;
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.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.*;

/**
 * <p>
 *
 * </p>
 *
 * @author nianchang
 * @date 2023/12/08 1:55 下午
 */
@Service
@SuppressWarnings("all")
public class PayDetailScanExtServiceImpl implements InitializingBean {

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

    private BlockingQueue workQueue;

    private ExecutorService executorService;

    @Autowired
    private OrderExtDao extDao;

    @Autowired
    private TbPayDetailDao payDetailDao;

    @Autowired
    private DistributedCacheManager cacheManager;

    private Map<String, Object> extMap = new ConcurrentHashMap<>();
    
    public void scan() {
        try {
            Date sTime = null;
            Date dTime = null;
            String start = "2023-09-01";
            int count = 0;
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
            OrderQuery query = new OrderQuery();
            query.setSize(10000);
            for (;;) {
                Calendar c = getCalendar(start);
                sTime = DateUtils.getStartDate(c.getTime());
                dTime = DateUtils.getEndDate(c.getTime());

                start = sdf.format(c.getTime());

                logger.info("[scan_pay_time]:{}", start);

                if (new Date().before(sTime)) {
                    return;
                }

                query.setOrderAtStart(sTime);
                query.setOrderAtEnd(dTime);

                Long lastId = null;
                for (;;) {
                    query.setLastId(lastId);
                    List<Long> idList = payDetailDao.selectId(query);
                    if (CollectionUtils.isEmpty(idList)) {
                        break;
                    }
                    List<OrderPayDetails> data = new ArrayList<>(10000);
                    List<List<Long>> partition = Lists.partition(idList, 5000);
                    for (List<Long> batch : partition) {
                        data.addAll(payDetailDao.selectById(batch));
                    }

                    exec(data);

                    count += idList.size();
                    logger.info("[pay_scan_ext]count:{}, lastId:{}, start:{}, end:{}",
                            count, lastId, sTime.getTime(), dTime.getTime());

                    if (idList.size() < 10000) {
                        break;
                    }
                    Result<String> redisData = cacheManager.get("scan_pay_ext_key");
                    if (redisData.isStatus() && Objects.nonNull(redisData.getData())) {
                        return;
                    }
                    lastId = idList.get(idList.size() - 1);
                }
            }
        } catch (Exception e) {
            logger.error("[pay_scan]cause by:{}", e);
        } finally {
            logger.info("[payExtMap]extMap:{}", JSON.toJSONString(extMap));
            List<OrderExt> list = new ArrayList<>();
            if (!CollectionUtils.isEmpty(extMap)) {
                extMap.forEach((k ,v) -> {
                    list.add(new OrderExt(k, "order_pay_details"));
                });
            }
            extDao.insertList(list);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        workQueue = new ArrayBlockingQueue(64);
        executorService = new ThreadPoolExecutor(8, 10, 1, TimeUnit.MINUTES,
                workQueue, new NamedThreadFactory("order-routing"),
                new ThreadPoolExecutor.AbortPolicy());
    }

    private Calendar getCalendar(String start) {
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd");
        Date parse = null;
        try {
            parse = sf.parse(start);
        } catch (ParseException ex) {
            ex.printStackTrace();
        }
        Calendar c = Calendar.getInstance();
        c.setTime(parse);
        c.add(Calendar.DAY_OF_MONTH, 1);
        return c;
    }

    private void exec(List<OrderPayDetails> list) {
        while (workQueue.remainingCapacity() < 10) {
            logger.info("workQueue size:{}", workQueue.size());
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        executorService.submit(() -> {
            try {
                for (OrderPayDetails each : list) {
                    extMap.putAll(each.getExtraMap());
                }
            } catch (Exception e) {
                logger.error("[execute]failed！cause by:{}", e);
            }
        });
    }
}
