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

    private final OrderAccountBiz orderAccountBiz;


    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.getByUserIds(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) {
        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<UserStaffBo> staffCommisstions = userFeign.findStaffSellerWater(Lists.newArrayList(staffMap.keySet()), startDate, endDate);
        Map<Integer, BigDecimal> staffCommissionMap = CollectionUtils.isEmpty(staffCommisstions) ? Collections.EMPTY_MAP : staffCommisstions.stream().collect(Collectors.toMap(UserStaffBo::getUserId, UserStaffBo::getCommission));

        //4.根据员工id去订单账目表查询
        List<OrderAccountBo> orderAccountBos = orderAccountBiz.selectByDateAndStatffIds(startDate, endDate, Lists.newArrayList(staffMap.keySet()));
        Map<Integer, Map<Integer, List<OrderAccountBo>>> accountMap = CollectionUtils.isEmpty(orderAccountBos) ? Collections.EMPTY_MAP : orderAccountBos.stream().collect(Collectors.groupingBy(OrderDTO::getUserId, Collectors.groupingBy(OrderDTO::getType, Collectors.toList())));
        //遍历员工
        for (UserStaffBo userStaffBo : userStaffBos) {
            StaffStatistics staffStatistics = creatDefaultStaffStatistics(startDate, userStaffBo);

            //1.设置佣金
             if (staffCommissionMap!=null && staffCommissionMap.get(staffStatistics.getUserId())!=null){
                 staffStatistics.setRoyaltyAmount(staffCommissionMap.get(staffStatistics.getUserId()));
             }
            //2.设置
            if(accountMap!=null && accountMap.get(staffStatistics.getUserId())!=null){
                Map<Integer, List<OrderAccountBo>> orderAccountMap = accountMap.get(staffStatistics.getUserId());
                //K:订单类型 V:对应的订单集合
                orderAccountMap.forEach((K,V)->{
                    Map<String, Object> orderRentResultMap = wrapStaffStatisticsMap(V);
                    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;
                    }
                });
            }
            //添加员工统计对象
            staffStatisticsList.add(staffStatistics);
        }
         //批量插入
        inserBatchStaffStatistics(staffStatisticsList);
    }

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

    private Map<String,Object> wrapStaffStatisticsMap(List<OrderAccountBo> orderAccountBos){
        Map<String,Object> staffStatisticsResultMap = new HashMap<>(2);
        AtomicInteger orderCounter = new AtomicInteger(0);
        BigDecimal totalAmount = BigDecimal.ZERO;
        BigDecimal totalRefundAmount = BigDecimal.ZERO;
        for (OrderAccountBo orderAccountBo : orderAccountBos) {
            if (Objects.equals(orderAccountBo.getType(),AccountTypeEnum.IN_ORDER_PAY.getCode())){
                orderCounter.incrementAndGet();
                if (Objects.nonNull(orderAccountBo.getDamageSafe()) && (orderAccountBo.getDamageSafe() == StatisticsStatusEnum.DAMAGE_SAFE)){
                    BigDecimal safeAmount = getSafeAmount(orderAccountBo);
                    totalAmount = totalAmount.subtract(safeAmount);
                }
                totalAmount = totalAmount.add(orderAccountBo.getAccountDetailEntity().getOrderAmount());
            }else{
                if (Objects.nonNull(orderAccountBo.getDamageSafe()) && (orderAccountBo.getDamageSafe() == StatisticsStatusEnum.DAMAGE_SAFE)){
                    List<OrderAccountDeduction> deductions = orderAccountBo.getAccountDetailEntity().getDeductions();
                    boolean isCancelAll = Objects.equals(orderAccountBo.getType(), AccountTypeEnum.OUT_ORDER_FUND.getCode()) && CollectionUtils.isEmpty(deductions);
                    boolean isViolateCancel = CollectionUtils.isNotEmpty(deductions) && deductions.stream()
                            .map(OrderAccountDeduction::getType).collect(Collectors.toList()).contains(DeductionTypeEnum.VIOLATE_CANCEL.getCode());
                    if (isCancelAll || isViolateCancel){
                        BigDecimal safeAmount = getSafeAmount(orderAccountBo);
                        totalRefundAmount = totalRefundAmount.subtract(safeAmount);
                    }
                }
                totalRefundAmount = totalRefundAmount.add(orderAccountBo.getAccountDetailEntity().getOrderAmount());
            }
        }
        staffStatisticsResultMap.put(TOTAL_AMOUNT_VAL,totalAmount.subtract(totalRefundAmount));
        staffStatisticsResultMap.put(TOTAL_NUM_VAL,orderCounter.get());
        return staffStatisticsResultMap;
    }

    private BigDecimal getSafeAmount(OrderAccountBo orderAccountBo){
        JSONObject data = orderAccountBo.getData();
        if(!data.isEmpty()) {
            Object paramJson = data.get(StatisticsStatusEnum.PARMAM_JSON);
            JSONObject jsonObject = JSONUtil.parseObj(paramJson);
            BigDecimal safeAmount = jsonObject.get(StatisticsStatusEnum.NO_DEDUCTIBLE_AMOUNT, BigDecimal.class);
            return safeAmount==null?BigDecimal.ZERO:safeAmount;
        }
        return BigDecimal.ZERO;
    }

    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());
        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;
    }
}
