package com.sckw.order.serivce;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import com.sckw.core.common.enums.enums.DictTypeEnum;
import com.sckw.core.exception.BusinessException;
import com.sckw.core.model.constant.Global;
import com.sckw.core.model.constant.NumberConstant;
import com.sckw.core.utils.BeanUtils;
import com.sckw.core.utils.DateUtils;
import com.sckw.core.web.context.LoginUserHolder;
import com.sckw.order.dao.KwoTradeOrderMapper;
import com.sckw.order.model.dto.*;
import com.sckw.order.model.vo.req.WbTOrderDataStsParam;
import com.sckw.order.model.vo.req.WbTOrderExcelStsParam;
import com.sckw.order.model.vo.res.TOrderDataStsResVO;
import com.sckw.order.model.vo.res.WbTOrderDataStsResVO;
import com.sckw.order.model.vo.res.WbTOrderExcelStsResVO;
import com.sckw.order.model.vo.res.WorkbenchPurchaseVO;
import com.sckw.payment.api.dubbo.PaymentDubboService;
import com.sckw.payment.api.model.dto.SettlementMoney;
import com.sckw.product.api.dubbo.GoodsInfoService;
import com.sckw.product.api.dubbo.GoodsStatisticsService;
import com.sckw.product.api.model.GoodsNumStsParam;
import com.sckw.product.api.model.KwpGoods;
import com.sckw.system.api.RemoteSystemService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @desc: 贸易订单统计service
 * @author: yzc
 * @date: 2023-09-13 8:36
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class KwoTradeOrderStatisticsService {

    private final KwoTradeOrderService kwoTradeOrderService;
    private final KwoTradeOrderMapper tradeOrderMapper;

    @DubboReference(version = "1.0.0", group = "design", check = false)
    private GoodsInfoService goodsInfoService;
    @DubboReference(version = "1.0.0", group = "design", check = false)
    private GoodsStatisticsService goodsStatisticsService;
    @DubboReference(version = "1.0.0", group = "design", check = false)
    private PaymentDubboService paymentDubboService;
    @DubboReference(version = "1.0.0", group = "design", check = false)
    private RemoteSystemService remoteSystemService;

    /**
     * @desc: 工作台贸易订单表格统计
     * @author: yzc
     * @date: 2023-09-13 15:46
     * @Param params:
     * @return: java.util.List<com.sckw.order.model.vo.res.WbTOrderExcelStsResVO>
     */
    public List<WbTOrderExcelStsResVO> workbenchExcel(WbTOrderExcelStsParam params) {
        Integer dateType = params.getDateType();
        Date startTime;
        if (Objects.equals(1, dateType)) {
            startTime = DateUtils.getStartTime(3, LocalDateTime.now());
        } else if (Objects.equals(3, dateType)) {
            startTime = DateUtils.getYearStartTime();
        } else {
            throw new BusinessException("Invalid date type");
        }
        List<WbTOrderExcelStsResVO> res = buildFormatDate(dateType, startTime);
        WbTOrderExcelStsReqDTO reqDTO = new WbTOrderExcelStsReqDTO();
        reqDTO.setUnitType(String.valueOf(params.getOrderType()))
                .setDateFormat(DateUtils.getDateFormat(dateType))
                .setStartTime(startTime)
                .setTopEntId(LoginUserHolder.getEntId())
                .setStsFactorsOne(params.getStsFactorsOne())
                .setStsFactorsTwo(params.getStsFactorsTwo());
        Map<String, WbTOrderExcelStsResDTO> map = kwoTradeOrderService.workbenchExcel(reqDTO).stream()
                .collect(Collectors.toMap(WbTOrderExcelStsResDTO::getFormatDate, e -> e, (k1, k2) -> k1));
        res.forEach(e -> {
            WbTOrderExcelStsResDTO dto = map.get(e.getFormatDate());
            if (Objects.nonNull(dto)) {
                BigDecimal one = dto.getStsFactorsOneValue();
                if (Objects.equals(params.getStsFactorsOne(), 1)) {
                    one = one.divide(NumberConstant.TEN_THOUSAND, 2, RoundingMode.HALF_UP);
                }
                BigDecimal two = dto.getStsFactorsTwoValue();
                if (Objects.equals(params.getStsFactorsTwo(), 1)) {
                    two = two.divide(NumberConstant.TEN_THOUSAND, 2, RoundingMode.HALF_UP);
                }
                e.setStsFactorsOneValue(one);
                e.setStsFactorsTwoValue(two);
            }
        });
        return res;
    }

    /**
     * @desc: 构建时间转换格式
     * @author: yzc
     * @date: 2023-09-13 15:45
     * @Param dateType:
     * @Param startTime:
     * @return: java.util.List<com.sckw.order.model.vo.res.WbTOrderExcelStsResVO>
     */
    private List<WbTOrderExcelStsResVO> buildFormatDate(Integer dateType, Date startTime) {
        LocalDate start = DateUtils.dateToLocalDate(startTime);
        LocalDate end = LocalDate.now();
        List<WbTOrderExcelStsResVO> res = new ArrayList<>();
        if (dateType == 1) {
            while (start.isBefore(end)) {
                WbTOrderExcelStsResVO vo = new WbTOrderExcelStsResVO();
                vo.setFormatDate(start.toString());
                start = start.plusDays(1);
                res.add(vo);
            }
            WbTOrderExcelStsResVO vo = new WbTOrderExcelStsResVO();
            vo.setFormatDate(end.toString());
            res.add(vo);
        } else {
            //当月第一天
            end = end.with(TemporalAdjusters.firstDayOfMonth());
            while (start.isBefore(end)) {
                WbTOrderExcelStsResVO vo = new WbTOrderExcelStsResVO();
                vo.setFormatDate(DateUtils.localDateToString(start, "yyyy-MM"));
                start = start.plusMonths(1);
                res.add(vo);
            }
            WbTOrderExcelStsResVO vo = new WbTOrderExcelStsResVO();
            vo.setFormatDate(DateUtils.localDateToString(end, "yyyy-MM"));
            res.add(vo);
        }
        return res;
    }

    /**
     * @desc: 工作台贸易订单数据统计
     * @author: yzc
     * @date: 2023-09-11 15:32
     * @Param params:
     * @return: com.sckw.report.model.vo.WbTOrderDataStsResVO
     */
    public WbTOrderDataStsResVO workbenchData(WbTOrderDataStsParam params) {
        WbTOrderDataStsResVO res = new WbTOrderDataStsResVO();
        List<TOrderDataStsResVO> list = new ArrayList<>();
        Long entId = LoginUserHolder.getEntId();
        String unit = params.getUnit();
        Integer orderType = params.getOrderType();
        Integer dateType = params.getDateType();
        Date currentStart = DateUtils.getStartTime(dateType, LocalDateTime.now());
        Date lastEnd = DateUtils.getLastEndTime(dateType);
        Date lastStart = DateUtils.getStartTime(dateType, DateUtil.toLocalDateTime(lastEnd));
        //当前数据
        TOrderDataStsResVO currentDataSts = new TOrderDataStsResVO();
        //商品数量
        GoodsNumStsParam goodsNumStsParam = new GoodsNumStsParam();
        goodsNumStsParam.setTopEntId(entId).setUnit(unit);
        Long currentGoodsNum = goodsStatisticsService.getOnShelfGoodsNum(goodsNumStsParam);
        //待付款金额
        SettlementMoney settlement = Objects.equals(orderType, 1) ?
                paymentDubboService.countPurchase(entId, dateType) : paymentDubboService.countSell(entId, dateType);
        //订单统计
        WorkbenchOrderStsDTO currentOrderSts = getOrderStsInfo(orderType, unit, currentStart, null);
        BigDecimal currentAmount = BigDecimal.valueOf(currentOrderSts.getOrderAmount());
        BigDecimal currentPrice = BigDecimal.valueOf(currentOrderSts.getOrderPrice());
        currentDataSts.setType(NumberConstant.ONE)
                .setGoodsNum(currentGoodsNum)
                .setOrderAmount(currentAmount)
                .setOrderPrice(currentPrice)
                .setWaitCollectedPrice(new BigDecimal(settlement.getCurrent()))
                .setNotArrivedAmount(currentAmount.subtract(BigDecimal.valueOf(currentOrderSts.getOrderActualAmount())));
        list.add(currentDataSts);

        //上周/上月数据
        TOrderDataStsResVO lastDataSts = new TOrderDataStsResVO();
        //商品数量
        goodsNumStsParam.setEndDate(lastEnd);
        Long lastGoodsNum = goodsStatisticsService.getOnShelfGoodsNum(goodsNumStsParam);
        //订单统计
        WorkbenchOrderStsDTO lastOrderSts = getOrderStsInfo(orderType, unit, lastStart, lastEnd);
        BigDecimal lastAmount = BigDecimal.valueOf(lastOrderSts.getOrderAmount());
        BigDecimal lastPrice = BigDecimal.valueOf(lastOrderSts.getOrderPrice());
        lastDataSts.setType(NumberConstant.TWO)
                .setGoodsNum(lastGoodsNum)
                .setOrderAmount(lastAmount)
                .setOrderPrice(lastPrice)
                .setWaitCollectedPrice(new BigDecimal(settlement.getBefore()))
                .setNotArrivedAmount(lastAmount.subtract(BigDecimal.valueOf(lastOrderSts.getOrderActualAmount())));
        list.add(lastDataSts);
        res.setTOrderDataStsList(list);
        //商品数量环比
        BigDecimal goodsNumRingRate = getRingRate(new BigDecimal(currentGoodsNum), new BigDecimal(lastGoodsNum));
        boolean goodsNumFlag = goodsNumRingRate.compareTo(BigDecimal.ZERO) >= 0;
        goodsNumRingRate = goodsNumRingRate.abs();
        //订单总量环比
        BigDecimal orderAmountRingRate = getRingRate(currentAmount, lastAmount);
        boolean orderAmountFlag = orderAmountRingRate.compareTo(BigDecimal.ZERO) >= 0;
        orderAmountRingRate = orderAmountRingRate.abs();
        //订单总金额环比
        BigDecimal orderPriceRingRate = getRingRate(currentPrice, lastPrice);
        boolean orderPriceFlag = orderPriceRingRate.compareTo(BigDecimal.ZERO) >= 0;
        orderPriceRingRate = orderPriceRingRate.abs();
        //未到货量环比
        BigDecimal notArrivedAmountRingRate = getRingRate(currentDataSts.getNotArrivedAmount(), lastDataSts.getNotArrivedAmount());
        boolean notArrivedAmountFlag = notArrivedAmountRingRate.compareTo(BigDecimal.ZERO) >= 0;
        notArrivedAmountRingRate = notArrivedAmountRingRate.abs();

        res.setGoodsNumRingRate(goodsNumRingRate)
                .setGoodsNumFlag(goodsNumFlag)
                .setOrderAmountRingRate(orderAmountRingRate)
                .setOrderAmountFlag(orderAmountFlag)
                .setOrderPriceRingRate(orderPriceRingRate)
                .setOrderPriceFlag(orderPriceFlag)
                .setNotArrivedAmountRingRate(notArrivedAmountRingRate)
                .setNotArrivedAmountFlag(notArrivedAmountFlag)
                .setWaitCollectedPriceRingRate(new BigDecimal(settlement.getPercent().replace("%", "")))
                .setWaitCollectedPriceFlag(Objects.equals(settlement.getFlag(), 1));
        return res;
    }

    private BigDecimal getRingRate(BigDecimal current, BigDecimal last) {
        BigDecimal res;
        if (last.compareTo(BigDecimal.ZERO) == 0) {
            res = (current.subtract(last)).compareTo(BigDecimal.ZERO) != 0 ? NumberConstant.ONE_HUNDRED : BigDecimal.ZERO;
        } else {
            res = (current.subtract(last)).multiply(NumberConstant.ONE_HUNDRED).divide(last, 2, RoundingMode.HALF_UP);
        }
        return res;
    }

    /**
     * @desc: 获取订单统计信息
     * @author: yzc
     * @date: 2023-09-11 17:55
     * @Param orderType:
     * @Param unit:
     * @Param start:
     * @Param end:
     * @return: com.sckw.report.model.dto.WorkbenchOrderStsDTO
     */
    private WorkbenchOrderStsDTO getOrderStsInfo(Integer orderType, String unit, Date start, Date end) {
        OrderStsInfoDTO dto = new OrderStsInfoDTO();
        dto.setOrderType(orderType).setUnit(unit)
                .setEntId(LoginUserHolder.getEntId())
                .setStartCreateTime(start)
                .setEndCreateTime(end);
        List<Integer> excludeStatuses = Arrays.asList(0, 2);
        WorkbenchOrderStsDTO result = tradeOrderMapper.getOrderStsInfo(dto, excludeStatuses);
        return Objects.isNull(result) ? new WorkbenchOrderStsDTO() : result;
    }

    /**
     * @desc: 工作台采购订单列表
     * @author: yzc
     * @date: 2023-09-13 19:37
     * @return: java.util.List<com.sckw.report.model.vo.WorkbenchPurchaseVO>
     */
    public List<WorkbenchPurchaseVO> workbenchPurchaseList() {
        Long procureTopEntId = LoginUserHolder.getEntId();
        List<Integer> excludeStatuses = Arrays.asList(0, 2, 7);
        List<WorkbenchPurchaseDTO> orders = tradeOrderMapper.workbenchPurchaseList(procureTopEntId, excludeStatuses);
        if (CollUtil.isEmpty(orders)) {
            return Collections.emptyList();
        }
        Map<String, Map<String, String>> dict = remoteSystemService.queryDictByType(List.of(
                DictTypeEnum.UNIT_TYPE.getType(), DictTypeEnum.PRODUCT_NAME_TYPE.getType()));
        Map<String, String> unitMap, goodsTypeMap;
        if (CollUtil.isNotEmpty(dict)) {
            unitMap = CollUtil.isNotEmpty(dict.get(DictTypeEnum.UNIT_TYPE.getType())) ?
                    dict.get(DictTypeEnum.UNIT_TYPE.getType()) : new HashMap<>(Global.NUMERICAL_SIXTEEN);
            goodsTypeMap = CollUtil.isNotEmpty(dict.get(DictTypeEnum.PRODUCT_NAME_TYPE.getType())) ?
                    dict.get(DictTypeEnum.PRODUCT_NAME_TYPE.getType()) : new HashMap<>(Global.NUMERICAL_SIXTEEN);
        } else {
            unitMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
            goodsTypeMap = new HashMap<>(Global.NUMERICAL_SIXTEEN);
        }
        List<Long> goodsIds = orders.stream().map(WorkbenchPurchaseDTO::getGoodsId).toList();
        Map<Long, KwpGoods> goodsMap = goodsInfoService.getGoodsByIds(goodsIds);
        List<WorkbenchPurchaseVO> result = new ArrayList<>();
        orders.forEach(e -> {
            WorkbenchPurchaseVO vo = BeanUtils.copyProperties(e, WorkbenchPurchaseVO.class);
            vo.setUnitLabel(unitMap.get(e.getUnit())).setAmount(setScale(e.getAmount()));
            KwpGoods goods = goodsMap.get(e.getGoodsId());
            if (Objects.nonNull(goods)) {
                vo.setGoodsName(goods.getName()).setGoodsType(goods.getGoodsType())
                        .setGoodsTypeLabel(goodsTypeMap.get(goods.getGoodsType()));
            }
            result.add(vo);
        });
        return result;
    }


    /**
     * @desc: 设置两位小数
     * @author: yzc
     * @date: 2023-09-26 10:11
     * @Param d:
     * @return: java.lang.BigDecimal
     */
    private BigDecimal setScale(BigDecimal d) {
        if (Objects.isNull(d) || BigDecimal.ZERO.compareTo(d) == 0) {
            return NumberConstant.ZERO_TWO;
        }
        return d.setScale(2, RoundingMode.HALF_UP);
    }
}

