package com.xxfc.platform.order.biz;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSON;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.github.wxiaoqi.security.common.util.CollectorsUtil;
import com.xxfc.platform.order.bo.LargeScreenDisplayConstantDataBo;
import com.xxfc.platform.order.contant.enumerate.OrderTypeEnum;
import com.xxfc.platform.order.entity.OrderProfileDisplay;
import com.xxfc.platform.order.mapper.OrderProfileDisplayMapper;
import com.xxfc.platform.order.pojo.dto.BaseOrderDTO;
import com.xxfc.platform.order.pojo.vo.*;
import com.xxfc.platform.universal.entity.Dictionary;
import com.xxfc.platform.universal.feign.ThirdFeign;
import com.xxfc.platform.vehicle.feign.VehicleFeign;
import com.xxfc.platform.vehicle.pojo.dto.BranchCompanyAreaDTO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author libin
 * @version 1.0
 * @description
 * @data 2019/12/24 17:06
 */
@Slf4j
@Transactional(rollbackFor = Exception.class)
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class OrderProfileDisplayBiz extends BaseBiz<OrderProfileDisplayMapper, OrderProfileDisplay> {
    /**
     * 字典查询type
     */
    @Value("${large.screen.type:LARGE_SCREEN_DISPLAY}")
    private String largeScreenType;
    /**
     * 字典查询code
     */
    @Value("${large.screen.code:LARGE_SCREEN_DISPLAY_CONSTANT}")
    private String largeScreenCode;

    private final ThirdFeign thirdFeign;
    private final BaseOrderBiz baseOrderBiz;
    private final VehicleFeign vehicleFeign;
    private final CenterOrderProfileDisplayBiz centerOrderProfileDisplayBiz;
    private final ThreadPoolTaskExecutor executor;
    private static final Integer WX_PAY_STATE = 1;
    private static final Integer ALI_PAY_STATE = 2;
    private static final Integer APP_PAY_STATE = 1;
    private static final Integer APPLET_PAY_STATE = 2;
    private static final Integer WECHAT_OFFICIAL_ACCOUNT_PAY_STATE = 3;
    private static final Integer IOS_PAY_STATE = 4;

    /**
     * @param startDate 开始时间
     * @param endDate   结束时间
     * @return
     */
    public LargeScreenDisplayDataVo findLargeScreenDisplayDataByDate(Date startDate, Date endDate) {
        LargeScreenDisplayDataVo largeScreenDisplayDataVo = new LargeScreenDisplayDataVo();
        //创建订单常量数据对象
        LargeScreenDisplayConstantDataBo largeScreenDisplayConstantDataBo = new LargeScreenDisplayConstantDataBo();
        //创建订单数据统计对象
        OrderProfileDispayVo orderProfileDispayVo = new OrderProfileDispayVo();

        //1.查询基础常量数据
        Dictionary largeScreenConstantDictionary = thirdFeign.findDictionaryByTypeAndCode(largeScreenType, largeScreenCode);
        if (Objects.nonNull(largeScreenConstantDictionary) && StringUtils.hasText(largeScreenConstantDictionary.getDetail())) {
            largeScreenDisplayConstantDataBo = JSON.parseObject(largeScreenConstantDictionary.getDetail(), LargeScreenDisplayConstantDataBo.class);
            orderProfileDispayVo.setTodayOrderNum(largeScreenDisplayConstantDataBo.getBaseTodayOrderNum());
            orderProfileDispayVo.setTodayOrderAmount(largeScreenDisplayConstantDataBo.getBaseTodayOrderAmount());
            orderProfileDispayVo.setOrderAmount(largeScreenDisplayConstantDataBo.getBaseOrderAmount());
            orderProfileDispayVo.setOrderNum(largeScreenDisplayConstantDataBo.getBaseOrderNum());
        }

        //2.1查询订单数据
        List<BaseOrderDTO> baseOrders = baseOrderBiz.findOrdersByDate(startDate, endDate);
        //2.2查询固定数据
        Date constantEndDate = DateUtil.offsetDay(endDate, -1).toJdkDate();
        List<OrderProfileDisplay> orderProfileDisplays = findOrderProfileDisplayDataByDate(startDate, constantEndDate);

        CountDownLatch latch = new CountDownLatch(3);
        //3. 十大运营中心订单数据
        executor.execute(() -> {
            try {
                analyticStatisticsCenterOrder(baseOrders, orderProfileDispayVo);
            } catch (Exception ex) {
                log.error("十大运营中心订单统计失败【{}】", ex);
            } finally {
                latch.countDown();
            }
        });
        //4.总的订单数据
        executor.execute(() -> {
            try {
                analyticStatisticsOrderProfiles(baseOrders, orderProfileDisplays, orderProfileDispayVo);
            } catch (Exception ex) {
                log.error("总订单数据解析失败【{}】", ex);
            } finally {
                latch.countDown();
            }
        });
        //5.支付方式|支付终端数据处理
        LargeScreenDisplayConstantDataBo finalLargeScreenDisplayConstantDataBo = largeScreenDisplayConstantDataBo;
        executor.execute(() -> {
            try {
                analyticStatisticsOrderPayWayProfiles(baseOrders, orderProfileDisplays, finalLargeScreenDisplayConstantDataBo,orderProfileDispayVo);
            } catch (Exception ex) {
                log.error("支付方式|支付终端数据处理失败【{}】", ex);
            } finally {
                latch.countDown();
            }
        });
        try {
            latch.await();
        } catch (InterruptedException e) {
            log.error("统计出错：【{}】", e);
        }

        largeScreenDisplayDataVo.setOrderProfileDispayData(orderProfileDispayVo);
        return largeScreenDisplayDataVo;
    }

    /**
     * 支付方式|终端的统计
     *
     * @param orderProfileDispayVo
     * @param largeScreenDisplayConstantDataBo
     * @param baseOrders
     * @return
     */
    private OrderProfileDispayVo analyticStatisticsOrderPayWayProfiles(List<BaseOrderDTO> baseOrders,
                                                                       List<OrderProfileDisplay> orderProfileDisplays,
                                                                       LargeScreenDisplayConstantDataBo largeScreenDisplayConstantDataBo,
                                                                       OrderProfileDispayVo orderProfileDispayVo) {
        OrderPayProfileDispalyVo orderPayProfileDispalyVo = new OrderPayProfileDispalyVo();
        BigDecimal orderAmountConstant = CollectionUtils.isEmpty(orderProfileDisplays) ? BigDecimal.ZERO :
                orderProfileDisplays.stream().map(OrderProfileDisplay::getOrderAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //订单总金额
        orderAmountConstant = orderAmountConstant.add(largeScreenDisplayConstantDataBo.getBaseTodayOrderAmount()).add(largeScreenDisplayConstantDataBo.getBaseOrderAmount());
        //1.支付方式统计
        wrapToPayProfileWithPayWay(baseOrders, orderAmountConstant, largeScreenDisplayConstantDataBo, orderPayProfileDispalyVo);
        //2.支付终端统计
        wrapToPayProfileWithPayTerminal(baseOrders, orderAmountConstant, largeScreenDisplayConstantDataBo, orderPayProfileDispalyVo);

        BigDecimal realOrderAmount = baseOrders.stream().map(BaseOrderDTO::getRealAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
        //累计订单金额
        orderProfileDispayVo.setOrderAmount(orderAmountConstant.add(realOrderAmount));
        orderProfileDispayVo.setOrderPayProfileDisplay(orderPayProfileDispalyVo);
        return orderProfileDispayVo;
    }

    /**
     * 支付方式统计
     *
     * @param baseOrders
     * @param orderAmountConstant
     * @param orderPayProfileDispalyVo
     * @return
     */
    private OrderPayProfileDispalyVo wrapToPayProfileWithPayWay(List<BaseOrderDTO> baseOrders,
                                                                BigDecimal orderAmountConstant,
                                                                LargeScreenDisplayConstantDataBo largeScreenDisplayConstantDataBo,
                                                                OrderPayProfileDispalyVo orderPayProfileDispalyVo) {
        Map<Integer, BigDecimal> payWayMap = CollectionUtils.isEmpty(baseOrders) ? Collections.EMPTY_MAP :
                baseOrders.stream().collect(Collectors.groupingBy(BaseOrderDTO::getPayWay, CollectorsUtil.summingBigDecimal(BaseOrderDTO::getRealAmount)));

        boolean isEmpty = payWayMap.isEmpty();
        BigDecimal wxPayAmount = isEmpty ? BigDecimal.ZERO : payWayMap.get(WX_PAY_STATE) == null ? BigDecimal.ZERO : payWayMap.get(WX_PAY_STATE);
        BigDecimal aliPayAmount = isEmpty ? BigDecimal.ZERO : payWayMap.get(ALI_PAY_STATE) == null ? BigDecimal.ZERO : payWayMap.get(ALI_PAY_STATE);
        //基础金额 * 占比率
        wxPayAmount = wxPayAmount.add(orderAmountConstant.multiply(new BigDecimal(String.valueOf(largeScreenDisplayConstantDataBo.getWxPayRatio()))));
        aliPayAmount = aliPayAmount.add(orderAmountConstant.multiply(new BigDecimal(String.valueOf(largeScreenDisplayConstantDataBo.getAliPayRatio()))));

        BigDecimal totalAmount = wxPayAmount.add(aliPayAmount);
        String wxPayRatio = wxPayAmount.divide(totalAmount,2,BigDecimal.ROUND_HALF_UP).toString();
        String aliPayRatio = aliPayAmount.divide(totalAmount,2,BigDecimal.ROUND_HALF_UP).toString();

        orderPayProfileDispalyVo.setWxPayRatio(wxPayRatio);
        orderPayProfileDispalyVo.setAliPayRatio(aliPayRatio);
        orderPayProfileDispalyVo.setWxPayAmount(wxPayAmount.setScale(2));
        orderPayProfileDispalyVo.setAliPayAmount(aliPayAmount.setScale(2));
        return orderPayProfileDispalyVo;
    }

    /**
     * 支付终端统计
     *
     * @param baseOrders
     * @param orderPayProfileDispalyVo
     * @return
     */
    private OrderPayProfileDispalyVo wrapToPayProfileWithPayTerminal(List<BaseOrderDTO> baseOrders,
                                                                     BigDecimal orderAmountConstant,
                                                                     LargeScreenDisplayConstantDataBo largeScreenDisplayConstantDataBo,
                                                                     OrderPayProfileDispalyVo orderPayProfileDispalyVo) {
        Map<Integer, BigDecimal> payTerminalMap = CollectionUtils.isEmpty(baseOrders) ? Collections.EMPTY_MAP :
                baseOrders.stream().collect(Collectors.groupingBy(BaseOrderDTO::getPayOrigin, CollectorsUtil.summingBigDecimal(BaseOrderDTO::getRealAmount)));
        boolean isEmpty = payTerminalMap.isEmpty();
        BigDecimal androidPayAmount = isEmpty ? BigDecimal.ZERO : payTerminalMap.get(APP_PAY_STATE) == null ? BigDecimal.ZERO : payTerminalMap.get(APP_PAY_STATE);
        BigDecimal appletPayAmount = isEmpty ? BigDecimal.ZERO : payTerminalMap.get(APPLET_PAY_STATE) == null ? BigDecimal.ZERO : payTerminalMap.get(APPLET_PAY_STATE);
        BigDecimal weChatOfficialAccountPayAmount = isEmpty ? BigDecimal.ZERO : payTerminalMap.get(WECHAT_OFFICIAL_ACCOUNT_PAY_STATE) == null ? BigDecimal.ZERO : payTerminalMap.get(WECHAT_OFFICIAL_ACCOUNT_PAY_STATE);
        BigDecimal iosPayAmount = isEmpty ? BigDecimal.ZERO : payTerminalMap.get(IOS_PAY_STATE) == null ? BigDecimal.ZERO : payTerminalMap.get(IOS_PAY_STATE);
        //applet 公众号 划为ios支付
        iosPayAmount = iosPayAmount.add(appletPayAmount).add(weChatOfficialAccountPayAmount);

        //基础金额 * 占比率
        androidPayAmount = androidPayAmount.add(orderAmountConstant.multiply(new BigDecimal(String.valueOf(largeScreenDisplayConstantDataBo.getAndroidPayRatio()))));
        iosPayAmount = iosPayAmount.add(orderAmountConstant.multiply(new BigDecimal(String.valueOf(largeScreenDisplayConstantDataBo.getIosPayRatio()))));

        BigDecimal totalAmount = androidPayAmount.add(iosPayAmount);
        String androidPayRatio = androidPayAmount.divide(totalAmount, 2, BigDecimal.ROUND_HALF_UP).toString();
        String iosPayRatio = iosPayAmount.divide(totalAmount, 2, BigDecimal.ROUND_HALF_UP).toString();

        orderPayProfileDispalyVo.setAndroidPayRatio(androidPayRatio);
        orderPayProfileDispalyVo.setIosPayRatio(iosPayRatio);
        orderPayProfileDispalyVo.setAndroidPayAmount(androidPayAmount.setScale(2));
        orderPayProfileDispalyVo.setIosPayAmount(iosPayAmount.setScale(2));
        return orderPayProfileDispalyVo;
    }

    /**
     * 订单统计解析
     *
     * @param
     * @param orderProfileDispayVo
     * @return
     */
    private OrderProfileDispayVo analyticStatisticsOrderProfiles(List<BaseOrderDTO> baseOrders,
                                                                 List<OrderProfileDisplay> orderProfileDisplays,
                                                                 OrderProfileDispayVo orderProfileDispayVo) {
        //包装数据
        List<BaseOrderDTO> baseOrderDTOS = wrapToBaseOrder(orderProfileDisplays);
        baseOrderDTOS.addAll(baseOrders);
        //1.真实数据按时间分组
        Map<Date, BigDecimal> orderAmountMap = baseOrderDTOS.stream().collect(Collectors.groupingBy(BaseOrderDTO::getPayDate, CollectorsUtil.summingBigDecimal(BaseOrderDTO::getRealAmount)));
        //2.某个时间段的订单统计
        //2.1 订单金额
        List<OrderProfileVo> orderProfileVos = createOrderProfiles(orderAmountMap,orderProfileDispayVo.getTodayOrderAmount());
        //2.2 订单量
        wrapBaseDateToOrderProfileDispay(baseOrders, orderProfileDispayVo);
        //3.今日金额
        Date date = DateUtil.beginOfDay(new Date()).toJdkDate();
        BigDecimal todayOrderAmount = orderAmountMap == null ? BigDecimal.ZERO : orderAmountMap.get(date) == null ? BigDecimal.ZERO : orderAmountMap.get(date);
        //3.1真实金额+基础金额
        todayOrderAmount = todayOrderAmount.add(orderProfileDispayVo.getTodayOrderAmount());

        orderProfileDispayVo.setTodayOrderAmount(todayOrderAmount);
        orderProfileDispayVo.setOrderProfiles(orderProfileVos);
        return orderProfileDispayVo;
    }

    /**
     * 包装基础数据  订单金额 与订单量
     *
     * @param orderProfileDispayVo
     * @return
     */
    private OrderProfileDispayVo wrapBaseDateToOrderProfileDispay(List<BaseOrderDTO> baseOrderDTOS, OrderProfileDispayVo orderProfileDispayVo) {
        Map<Date, Long> orderNumMap = CollectionUtils.isEmpty(baseOrderDTOS) ? Collections.EMPTY_MAP :
                baseOrderDTOS.stream().collect(Collectors.groupingBy(BaseOrderDTO::getPayDate, Collectors.counting()));
        if (orderNumMap == null || orderNumMap.isEmpty()) {
            return orderProfileDispayVo;
        }
        Date date = DateUtil.beginOfDay(new Date()).toJdkDate();
        //今日订单量
        long todayOrderNum = orderNumMap.get(date) == null ? 0 : orderNumMap.get(date);
        //真实订单量 + 基础订单量
        todayOrderNum = todayOrderNum + orderProfileDispayVo.getTodayOrderNum();
        //总订单量
        long totalOrderNum = orderProfileDispayVo.getOrderNum()+baseOrderDTOS.size()+orderProfileDispayVo.getTodayOrderNum();
        orderProfileDispayVo.setTodayOrderNum(todayOrderNum);
        orderProfileDispayVo.setOrderNum(totalOrderNum);
        return orderProfileDispayVo;
    }

    /**
     * 组装对象
     *
     * @param orderAmountMap
     * @return
     */
    private List<OrderProfileVo> createOrderProfiles(Map<Date, BigDecimal> orderAmountMap,BigDecimal baseTodayOrderAmount) {
        List<OrderProfileVo> orderProfileVos = new ArrayList<>();
        if (orderAmountMap == null || orderAmountMap.isEmpty()) {
            return orderProfileVos;
        }
        Set<Map.Entry<Date, BigDecimal>> orderAmountEntrySet = orderAmountMap.entrySet();
        Iterator<Map.Entry<Date, BigDecimal>> orderAmountIterator = orderAmountEntrySet.iterator();
        OrderProfileVo orderProfileVo;
        Date date = DateUtil.beginOfDay(new Date()).toJdkDate();
        boolean isToday = (orderAmountMap==null||orderAmountMap.isEmpty())?false:Objects.nonNull(orderAmountMap.get(date));
        while (orderAmountIterator.hasNext()) {
            Map.Entry<Date, BigDecimal> orderAmountMapEntry = orderAmountIterator.next();
            orderProfileVo = new OrderProfileVo();
            orderProfileVo.setOrderDate(orderAmountMapEntry.getKey());
            BigDecimal orderAmount = orderAmountMapEntry.getValue();
            if (isToday){
                orderAmount =orderAmount.add(baseTodayOrderAmount);
            }
            orderProfileVo.setOrderAmount(orderAmount);
            orderProfileVo.setDate(DateUtil.format(orderProfileVo.getOrderDate(),"MM.dd"));
            orderProfileVos.add(orderProfileVo);
        }

        if (!isToday){
            OrderProfileVo todayOrderProfileVo = new OrderProfileVo();
            todayOrderProfileVo.setOrderDate(date);
            todayOrderProfileVo.setOrderAmount(baseTodayOrderAmount);
            orderProfileVos.add(todayOrderProfileVo);
        }
        orderProfileVos.sort(Comparator.comparing(OrderProfileVo::getOrderDate));
        return orderProfileVos;
    }

    /**
     * 运营中心订单统计
     *
     * @param orderProfileDispayVo
     * @return
     */
    private OrderProfileDispayVo analyticStatisticsCenterOrder(List<BaseOrderDTO> baseOrders,
                                                               OrderProfileDispayVo orderProfileDispayVo) {

         //根据公司id查询公司区域
         Map<Integer,BranchCompanyAreaDTO> companyAreaDTOMap = new HashMap<>(100);
         if (!CollectionUtils.isEmpty(baseOrders)){
             List<Integer> companyIds = baseOrders.stream().map(BaseOrderDTO::getCompanyId).collect(Collectors.toList());
             List<BranchCompanyAreaDTO> branchCompnays = vehicleFeign.findBranchCompnayAreaByIds(companyIds);
              companyAreaDTOMap = branchCompnays.stream().collect(Collectors.toMap(BranchCompanyAreaDTO::getCompanyId, Function.identity()));
         }

        //筛选出租车订单
        Map<Integer, BranchCompanyAreaDTO> finalCompanyAreaDTOMap = companyAreaDTOMap;
        Map<Integer, BigDecimal> areaAmountMap = CollectionUtils.isEmpty(baseOrders) ? Collections.EMPTY_MAP :
                baseOrders.stream().filter(x -> Objects.equals(x.getType(), OrderTypeEnum.RENT_VEHICLE.getCode()))
                        .peek(x->{
                            BranchCompanyAreaDTO branchCompanyAreaDTO = finalCompanyAreaDTOMap.get(x.getCompanyId());
                            x.setAreaId(branchCompanyAreaDTO.getAreaId());
                        })
                        .collect(Collectors.groupingBy(BaseOrderDTO::getAreaId, CollectorsUtil.summingBigDecimal(BaseOrderDTO::getRealAmount)));

        List<CenterOrderProfileDisplayVo> centerOrderProfileDisplayVos = createCenterOrderProfileDisplay(areaAmountMap);
        orderProfileDispayVo.setCenterOrderProfileDisplays(centerOrderProfileDisplayVos);
        return orderProfileDispayVo;
    }

    /**
     * 区域统计处理
     *
     * @param areaAmountMap
     * @return
     */
    private List<CenterOrderProfileDisplayVo> createCenterOrderProfileDisplay(Map<Integer, BigDecimal> areaAmountMap) {
        List<CenterOrderProfileDisplayVo> centerOrderProfileDisplayVos = new ArrayList<>();
        //查询全部区域
        List<CenterOrderProfileDisplayVo> centerOrderProfileDisplays = centerOrderProfileDisplayBiz.findCenterOrderProfileDisplays();

        for (CenterOrderProfileDisplayVo centerOrderProfileDisplay : centerOrderProfileDisplays) {
            Integer areaId = centerOrderProfileDisplay.getAreaId();
            BigDecimal areaAmount = areaAmountMap == null ? BigDecimal.ZERO : areaAmountMap.get(areaId) == null ? BigDecimal.ZERO : areaAmountMap.get(areaId);
            areaAmount = areaAmount.add(centerOrderProfileDisplay.getAreaAmount());
            centerOrderProfileDisplay.setAreaAmount(areaAmount);
            centerOrderProfileDisplayVos.add(centerOrderProfileDisplay);
        }
        return centerOrderProfileDisplayVos;
    }

    /**
     * 包装成baseOrder
     *
     * @param orderProfileDisplays
     * @return
     */
    private List<BaseOrderDTO> wrapToBaseOrder(List<OrderProfileDisplay> orderProfileDisplays) {
        List<BaseOrderDTO> baseOrderDTOS = orderProfileDisplays.stream().map(x -> {
            BaseOrderDTO baseOrderDTO = new BaseOrderDTO();
            baseOrderDTO.setRealAmount(x.getOrderAmount());
            baseOrderDTO.setPayDate(x.getOrderDate());
            return baseOrderDTO;
        }).collect(Collectors.toList());
        return CollectionUtils.isEmpty(baseOrderDTOS) ? new ArrayList<>() : baseOrderDTOS;
    }


    /**
     * 根据开始时间与结束时间查询
     *
     * @param startDate
     * @param endDate
     * @return
     */
    public List<OrderProfileDisplay> findOrderProfileDisplayDataByDate(Date startDate, Date endDate) {
        List<OrderProfileDisplay> orderProfileDisplays = mapper.findOrderProfileDisplayDatabyDate(startDate, endDate);
        return CollectionUtils.isEmpty(orderProfileDisplays) ? Collections.emptyList() : orderProfileDisplays;
    }
}
