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

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.tmerclub.cloud.api.delivery.dto.DeliveryOrderDTO;
import com.tmerclub.cloud.api.delivery.dto.DeliveryOrderItemDTO;
import com.tmerclub.cloud.api.delivery.vo.DeliveryCompanyExcelVO;
import com.tmerclub.cloud.api.feign.EsOrderFeignClient;
import com.tmerclub.cloud.api.order.constant.OrderStatus;
import com.tmerclub.cloud.common.constant.Constant;
import com.tmerclub.cloud.common.constant.SysTypeEnum;
import com.tmerclub.cloud.common.order.bo.EsOrderBO;
import com.tmerclub.cloud.common.order.constant.DeliveryType;
import com.tmerclub.cloud.common.order.constant.OrderActivityType;
import com.tmerclub.cloud.common.order.dto.OrderSearchDTO;
import com.tmerclub.cloud.common.order.vo.OrderItemVO;
import com.tmerclub.cloud.common.order.vo.OrderVO;
import com.tmerclub.cloud.common.security.AuthUserContext;
import com.tmerclub.cloud.common.util.ExcelUtil;
import com.tmerclub.cloud.common.util.PrincipalUtil;
import com.tmerclub.cloud.delivery.manager.DeliveryManager;
import com.tmerclub.cloud.order.constant.OrderExportError;
import com.tmerclub.cloud.order.service.OrderExcelService;
import com.tmerclub.cloud.order.service.OrderItemService;
import com.tmerclub.cloud.order.service.OrderService;
import com.tmerclub.cloud.order.vo.SupplierUnDeliveryOrderExcelVO;
import com.tmerclub.cloud.order.vo.UnDeliveryOrderExcelVO;
import jakarta.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

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

/**
 * 订单excel
 *
 * @author Pineapple
 * @date 2021/7/20 8:48
 */
@Service
public class OrderExcelServiceImpl implements OrderExcelService {
    @Autowired
    private OrderService orderService;
    @Autowired
    private DeliveryManager deliveryManager;
    @Autowired
    private OrderItemService orderItemService;

    @DubboReference
    private EsOrderFeignClient esOrderFeignClient;

    /**
     * 配送类型
     */
    public static final int DELIVERY_MODE_INDEX = 2;

    /**
     * 快递公司名称
     */
    public static final int DELIVERY_COMPANY_NAME_INDEX = 3;

    private static final int FLOW_ID_MIN = 4;
    private static final int FLOW_ID_MAX = 30;

    public static final String[] DELIVERY_MODE = {DeliveryType.DELIVERY.description(), DeliveryType.NOT_DELIVERY.description()};

    private static final Logger log = LoggerFactory.getLogger(OrderExcelServiceImpl.class);

