package com.bohuikeji.delivery.backstage.excel;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSONObject;
import com.bohuikeji.delivery.backstage.entity.TCustomerBig;
import com.bohuikeji.delivery.backstage.entity.TOrder;
import com.bohuikeji.delivery.backstage.entity.TOrderTemplate;
import com.bohuikeji.delivery.backstage.service.ITLogisticsService;
import com.bohuikeji.delivery.backstage.service.ITOrderService;
import com.bohuikeji.delivery.backstage.thread.AddressGeoThread;
import com.bohuikeji.frame.module.admin.thread.CachedThreadPool;
import com.bohuikeji.delivery.backstage.thread.OrderSaveThread;
import com.bohuikeji.frame.module.admin.component.RedisUtil;
import com.bohuikeji.frame.module.admin.entity.vo.AddressVo;
import com.bohuikeji.frame.module.admin.exception.WebVerifiedException;
import com.bohuikeji.frame.module.admin.utils.MapUtils;
import com.bohuikeji.frame.module.admin.utils.NumberUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import java.math.BigDecimal;
import java.text.MessageFormat;
import java.util.HashMap;
import java.util.Map;

/**
 * Excel行读取监听器
 * 批量订单对象
 * @author 10552
 */
@Slf4j
@Data
public class OrderDataListener extends AnalysisEventListener<OrderDom> {
    private RedisUtil redisUtil;
    private TOrderTemplate template;
    private TCustomerBig big;
    private ITOrderService orderService;
    private ITLogisticsService logisticsService;
    private String batchNum;
    private String rmk;
    /**
     * 缓存key
     */
    String redisKey;

    /**
     * Redis持久层对象传入
     * @param redisUtil
     */
    public OrderDataListener(RedisUtil redisUtil, ITOrderService orderService, ITLogisticsService logisticsService,TOrderTemplate template, TCustomerBig big, String batchNum, String rmk) {
        this.redisUtil = redisUtil;
        this.template=template;
        this.big=big;
        this.orderService=orderService;
        this.logisticsService=logisticsService;
        this.batchNum=batchNum;
        this.rmk=rmk;
        this.redisKey="batchOrder:"+batchNum;
    }

    /**
     * 每隔1000条存储到redis，实际使用中可以3000条，然后清理list ，方便内存回收
     */
    private static final int BATCH_COUNT = 5;
    /**
     * 订单数量累加
     */
    private Integer orderCount=0;
    /**
     * 包裹数量
     */
    private Integer logisticsCount=0;
    /**
     * Excel行索引
     */
    private Integer index=0;
    /**
     * 寄件地址缓存表
     * 缓存5个寄件地址
     */
    private Map<String, AddressVo> mailingAddressCache=new HashMap<>(5);

    /**
     * 寄件地址缓存
     */
    String mailingAddress;
    AddressVo mailingAddressVo;

    Long startTime=System.currentTimeMillis();

