package com.xxfc.platform.order.biz;

import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.github.wxiaoqi.security.admin.bo.UserStaffBo;
import com.github.wxiaoqi.security.admin.dto.AppUserSellingWaterDTO;
import com.github.wxiaoqi.security.admin.entity.AppUserSellingWater;
import com.github.wxiaoqi.security.admin.feign.UserFeign;
import com.github.wxiaoqi.security.admin.vo.AppUserVo;
import com.github.wxiaoqi.security.common.biz.BaseBiz;
import com.github.wxiaoqi.security.common.msg.ObjectRestResponse;
import com.github.wxiaoqi.security.common.vo.PageDataVO;
import com.google.common.collect.Lists;
import com.xxfc.platform.order.contant.enumerate.AccountTypeEnum;
import com.xxfc.platform.order.contant.enumerate.DeductionTypeEnum;
import com.xxfc.platform.order.contant.enumerate.StatisticsStatusEnum;
import com.xxfc.platform.order.entity.BaseOrder;
import com.xxfc.platform.order.entity.StaffStatistics;
import com.xxfc.platform.order.mapper.StaffStatisticsMapper;
import com.xxfc.platform.order.pojo.account.OrderAccountBo;
import com.xxfc.platform.order.pojo.account.OrderAccountDeduction;
import com.xxfc.platform.order.pojo.bo.StaffStatisticsTotalBo;
import com.xxfc.platform.order.pojo.dto.OrderDTO;
import com.xxfc.platform.order.pojo.dto.StaffStatisticsFindDTO;
import com.xxfc.platform.order.pojo.vo.StaffStatisticsVo;
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.time.LocalDate;
import java.time.ZoneId;
import java.time.temporal.WeekFields;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author libin
 * @version 1.0
 * @description
 * @data 2019/11/25 12:36
 */