    @Override
    public void excelUnDeliveryOrderList(HttpServletResponse response, OrderSearchDTO orderSearchDTO) {
        List<EsOrderBO> esOrderBOList = esOrderFeignClient.getUndeliveryOrder(orderSearchDTO);
        log.info("查询未发货订单列表，参数：{}，结果：{}", orderSearchDTO, esOrderBOList);
        List<UnDeliveryOrderExcelVO> unDeliveryOrderList = esOrderBOList.stream().map(esOrderVO -> {
            UnDeliveryOrderExcelVO unDeliveryOrderExcelVO = new UnDeliveryOrderExcelVO();
            unDeliveryOrderExcelVO.setOrderId(esOrderVO.getOrderId().toString());
            unDeliveryOrderExcelVO.setDeliveryType(String.valueOf(esOrderVO.getDeliveryType()));
            unDeliveryOrderExcelVO.setMobile(esOrderVO.getMobile());
            unDeliveryOrderExcelVO.setConsignee(esOrderVO.getConsignee());
            if (Objects.nonNull(esOrderVO.getAddr())) {
                unDeliveryOrderExcelVO.setReceivingAddr(esOrderVO.getProvince() + esOrderVO.getCity() + esOrderVO.getArea() + esOrderVO.getAddr());
            }

            List<OrderItemVO> orderItemVOList = esOrderVO.getOrderItems().stream().map(esOrderItemBO -> {
                OrderItemVO orderItemVO = new OrderItemVO();
                orderItemVO.setOrderItemId(esOrderItemBO.getOrderItemId());
                orderItemVO.setSpuName(esOrderItemBO.getSpuNameZh());
                orderItemVO.setOrderId(esOrderItemBO.getOrderId());
                orderItemVO.setSkuName(esOrderItemBO.getSkuNameZh());
                orderItemVO.setCount(esOrderItemBO.getCount());
                orderItemVO.setGiveawayAmount(esOrderItemBO.getGiveawayAmount());
                orderItemVO.setActivityId(esOrderItemBO.getActivityId());
                orderItemVO.setActivityType(esOrderItemBO.getActivityType());
                orderItemVO.setBeDeliveredNum(esOrderItemBO.getBeDeliveredNum());
                return orderItemVO;
            }).collect(Collectors.toList());
            unDeliveryOrderExcelVO.setOrderItemList(orderItemVOList);
            return unDeliveryOrderExcelVO;
        }).collect(Collectors.toList());


        List<UnDeliveryOrderExcelVO> excelVOList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        Map<Integer, String[]> map = new HashMap<>(8);
        map.put(DELIVERY_MODE_INDEX, DELIVERY_MODE);
        int index = 0;
        //合并单元格
        Long orderId = null;
        for (UnDeliveryOrderExcelVO orderExcel : unDeliveryOrderList) {
            List<OrderItemVO> orderItemList = orderExcel.getOrderItemList();
            if (CollectionUtil.isEmpty(orderItemList)) {
                continue;
            }
            if (!Objects.equals(Long.valueOf(orderExcel.getOrderId()), orderId)) {
                index++;
                orderId = Long.valueOf(orderExcel.getOrderId());
            }
            orderExcel.setSeq(String.valueOf(index));
            //物流信息
            orderExcel.setDeliveryType(DeliveryType.getDescription(Integer.valueOf(orderExcel.getDeliveryType())));
            //快递公司名称
            map.put(DELIVERY_COMPANY_NAME_INDEX, getDeliveryCompanyName());

            handleSpuExcelInfo(excelVOList, orderItemList, orderExcel);
        }
        writeExcel(response, excelVOList, map);
    }

    private void handleSpuExcelInfo(List<UnDeliveryOrderExcelVO> excelVOList, List<OrderItemVO> orderItemVOList, UnDeliveryOrderExcelVO orderExcel) {
        Map<Long, List<OrderItemVO>> map = orderItemVOList.stream().filter(orderItemVO -> Objects.equals(orderItemVO.getActivityType(), OrderActivityType.GIVEAWAY.value()))
                .collect(Collectors.groupingBy(OrderItemVO::getActivityId));

        for (OrderItemVO orderItemVO : orderItemVOList) {
            // 赠品订单项不需要进行处理
            // TODO 组合待测试
            if (Objects.equals(orderItemVO.getActivityType(), OrderActivityType.GIVEAWAY.value())) {
                continue;
            }
            int num = 0;
            if (map.containsKey(orderItemVO.getOrderItemId())) {
                for (OrderItemVO giveawayVO : map.get(orderItemVO.getOrderItemId())) {
                    num++;
                    UnDeliveryOrderExcelVO unOrderExcel = new UnDeliveryOrderExcelVO();
                    addNewExcelVo(unOrderExcel, orderItemVO, orderExcel);
                    String giveawayName = giveawayVO.getSpuName() + (StrUtil.isBlank(giveawayVO.getSkuName()) ? "" : "  " + giveawayVO.getSkuName());
                    unOrderExcel.setGiveawayName(giveawayName);
                    unOrderExcel.setGiveawayCount(giveawayVO.getBeDeliveredNum());
                    if (num > 1) {
                        unOrderExcel.setSpuName(null);
                        unOrderExcel.setSkuName(null);
                        unOrderExcel.setCount(null);
                    }
                    excelVOList.add(unOrderExcel);
                }
            } else {
                UnDeliveryOrderExcelVO unOrderExcel = new UnDeliveryOrderExcelVO();
                addNewExcelVo(unOrderExcel, orderItemVO, orderExcel);
                excelVOList.add(unOrderExcel);
            }
        }
    }