    @Override
    public void invoke(OrderDom orderDom, AnalysisContext analysisContext) {
        log.info("读取到一行数据：{}",orderDom);
        index=analysisContext.readRowHolder().getRowIndex()+1;
        String errorStr=orderDom.checkOrder(index);
        //解析检查
        if(errorStr!=null){
            throw new WebVerifiedException(errorStr);
        }
        /**
         * 解析寄件地址
         */
        mailingAddress=orderDom.getMailingAddress();
        //读取寄件地址缓存
        mailingAddressVo=mailingAddressCache.get(mailingAddress);
        if(mailingAddressVo==null){
            try {
                mailingAddressVo=MapUtils.getAddressGeo(mailingAddress);
            }
            catch (WebVerifiedException e){
                throw new WebVerifiedException(MessageFormat.format("【解析错误】寄件地址解析错误，不是合法的地址信息，位于第{0}行",index));
            }
            mailingAddressCache.put(mailingAddress,mailingAddressVo);
        }
        /**
         * 配置订单,填充订单其他信息
         */
        TOrder order=orderDom.coverToOrder();
        order.setOrderNum(NumberUtils.get("B"));
        order.setOrderBatchNum(batchNum);
        order.setMailingAddress(mailingAddressVo);
        template.pushOrder(order);
        order.setOrderBatchPlayerId(big.getBigId());
        order.setPlayerId(big.getCustomerId());
        order.setFranId(big.getFranId());
        //设置订单预估金额为客户签约金额
        order.setOrderExpectPrice(new BigDecimal(big.getBigPrice()).setScale(2, BigDecimal.ROUND_DOWN));
        try {
            //使用子线程解析收件地址，因为地址解析需要调用第三方接口，非常耗时，所以此处使用线程池处理
            CachedThreadPool.execute(new AddressGeoThread(order,redisUtil,orderDom.getReceiptAddress(),redisKey,index));
        } catch (Exception e) {
            log.error("批量下单——收件地址解析异常，线程创建失败",e);
        }
        orderCount+=1;
        logisticsCount+=order.getPackageCount();
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        //正常订单列表
        long orderSize=redisUtil.listSize(redisKey);
        //异常列表
        long errorSize=redisUtil.listSize("batchError:"+batchNum);
        //上次缓存
        long lastSize=orderSize;
        //循环检查子线程是否全部执行完毕
        while(true){
            log.info("当前订单数量：{}，正常订单大小：{}，异常订单大小：{}",orderCount,orderSize,errorSize);
            //当 订单数量=解析正常订单数量+解析异常订单数量 时代表子线程已处理完成，结束循环
            if(orderCount<=orderSize+errorSize){break;}
            else{
                //每隔500毫秒检查一次
                try {
                    Thread.sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                //刷新Redis
                orderSize=redisUtil.listSize(redisKey);
                errorSize=redisUtil.listSize("batchError:"+batchNum);
                if(lastSize==orderSize){
                    log.error("订单导出死循环：当前订单数量：{}，正常订单大小：{}，异常订单大小：{}",orderCount,orderSize,errorSize);
                    redisUtil.del(redisKey);
                    break;
                }
            }

        }
        if(errorSize>0){
            log.warn("发现异常订单，取消执行存储，异常订单数量：[{}]，批量单号：[{}]",errorSize,batchNum);
            //清除本次导入的正常订单，保留异常信息由调用者查询返回到页面
            redisUtil.del(redisKey);
            return;
        }
        /**
         * 分批保存订单
         */
        //每页大小
        int pageSize = 10;
        //共有多少页
        int pageCount=orderCount/pageSize;
        for (int i=1;i<=pageCount+1;i++){
            int start = pageSize * (i - 1);
            // 因为redis中list元素位置基数是0
            int end = start + pageSize - 1;
            try {
                //同样，使用线程池保存到数据库
                CachedThreadPool.execute(new OrderSaveThread(orderService,logisticsService,redisUtil.getListByRang(redisKey,start,end,TOrder.class)));
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        log.info("批量下单解析完成,时间:{}",System.currentTimeMillis()-startTime);
        //下单完成，清理Redis
        redisUtil.del(redisKey,"batchError:"+batchNum);

        JSONObject result=new JSONObject();
        //总共导入多少个订单
        result.put("orderCount",orderCount);
        //总共多少个包裹
        result.put("logisticsCount",logisticsCount);
        //导入统计
        redisUtil.set(batchNum+"Result",result,10);
    }
    @Override
    public void onException(Exception exception, AnalysisContext context) {
        /*log.error("订单抛出异常,位于第[{}],行，第[{列}]",context.readRowHolder().getRowIndex());
        if (exception instanceof ExcelDataConvertException) {
            ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException)exception;
            log.error("第{}行，第{}列解析异常", excelDataConvertException.getRowIndex(),
                    excelDataConvertException.getColumnIndex());
        }*/
        throw new WebVerifiedException("Excel表格数据格式有误，位于第："+(context.readRowHolder().getRowIndex()+1)+"行，请检查包裹数量、电话号码等填写是否正常，该行数据是否有空缺！");
    }
}