@Transactional(rollbackFor = Exception.class)
@Service
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class StaffStatisticsBiz extends BaseBiz<StaffStatisticsMapper, StaffStatistics> {

    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 UserFeign userFeign;

   private final BaseOrderBiz baseOrderBiz;

    public StaffStatisticsTotalBo staffStatisticsWithTotal(StaffStatisticsFindDTO staffStatisticsFindDTO) {
        return mapper.statistics(staffStatisticsFindDTO);
    }

    public PageDataVO<StaffStatisticsVo> staffStatisticsWithPage(StaffStatisticsFindDTO staffStatisticsFindDTO) {
        PageDataVO<StaffStatisticsVo> pageDataVO = new PageDataVO<>();

        PageDataVO<StaffStatistics> statisticsVoPageDataVO = PageDataVO.pageInfo(staffStatisticsFindDTO.getPage(), staffStatisticsFindDTO.getLimit(), () -> mapper.selectStaffStatistics(staffStatisticsFindDTO));
        List<StaffStatistics> data = statisticsVoPageDataVO.getData();
        if (CollectionUtils.isEmpty(data)) {
            pageDataVO.setTotalCount(0L);
            pageDataVO.setTotalPage(0);
            pageDataVO.setPageNum(staffStatisticsFindDTO.getPage());
            pageDataVO.setPageSize(staffStatisticsFindDTO.getLimit());
            pageDataVO.setData(Collections.EMPTY_LIST);
            return pageDataVO;
        }

        List<Integer> userIds = data.stream().map(StaffStatistics::getUserId).collect(Collectors.toList());
        //根据员工id查询员工信息
        ObjectRestResponse<List<AppUserVo>> result = userFeign.getByUserIdList(userIds);
        List<AppUserVo> users = result.getData();
        Map<Integer, AppUserVo> userVoMap = CollectionUtils.isEmpty(users) ? Collections.EMPTY_MAP : users.stream().filter(x -> Objects.nonNull(x.getUserid())).collect(Collectors.toMap(AppUserVo::getUserid, Function.identity()));
        Map<Integer, List<StaffStatistics>> statisticsMap = data.stream().collect(Collectors.groupingBy(StaffStatistics::getUserId, Collectors.toList()));

        List<StaffStatisticsVo> staffStatisticsVos = new ArrayList<>(data.size());
        StaffStatisticsVo staffStatisticsVo = new StaffStatisticsVo();
        statisticsMap.forEach((K, V) -> {
            List<String> compnayNames = new ArrayList<>(V.size());
            List<String> postionNames = new ArrayList<>(V.size());
            StaffStatisticsVo staffStatisticsClone = ObjectUtil.cloneByStream(staffStatisticsVo);
            for (StaffStatistics staffStatistics : V) {
                staffStatisticsClone.setSellAmount(staffStatistics.getSellAmount().add(staffStatisticsClone.getSellAmount()));
                staffStatisticsClone.setRoyaltyAmount(staffStatistics.getRoyaltyAmount().add(staffStatisticsClone.getRoyaltyAmount()));
                staffStatisticsClone.setRentVehicleAmount(staffStatistics.getRentVehicleAmount().add(staffStatisticsClone.getRentVehicleAmount()));
                staffStatisticsClone.setRentVehicleOrderNum(staffStatistics.getRentVehicleOrderNum() + staffStatisticsClone.getRentVehicleOrderNum());
                staffStatisticsClone.setTravelAmount(staffStatistics.getTravelAmount().add(staffStatisticsClone.getTravelAmount()));
                staffStatisticsClone.setTravelOrderNum(staffStatistics.getTravelOrderNum() + staffStatisticsClone.getTravelOrderNum());
                staffStatisticsClone.setMemberAmount(staffStatistics.getMemberAmount().add(staffStatisticsClone.getMemberAmount()));
                staffStatisticsClone.setMemberOrderNum(staffStatistics.getMemberOrderNum() + staffStatisticsClone.getMemberOrderNum());
                compnayNames.add(staffStatistics.getCompanyName());
                postionNames.add(staffStatistics.getPostionName());
            }
            AppUserVo appUserVo = userVoMap == null ? new AppUserVo() : userVoMap.get(K) == null ? new AppUserVo() : userVoMap.get(K);
            String name = StringUtils.hasText(appUserVo.getRealname()) ? appUserVo.getRealname() : StringUtils.hasText(appUserVo.getNickname()) ? appUserVo.getNickname() : "";
            staffStatisticsClone.setName(name);
            staffStatisticsClone.setPhone(appUserVo.getUsername());
            staffStatisticsClone.setCompanyNames(compnayNames);
            staffStatisticsClone.setPostionNames(postionNames);
            staffStatisticsVos.add(staffStatisticsClone);

        });

        pageDataVO.setData(staffStatisticsVos);
        pageDataVO.setPageSize(statisticsVoPageDataVO.getPageSize());
        pageDataVO.setPageNum(statisticsVoPageDataVO.getPageNum());
        pageDataVO.setTotalPage(statisticsVoPageDataVO.getTotalPage());
        pageDataVO.setTotalCount(statisticsVoPageDataVO.getTotalCount());
        return pageDataVO;
    }


    public void staffStatisticsJob(Date startDate, Date endDate, Map<Integer, String> companyMap,Map<Integer,String> postionMap) {
        List<StaffStatistics> staffStatisticsList = new ArrayList<>();
        //1.查询全部员工信息
        List<UserStaffBo> userStaffBos = userFeign.findAllStaffs();
        //2.根据员工id调用用户服务查询员工的佣金
        if (CollectionUtils.isEmpty(userStaffBos)) {
            return;
        }

        Map<Integer, UserStaffBo> staffMap = userStaffBos.stream().collect(Collectors.toMap(UserStaffBo::getUserId, Function.identity()));
        List<AppUserSellingWaterDTO> appUserSellingWaterDTOS = userFeign.findStaffSellerWater(Lists.newArrayList(staffMap.keySet()),startDate,endDate);
        Map<Integer, Map<String, Map<Integer,Map<Integer,List<AppUserSellingWaterDTO>>>>> sellingWaterMap = new HashMap<>();

        if (CollectionUtils.isNotEmpty(appUserSellingWaterDTOS)){
            List<Integer> orderIds = appUserSellingWaterDTOS.stream().map(AppUserSellingWaterDTO::getOrderId).collect(Collectors.toList());
            List<OrderDTO> orders = baseOrderBiz.selectBaeOrderByOrderIds(orderIds);
            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())))));
        }
        for (UserStaffBo userStaffBo : userStaffBos) {
            String stateGroup = String.format("%d-%d-%d",userStaffBo.getUserId(),userStaffBo.getPostionId(),userStaffBo.getCompanyId()==null?NO_COMPANY_STATE:userStaffBo.getCompanyId());
            StaffStatistics staffStatistics = creatDefaultStaffStatistics(startDate, userStaffBo);
            if (!sellingWaterMap.isEmpty()){
                Map<String, Map<Integer,Map<Integer,List<AppUserSellingWaterDTO>>>> userSellingWaterMap = sellingWaterMap.get(userStaffBo.getUserId());
                if (userSellingWaterMap!=null && !userSellingWaterMap.isEmpty()){
                    Map<Integer,Map<Integer,List<AppUserSellingWaterDTO>>> waterDTOSMap = userSellingWaterMap.get(stateGroup);
                    AtomicReference<BigDecimal> royaltyAmountAtomicReference = new AtomicReference<>(BigDecimal.ZERO);
                    if (waterDTOSMap!=null && !waterDTOSMap.isEmpty()) {
                        wrapStaffStatistics(waterDTOSMap,royaltyAmountAtomicReference,staffStatistics);
                    }

                    userSellingWaterMap.remove(stateGroup);
                    if (!userSellingWaterMap.isEmpty()){
                        userSellingWaterMap.forEach((K,V)->{
                            String[] sates = K.split("-");
                            StaffStatistics staffStatisticsTo = creatDefaultStaffStatistics(startDate, userStaffBo);
                            staffStatisticsTo.setUserPostionId(Integer.valueOf(sates[1]));
                            staffStatisticsTo.setCompanyId(Integer.valueOf(sates[2]));
                            staffStatisticsTo.setCompanyName(companyMap.get(staffStatisticsTo.getCompanyId()));
                            staffStatisticsTo.setPostionName(postionMap.get(staffStatisticsTo.getUserPostionId()));
                            AtomicReference<BigDecimal> royaltyference = new AtomicReference(BigDecimal.ZERO);
                            wrapStaffStatistics(V,royaltyference,staffStatisticsTo);
                            staffStatisticsTo.setRoyaltyAmount(royaltyference.get());
                            staffStatisticsList.add(staffStatisticsTo);
                        });
                    }
                }
            }
            staffStatisticsList.add(staffStatistics);
        }
         //批量插入
        inserBatchStaffStatistics(staffStatisticsList);
    }


    public void inserBatchStaffStatistics(List<StaffStatistics> staffStatistics){
        mapper.insertList(staffStatistics);
    }

    private StaffStatistics wrapStaffStatistics(Map<Integer,Map<Integer,List<AppUserSellingWaterDTO>>> waterDTOSMap,AtomicReference<BigDecimal> royaltyAmountAtomicReference,StaffStatistics staffStatistics){
        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:
                    staffStatistics.setRentVehicleOrderNum((Integer) orderRentResultMap.get(TOTAL_NUM_VAL));
                    staffStatistics.setRentVehicleAmount((BigDecimal) orderRentResultMap.get(TOTAL_AMOUNT_VAL));
                    break;
                case StatisticsStatusEnum.ORDER_TOUR_TYPE:
                    staffStatistics.setTravelOrderNum((Integer) orderRentResultMap.get(TOTAL_NUM_VAL));
                    staffStatistics.setTravelAmount((BigDecimal) orderRentResultMap.get(TOTAL_AMOUNT_VAL));
                    break;
                case StatisticsStatusEnum.ORDER_MEMBER_TYPE:
                    staffStatistics.setMemberOrderNum((Integer) orderRentResultMap.get(TOTAL_NUM_VAL));
                    staffStatistics.setMemberAmount((BigDecimal) orderRentResultMap.get(TOTAL_AMOUNT_VAL));
                    break;
                default:
                    break;
            }
        });

        return staffStatistics;
    }

    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.getCommission().divide(new BigDecimal(String.valueOf(waterDTO.getExtract()))));
                    orderCounter.incrementAndGet();
                }

                if (sellingWaterEntry.getKey()==1){
                    royaltyAmount = royaltyAmount.subtract(waterDTO.getCommission());
                    totalAmount = totalAmount.subtract(waterDTO.getCommission().divide(new BigDecimal(String.valueOf(waterDTO.getExtract()/100.00))));
                    
                }
             }
            staffStatisticsResultMap.put(TOTAL_AMOUNT_VAL,totalAmount);
            staffStatisticsResultMap.put(TOTAL_NUM_VAL,orderCounter.get());
            staffStatisticsResultMap.put(TOTAL_COMMISSION_VAL,royaltyAmount);
        }
       return staffStatisticsResultMap;
    }

    private StaffStatistics creatDefaultStaffStatistics(Date startDate, UserStaffBo userStaffBo) {
        StaffStatistics staffStatistics = new StaffStatistics();

        LocalDate localDate = LocalDate.from(new Date().toInstant().atZone(ZoneId.systemDefault()));
        String year = String.valueOf(localDate.getYear());
        String month = String.format("%s%d", year, localDate.getMonthValue());
        String weekOfYear = String.format("%s%d", year, localDate.get(WeekFields.of(Locale.CHINESE).weekOfYear()));

        staffStatistics.setUserId(userStaffBo.getUserId());
        staffStatistics.setCompanyId(userStaffBo.getCompanyId()==null?NO_COMPANY_STATE:userStaffBo.getCompanyId());
        staffStatistics.setCompanyName(userStaffBo.getCompanyName());
        staffStatistics.setUserPostionId(userStaffBo.getPostionId());
        staffStatistics.setPostionName(userStaffBo.getPostionName());
        staffStatistics.setMemberAmount(BigDecimal.ZERO);
        staffStatistics.setMemberOrderNum(0);
        staffStatistics.setTravelAmount(BigDecimal.ZERO);
        staffStatistics.setTravelOrderNum(0);
        staffStatistics.setRentVehicleAmount(BigDecimal.ZERO);
        staffStatistics.setRentVehicleOrderNum(0);
        staffStatistics.setSellAmount(BigDecimal.ZERO);
        staffStatistics.setRoyaltyAmount(BigDecimal.ZERO);
        staffStatistics.setDate(startDate);
        staffStatistics.setYear(year);
        staffStatistics.setMonth(month);
        staffStatistics.setWeekOfYear(weekOfYear);
        staffStatistics.setCrtTime(new Date());
        return staffStatistics;
    }
}