    private void addNewExcelVo(UnDeliveryOrderExcelVO unOrderExcel, OrderItemVO orderItemVO, UnDeliveryOrderExcelVO orderExcel) {
        unOrderExcel.setOrderId(orderExcel.getOrderId());
        unOrderExcel.setSeq(orderExcel.getSeq());
        unOrderExcel.setDeliveryType(orderExcel.getDeliveryType());
        unOrderExcel.setDeliveryCompanyName(orderExcel.getDeliveryCompanyName());
        unOrderExcel.setDeliveryNo(orderExcel.getDeliveryNo());
        unOrderExcel.setConsignee(orderExcel.getConsignee());
        unOrderExcel.setMobile(orderExcel.getMobile());
        unOrderExcel.setReceivingAddr(orderExcel.getReceivingAddr());
        unOrderExcel.setSpuName(orderItemVO.getSpuName());
        unOrderExcel.setSkuName(orderItemVO.getSkuName());
        unOrderExcel.setCount(orderItemVO.getBeDeliveredNum());
    }


    @Override
    public void exportOrderExcel(List<UnDeliveryOrderExcelVO> list, Map<Integer, List<String>> errorMap) {
        List<DeliveryCompanyExcelVO> companyExcelList = deliveryManager.listDeliveryCompany();
        Map<String, Long> companyNameMap = companyExcelList.stream().collect(Collectors.toMap(DeliveryCompanyExcelVO::getName, DeliveryCompanyExcelVO::getDeliveryCompanyId));
        Map<String, List<UnDeliveryOrderExcelVO>> orderMap = list.stream().collect(Collectors.groupingBy(UnDeliveryOrderExcelVO::getSeq));
        for (String seq : orderMap.keySet()) {
            List<UnDeliveryOrderExcelVO> orderExcels = orderMap.get(seq);
            DeliveryOrderDTO deliveryOrder = new DeliveryOrderDTO();
            try {
                loadOrderData(orderExcels, deliveryOrder, companyNameMap, errorMap);
            } catch (Exception e) {
                addOrderExcelError(errorMap, OrderExportError.OTHER, e.getMessage());
                continue;
            }
            if (Objects.nonNull(deliveryOrder.getSelectOrderItems())) {
                try {
                    //订单发货
                    orderService.delivery(deliveryOrder);
                } catch (Exception e) {
                    addOrderExcelError(errorMap, OrderExportError.ORDER_ID, seq);
                }
            }
        }
    }

    @Override
    public String orderExportError(Map<Integer, List<String>> errorMap) {
        StringBuilder info = new StringBuilder(Constant.INITIAL_CAPACITY);
        for (Integer key : errorMap.keySet()) {
            OrderExportError orderExportError = OrderExportError.instance(key);
            List<String> list = errorMap.get(key);
            if (Objects.equals(orderExportError, OrderExportError.OTHER)) {
                info.append(list.toString()).append("\n");
                continue;
            }
            info.append(Objects.requireNonNull(orderExportError).errorInfo()).append(list.toString()).append("\n");
        }
        return info.toString();
    }

