package com.github.wxiaoqi.security.admin.biz;

import cn.hutool.core.collection.CollUtil;
import com.github.wxiaoqi.security.admin.bo.StaffStatisticsBo;
import com.github.wxiaoqi.security.admin.bo.StaffStatisticsTotalBo;
import com.github.wxiaoqi.security.admin.bo.UserStaffBo;
import com.github.wxiaoqi.security.admin.dto.AppUserSellingWaterDTO;
import com.github.wxiaoqi.security.admin.dto.StaffStatisticsFindDTO;
import com.github.wxiaoqi.security.admin.vo.WalletPostionVo;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.vo.PageDataVO;
import com.xxfc.platform.order.contant.enumerate.StatisticsStatusEnum;
import com.xxfc.platform.order.feign.OrderFeign;
import com.xxfc.platform.order.pojo.dto.OrderDTO;
import com.xxfc.platform.vehicle.feign.VehicleFeign;
import lombok.RequiredArgsConstructor;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @author libin
 * @version 1.0
 * @description
 * @data 2019/11/26 17:01
 */
@Transactional(rollbackFor = Exception.class)
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class StaffStatisticsBiz {

    private static final String TOTAL_VAL = "total";
    private static final String PAGE_VAL = "page";
    private static final String TOTAL_AMOUNT_VAL = "totalAmount";
    private static final String TOTAL_NUM_VAL = "totalNum";
    private static final String TOTAL_COMMISSION_VAL = "totalCommission";
    private static final Integer NO_COMPANY_STATE = 0;
    private final AppUserDetailBiz appUserDetailBiz;
    private final AppUserSellingWaterBiz appUserSellingWaterBiz;
    private final OrderFeign orderFeign;
    private final VehicleFeign vehicleFeign;
    private final AppUserPositionBiz appUserPositionBiz;

    public Map<String, Object> listStaffStatisticsWithPage(StaffStatisticsFindDTO staffStatisticsFindDTO) {
        Map<String, Object> result = new HashMap<>(2);
        PageDataVO<StaffStatisticsBo> pageDataVO = new PageDataVO<>();
        List<StaffStatisticsBo> staffStatisticsBos = new ArrayList<>();
        //1.分页查询全部员工
        PageDataVO<UserStaffBo> staffPage = appUserDetailBiz.findAllStaffsByCompanyIdAndPostionIdWithPage(staffStatisticsFindDTO);
        List<UserStaffBo> allStaffs = staffPage.getData();
        if (CollectionUtils.isEmpty(allStaffs)) {
            result.put(TOTAL_VAL, new StaffStatisticsTotalBo());
            pageDataVO.setData(Collections.EMPTY_LIST);
            pageDataVO.setPageSize(staffStatisticsFindDTO.getLimit());
            pageDataVO.setPageNum(staffStatisticsFindDTO.getPage());
            pageDataVO.setTotalPage(0);
            pageDataVO.setTotalCount(0L);
            result.put(PAGE_VAL, pageDataVO);
            return result;
        }
        //2.根据员工id范围和其他条件分页查询
        List<Integer> userIds = allStaffs.stream().map(UserStaffBo::getUserId).collect(Collectors.toList());
        List<AppUserSellingWaterDTO> appUserSellingWaterDTOS = appUserSellingWaterBiz.findStatffSellerWaterByUserIdsAndTime(userIds, staffStatisticsFindDTO.getStartDate(), staffStatisticsFindDTO.getEndDate());
        Map<Integer, Map<String, Map<Integer, Map<Integer, List<AppUserSellingWaterDTO>>>>> sellingWaterMap = new HashMap<>();

        if (CollectionUtils.isNotEmpty(appUserSellingWaterDTOS)) {
            //3.根据订单id查询订单相关信息
            List<Integer> orderIds = appUserSellingWaterDTOS.stream().map(AppUserSellingWaterDTO::getOrderId).collect(Collectors.toList());
            ObjectRestResponse<List<OrderDTO>> response =orderFeign.findOrdersByorderId(orderIds);
            List<OrderDTO> orders =response.getData();
            Map<Integer, Integer> ordersMap = orders.stream().collect(Collectors.toMap(OrderDTO::getId, OrderDTO::getCompanyId));
            for (AppUserSellingWaterDTO appUserSellingWaterDTO : appUserSellingWaterDTOS) {
                appUserSellingWaterDTO.setCompanyId(ordersMap.get(appUserSellingWaterDTO.getOrderId()) == null ? NO_COMPANY_STATE : ordersMap.get(appUserSellingWaterDTO.getOrderId()));
                appUserSellingWaterDTO.setStateGroup(String.format("%d-%d-%d", appUserSellingWaterDTO.getUserId(), appUserSellingWaterDTO.getPositionId(), appUserSellingWaterDTO.getCompanyId()));
            }


            sellingWaterMap = appUserSellingWaterDTOS.stream().collect(Collectors.groupingBy(AppUserSellingWaterDTO::getUserId,
                    Collectors.groupingBy(AppUserSellingWaterDTO::getStateGroup, Collectors.groupingBy(AppUserSellingWaterDTO::getOrderType, Collectors.groupingBy(AppUserSellingWaterDTO::getStatus, Collectors.toList())))));
        }

        Map<Integer, String> companyMap = vehicleFeign.findCompanyMap();
        List<WalletPostionVo> allPostions = appUserPositionBiz.findAllPostions();
        Map<Integer, String> postionMap = allPostions.stream().collect(Collectors.toMap(WalletPostionVo::getId, WalletPostionVo::getName));
        for (UserStaffBo userStaffBo : allStaffs) {
            String stateGroup = String.format("%d-%d-%d", userStaffBo.getUserId(), userStaffBo.getPostionId(), userStaffBo.getCompanyId() == null ? NO_COMPANY_STATE : userStaffBo.getCompanyId());
            StaffStatisticsBo staffStatisticsBo = creatDefaultStaffStatistics(userStaffBo);
            if (!sellingWaterMap.isEmpty()) {
                Map<String, Map<Integer, Map<Integer, List<AppUserSellingWaterDTO>>>> userSellingWaterMap = sellingWaterMap.get(userStaffBo.getUserId());
                if (userSellingWaterMap != null && !userSellingWaterMap.isEmpty()) {
                    AtomicReference<BigDecimal> royaltyAmountAtomicReference = new AtomicReference<>(BigDecimal.ZERO);
                    List<String> postionNames = new ArrayList<>();
                    List<Integer> postionIds = new ArrayList<>();
                    List<String> companyNames = new ArrayList<>();
                    List<Integer> compnayIds = new ArrayList<>();
                    userSellingWaterMap.forEach((K, V) -> {
                        if (!K.equals(stateGroup)) {
                            String[] sates = K.split("-");
                            String postionIdStr = sates[1];
                            postionMap.get(Integer.valueOf(postionIdStr));
                            String companyIdStr = sates[2];
                            if (!postionIds.contains(Integer.valueOf(postionIdStr))) {
                                postionNames.add(postionMap.get(Integer.valueOf(postionIdStr)));
                                postionIds.add(Integer.valueOf(postionIdStr));
                            }

                            if (!compnayIds.contains(Integer.valueOf(companyIdStr))) {
                                String companyName = companyMap.get(Integer.valueOf(companyIdStr));
                                if (StringUtils.hasText(companyName)){
                                    companyNames.add(companyName);
                                    compnayIds.add(Integer.valueOf(companyIdStr));
                                }
                            }
                            staffStatisticsBo.setPostionIds(postionIds);
                            staffStatisticsBo.setPostionNames(postionNames);
                            staffStatisticsBo.setCompnayIds(compnayIds);
                            staffStatisticsBo.setCompanyNames(companyNames);
                        }
                        wrapStaffStatistics(V, royaltyAmountAtomicReference, staffStatisticsBo);
                    });
                    compnayIds.addAll(staffStatisticsBo.getCompnayIds());
                    companyNames.addAll(staffStatisticsBo.getCompanyNames());
                    postionIds.addAll(staffStatisticsBo.getCompnayIds());
                    postionNames.addAll(staffStatisticsBo.getPostionNames());
                    staffStatisticsBo.setCompnayIds(compnayIds);
                    staffStatisticsBo.setCompanyNames(companyNames.stream().distinct().collect(Collectors.toList()));
                    staffStatisticsBo.setPostionIds(postionIds);
                    staffStatisticsBo.setPostionNames(postionNames.stream().distinct().collect(Collectors.toList()));
                    staffStatisticsBo.setRoyaltyAmount(royaltyAmountAtomicReference.get());
                }
            }
            staffStatisticsBos.add(staffStatisticsBo);
        }
        if (staffStatisticsFindDTO.getPage() == 1) {
            StaffStatisticsTotalBo staffStatisticsTotalBo = new StaffStatisticsTotalBo();
            Supplier<Stream<StaffStatisticsBo>> streamSupplier = () -> staffStatisticsBos.stream();
            BigDecimal royaltyAmount = streamSupplier.get().map(StaffStatisticsBo::getRoyaltyAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            BigDecimal sellAmount = streamSupplier.get().map(StaffStatisticsBo::getSellAmount).reduce(BigDecimal.ZERO, BigDecimal::add);
            staffStatisticsTotalBo.setTotalRoyaltyAmount(royaltyAmount);
            staffStatisticsTotalBo.setTotalSellAmount(sellAmount);
            staffStatisticsTotalBo.setStaffNum(allStaffs.size());
            result.put(TOTAL_VAL, staffStatisticsTotalBo);
        }
        pageDataVO.setData(staffStatisticsBos);
        pageDataVO.setPageNum(staffPage.getPageNum());
        pageDataVO.setPageSize(staffPage.getPageSize());
        pageDataVO.setTotalCount(staffPage.getTotalCount());
        pageDataVO.setTotalPage(staffPage.getTotalPage());
        result.put(PAGE_VAL, pageDataVO);
        return result;
    }

    private StaffStatisticsBo wrapStaffStatistics(Map<Integer, Map<Integer, List<AppUserSellingWaterDTO>>> waterDTOSMap, AtomicReference<BigDecimal> royaltyAmountAtomicReference, StaffStatisticsBo staffStatisticsBo) {
        waterDTOSMap.forEach((K, V) -> {
            Map<String, Object> orderRentResultMap = wrapStaffStatisticsMap(V);
            BigDecimal royaltyAmount = royaltyAmountAtomicReference.get();
            royaltyAmount = royaltyAmount.add((BigDecimal) orderRentResultMap.get(TOTAL_COMMISSION_VAL));
            royaltyAmountAtomicReference.set(royaltyAmount);
            switch (K) {
                case StatisticsStatusEnum.ORDER_RENT_VEHICLE_TYPE:
                    staffStatisticsBo.setRentVehicleOrderNum(((Integer) orderRentResultMap.get(TOTAL_NUM_VAL))+staffStatisticsBo.getRentVehicleOrderNum());
                    staffStatisticsBo.setRentVehicleAmount(((BigDecimal) orderRentResultMap.get(TOTAL_AMOUNT_VAL)).add(staffStatisticsBo.getRentVehicleAmount()));
                    break;
                case StatisticsStatusEnum.ORDER_TOUR_TYPE:
                    staffStatisticsBo.setTravelOrderNum(((Integer) orderRentResultMap.get(TOTAL_NUM_VAL))+staffStatisticsBo.getTravelOrderNum());
                    staffStatisticsBo.setTravelAmount(((BigDecimal) orderRentResultMap.get(TOTAL_AMOUNT_VAL)).add(staffStatisticsBo.getTravelAmount()));
                    break;
                case StatisticsStatusEnum.ORDER_MEMBER_TYPE:
                    staffStatisticsBo.setMemberOrderNum(((Integer) orderRentResultMap.get(TOTAL_NUM_VAL))+staffStatisticsBo.getMemberOrderNum());
                    staffStatisticsBo.setMemberAmount(((BigDecimal) orderRentResultMap.get(TOTAL_AMOUNT_VAL)).add(staffStatisticsBo.getMemberAmount()));
                    break;
                default:
                    break;
            }
        });
        return staffStatisticsBo;
    }

    private Map<String, Object> wrapStaffStatisticsMap(Map<Integer, List<AppUserSellingWaterDTO>> sellingWaters) {
        Map<String, Object> staffStatisticsResultMap = new HashMap<>(2);
        AtomicInteger orderCounter = new AtomicInteger(0);
        if (sellingWaters == null || sellingWaters.isEmpty()) {
            staffStatisticsResultMap.put(TOTAL_AMOUNT_VAL, BigDecimal.ZERO);
            staffStatisticsResultMap.put(TOTAL_NUM_VAL, orderCounter.get());
        }
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal royaltyAmount = BigDecimal.ZERO;
        Set<Map.Entry<Integer, List<AppUserSellingWaterDTO>>> sellingWaterSet = sellingWaters.entrySet();
        for (Map.Entry<Integer, List<AppUserSellingWaterDTO>> sellingWaterEntry : sellingWaterSet) {
            List<AppUserSellingWaterDTO> waterDTOS = sellingWaterEntry.getValue();
            for (AppUserSellingWaterDTO waterDTO : waterDTOS) {
                if (sellingWaterEntry.getKey() == 0) {
                    royaltyAmount = royaltyAmount.add(waterDTO.getCommission());
                    totalAmount = totalAmount.add(waterDTO.getPrice());
                    orderCounter.incrementAndGet();
                }

                if (sellingWaterEntry.getKey() == 1) {
                    royaltyAmount = royaltyAmount.subtract(waterDTO.getCommission());
                    totalAmount = totalAmount.subtract(waterDTO.getPrice());
                }
            }
            staffStatisticsResultMap.put(TOTAL_AMOUNT_VAL, totalAmount);
            staffStatisticsResultMap.put(TOTAL_NUM_VAL, orderCounter.get());
            staffStatisticsResultMap.put(TOTAL_COMMISSION_VAL, royaltyAmount);
        }
        return staffStatisticsResultMap;
    }

    private StaffStatisticsBo creatDefaultStaffStatistics(UserStaffBo userStaffBo) {
        StaffStatisticsBo staffStatisticsBo = new StaffStatisticsBo();
        if (userStaffBo.getCompanyId() != null) {
            staffStatisticsBo.setCompanyNames(Arrays.asList(userStaffBo.getCompanyName()));
            staffStatisticsBo.setCompnayIds(Arrays.asList(userStaffBo.getCompanyId()));
        }
        if (userStaffBo.getPostionId() != null) {
            staffStatisticsBo.setPostionNames(Arrays.asList(userStaffBo.getPostionName()));
            staffStatisticsBo.setPostionIds(Arrays.asList(userStaffBo.getPostionId()));
        }
        staffStatisticsBo.setUserId(userStaffBo.getUserId());
        staffStatisticsBo.setName(StringUtils.hasText(userStaffBo.getRealName())?userStaffBo.getRealName():userStaffBo.getNickName());
        staffStatisticsBo.setPhone(userStaffBo.getPhone());
        staffStatisticsBo.setMemberAmount(BigDecimal.ZERO);
        staffStatisticsBo.setMemberOrderNum(0);
        staffStatisticsBo.setTravelAmount(BigDecimal.ZERO);
        staffStatisticsBo.setTravelOrderNum(0);
        staffStatisticsBo.setRentVehicleAmount(BigDecimal.ZERO);
        staffStatisticsBo.setRentVehicleOrderNum(0);
        staffStatisticsBo.setSellAmount(BigDecimal.ZERO);
        staffStatisticsBo.setRoyaltyAmount(BigDecimal.ZERO);
        return staffStatisticsBo;
    }


}