    private void loadOrderData(List<UnDeliveryOrderExcelVO> orderExcels, DeliveryOrderDTO deliveryOrder, Map<String, Long> companyNameMap, Map<Integer, List<String>> errorMap) {
        UnDeliveryOrderExcelVO orderExcel = orderExcels.get(0);
        //订单id不能为空且该订单必须为当前店铺的订单，并为待发货状态
        if (Objects.isNull(orderExcel.getOrderId())) {
            addOrderExcelError(errorMap, OrderExportError.ORDER_ID, orderExcel.getSeq());
            return;
        }
        OrderVO order = orderService.getOrderByOrderId(Long.valueOf(orderExcel.getOrderId()));
        if (Objects.isNull(order) || !Objects.equals(order.getStatus(), OrderStatus.PAYED.value())) {
            addOrderExcelError(errorMap, OrderExportError.ORDER_ID, orderExcel.getSeq());
            return;
        }
        deliveryOrder.setOrderAddrId(order.getOrderAddrId());
        List<OrderItemVO> orderItemVOList = orderItemService.listOrderItemAndLangByOrderId(Long.valueOf(orderExcel.getOrderId()));
        for (OrderItemVO orderItemVO : orderItemVOList) {
            if (Objects.equals(orderItemVO.getBeDeliveredNum(), 0) || !Objects.equals(orderItemVO.getBeDeliveredNum(), orderItemVO.getCount())) {
                //如果该订单已经被部分发货或全部发货则不予发货
                addOrderExcelError(errorMap, OrderExportError.BE_DELIVERY, orderExcel.getSeq());
                return;
            }
        }
        deliveryOrder.setOrderId(Long.valueOf(orderExcel.getOrderId()));
        //配送方式不为空
        if (Objects.isNull(orderExcel.getDeliveryType())) {
            addOrderExcelError(errorMap, OrderExportError.DELIVERY_TYPE, orderExcel.getSeq());
            return;
        }
        if (Objects.equals(orderExcel.getDeliveryType(), DeliveryType.NOT_DELIVERY.description())) {
            deliveryOrder.setDeliveryType(DeliveryType.NOT_DELIVERY.value());
        } else if (Objects.equals(orderExcel.getDeliveryType(), DeliveryType.DELIVERY.description())) {
            deliveryOrder.setDeliveryType(DeliveryType.DELIVERY.value());
        } else {
            addOrderExcelError(errorMap, OrderExportError.DELIVERY_TYPE, orderExcel.getSeq());
            return;
        }
        //订单的配送方式为快递配送时，导入的配送方式可选择快递配送和无需快递，其他情况订单的配送方式与导入的配送方式需保持一致
        if (!Objects.equals(deliveryOrder.getDeliveryType(), order.getDeliveryType()) && Objects.equals(order.getDeliveryType(), DeliveryType.DELIVERY.value())) {
            if (!Objects.equals(deliveryOrder.getDeliveryType(), DeliveryType.NOT_DELIVERY.value())) {
                addOrderExcelError(errorMap, OrderExportError.DELIVERY_TYPE, orderExcel.getSeq());
                return;
            }
        } else if (!Objects.equals(deliveryOrder.getDeliveryType(), order.getDeliveryType()) && !Objects.equals(order.getDeliveryType(), DeliveryType.DELIVERY.value())) {
            addOrderExcelError(errorMap, OrderExportError.DELIVERY_TYPE, orderExcel.getSeq());
            return;
        }

        //只有快递才需要快递校验
        if (Objects.equals(orderExcel.getDeliveryType(), DeliveryType.DELIVERY.description())) {
            //快递公司校验
            if (Objects.isNull(orderExcel.getDeliveryCompanyName())) {
                addOrderExcelError(errorMap, OrderExportError.DELIVERY_COMPANY_NAME, orderExcel.getSeq());
                return;
            }
            Long deliveryCompanyId = companyNameMap.get(orderExcel.getDeliveryCompanyName());
            deliveryOrder.setDeliveryCompanyId(deliveryCompanyId);
            //快递单号校验
            String deliveryNo = orderExcel.getDeliveryNo();
            deliveryNo = StringUtils.deleteWhitespace(deliveryNo);
            boolean isTrue = StrUtil.isBlank(deliveryNo) ||
                    !PrincipalUtil.isMatching(PrincipalUtil.WITHOUT_CHINESE, deliveryNo)
                    || (FLOW_ID_MAX < deliveryNo.length() || FLOW_ID_MIN > deliveryNo.length());
            if (isTrue) {
                addOrderExcelError(errorMap, OrderExportError.DELIVERY_NO, orderExcel.getSeq());
                return;
            }
            deliveryOrder.setDeliveryNo(deliveryNo);
        }
        List<DeliveryOrderItemDTO> orderItemFeignVOList = new ArrayList<>(Constant.INITIAL_CAPACITY);
        for (OrderItemVO orderItemVO : orderItemVOList) {
            DeliveryOrderItemDTO deliveryOrderItemFeignVO = new DeliveryOrderItemDTO();
            deliveryOrderItemFeignVO.setOrderItemId(orderItemVO.getOrderItemId());
            deliveryOrderItemFeignVO.setChangeNum(orderItemVO.getBeDeliveredNum());
            deliveryOrderItemFeignVO.setPic(orderItemVO.getPic());
            deliveryOrderItemFeignVO.setSpuName(orderItemVO.getSpuName());
            deliveryOrderItemFeignVO.setSupplierPic(orderItemVO.getPic());
            deliveryOrderItemFeignVO.setSupplierSpuName(orderItemVO.getSpuName());
            orderItemFeignVOList.add(deliveryOrderItemFeignVO);
        }
        deliveryOrder.setSelectOrderItems(orderItemFeignVOList);
    }

    private void addOrderExcelError(Map<Integer, List<String>> errorMap, OrderExportError orderExportError, String error) {
        List<String> list = errorMap.get(orderExportError.value());
        if (CollUtil.isEmpty(list)) {
            list = new ArrayList<>(0);
            errorMap.put(orderExportError.value(), list);
        }
        list.add(error);
    }

    private String[] getDeliveryCompanyName() {
        //快递公司名称
        List<DeliveryCompanyExcelVO> companyExcelList = deliveryManager.listDeliveryCompany();
        if (!CollUtil.isEmpty(companyExcelList)) {
            String[] companyNames = new String[companyExcelList.size()];
            for (int i = 0; i < companyExcelList.size(); i++) {
                DeliveryCompanyExcelVO companyExcel = companyExcelList.get(i);
                companyNames[i] = companyExcel.getName();
            }
            return companyNames;
        }
        return null;
    }

    private void writeExcel(HttpServletResponse response, List<UnDeliveryOrderExcelVO> list, Map<Integer, String[]> map) {
        ExcelWriter excelWriter = null;
        Integer sysType = AuthUserContext.get().getSysType();
        try {
            ExcelWriterBuilder excelWriterMerge;
            WriteSheet writeSheet;
            if (Objects.equals(SysTypeEnum.SUPPLIER.value(), sysType)) {
                excelWriterMerge = ExcelUtil.getExcelWriterMerge(response, SupplierUnDeliveryOrderExcelVO.EXCEL_NAME, SupplierUnDeliveryOrderExcelVO.MERGE_ROW_INDEX,
                        SupplierUnDeliveryOrderExcelVO.MERGE_COLUMN_INDEX);
                writeSheet = EasyExcel.writerSheet(SupplierUnDeliveryOrderExcelVO.EXCEL_NAME).head(SupplierUnDeliveryOrderExcelVO.class).build();
            } else if (Objects.equals(SysTypeEnum.PLATFORM.value(), sysType)) {
                excelWriterMerge = ExcelUtil.getExcelWriterMerge(response, UnDeliveryOrderExcelVO.EXCEL_NAME, UnDeliveryOrderExcelVO.MERGE_ROW_INDEX,
                        UnDeliveryOrderExcelVO.MERGE_COLUMN_INDEX);
                writeSheet = EasyExcel.writerSheet(UnDeliveryOrderExcelVO.EXCEL_NAME)
                        .head(UnDeliveryOrderExcelVO.class)
                        .excludeColumnFieldNames(Arrays.asList("giveawayName", "giveawayCount")).build();
            }else {
                excelWriterMerge = ExcelUtil.getExcelWriterMerge(response, UnDeliveryOrderExcelVO.EXCEL_NAME, UnDeliveryOrderExcelVO.MERGE_ROW_INDEX,
                        UnDeliveryOrderExcelVO.MERGE_COLUMN_INDEX);
                writeSheet = EasyExcel.writerSheet(UnDeliveryOrderExcelVO.EXCEL_NAME).head(UnDeliveryOrderExcelVO.class).build();
            }
            excelWriter = ExcelUtil.getExcelModel(excelWriterMerge, map, Constant.START_ROW).build();
            excelWriter.write(list, writeSheet);
        } catch (Exception e) {
            log.error("导出excel失败", e);
        } finally {
            if (excelWriter != null) {
                excelWriter.finish();
            }
        }
    }
}
