
package com.tmerclub.cloud.flow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.tmerclub.cloud.api.feign.SearchSpuFeignClient;
import com.tmerclub.cloud.api.multishop.feign.ShopDetailFeignClient;
import com.tmerclub.cloud.api.multishop.vo.ShopDetailVO;
import com.tmerclub.cloud.api.order.dto.FlowOrderItemDTO;
import com.tmerclub.cloud.api.order.feign.OrderFeignClient;
import com.tmerclub.cloud.api.order.feign.OrderRefundFeignClient;
import com.tmerclub.cloud.api.order.vo.FlowOrderItemVO;
import com.tmerclub.cloud.api.product.dto.ProdEffectDTO;
import com.tmerclub.cloud.api.product.feign.ProductFeignClient;
import com.tmerclub.cloud.api.product.vo.ProdEffectRespVO;
import com.tmerclub.cloud.api.user.feign.UserFeignClient;
import com.tmerclub.cloud.common.database.dto.PageDTO;
import com.tmerclub.cloud.common.database.vo.PageVO;
import com.tmerclub.cloud.common.exception.LuckException;
import com.tmerclub.cloud.common.product.vo.search.SpuSearchVO;
import com.tmerclub.cloud.common.response.ServerResponseEntity;
import com.tmerclub.cloud.common.security.AuthUserContext;
import com.tmerclub.cloud.common.util.AnalysisUtil;
import com.tmerclub.cloud.common.util.BeanUtil;
import com.tmerclub.cloud.common.util.DateUtils;
import com.tmerclub.cloud.common.util.PriceUtil;
import com.tmerclub.cloud.common.vo.DateVO;
import com.tmerclub.cloud.flow.bo.FlowSpuBO;
import com.tmerclub.cloud.flow.bo.FlowSpuUserBO;
import com.tmerclub.cloud.flow.constant.FlowDateTypeEnum;
import com.tmerclub.cloud.flow.dto.SingleProdTrendDTO;
import com.tmerclub.cloud.flow.service.ProductAnalyseService;
import com.tmerclub.cloud.flow.service.SpuAnalyseMongoService;
import com.tmerclub.cloud.flow.util.FlowUtil;
import com.tmerclub.cloud.flow.vo.FlowProdEffectDataExcelVO;
import com.tmerclub.cloud.flow.vo.ProductFlowInfoVO;
import com.tmerclub.cloud.flow.vo.ShopFlowInfoVO;
import com.tmerclub.cloud.flow.vo.SingleProdTrendVO;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;


/**
 * 流量分析—商品分析
 *
 * @author YXF
 * @date 2021-05-21 15:25:19
 */
@Service
public class ProductAnalyseServiceImpl implements ProductAnalyseService {
    private static final Logger LOG = LoggerFactory.getLogger(ProductAnalyseServiceImpl.class);

    @Autowired
    private SpuAnalyseMongoService spuAnalyseMongoService;
    @Autowired
    private MongoTemplate mongoTemplate;
    @DubboReference
    private SearchSpuFeignClient searchSpuFeignClient;
    @DubboReference
    private OrderFeignClient orderFeignClient;
    @DubboReference
    private ProductFeignClient productFeignClient;
    @DubboReference
    private OrderRefundFeignClient orderRefundFeignClient;
    @DubboReference
    private ShopDetailFeignClient shopDetailFeignClient;
    @DubboReference
    private UserFeignClient userFeignClient;

    @Override
    public PageVO<ProdEffectRespVO> getProductEffect(PageDTO pageDTO, ProdEffectDTO prodEffectDTO) {
        FlowUtil.handleTime(prodEffectDTO);
        // 今日实时- 两分钟统计一次，统计后再查询
        if (Objects.equals(prodEffectDTO.getDateType(), FlowDateTypeEnum.REAL_TIME.value())) {
            LOG.info("商品洞察-今日实时-统计商品数据");
            spuAnalyseMongoService.statisticsRealSpuData();
        }

        // 查询商品列表
        PageVO<FlowSpuBO> spuPage = spuAnalyseMongoService.pageSpuEffect(pageDTO, prodEffectDTO);

        PageVO<ProdEffectRespVO> prodEffectRespPage = new PageVO<>();
        prodEffectRespPage.setTotal(spuPage.getTotal());
        prodEffectRespPage.setPages(spuPage.getPages());
        List<ProdEffectRespVO> prodEffectRespList = new ArrayList<>(spuPage.getList().size());
        prodEffectRespPage.setList(prodEffectRespList);
        if (CollUtil.isEmpty(spuPage.getList())) {
            LOG.info("商品洞察-商品数据为空");
            return prodEffectRespPage;
        }

        // 查询商品信息
        LOG.info("商品洞察-查询商品信息");
        List<Long> spuIds = spuPage.getList().stream().map(FlowSpuBO::getSpuId).collect(Collectors.toList());
        ServerResponseEntity<List<SpuSearchVO>> spuResponseEntity = searchSpuFeignClient.listSpuBySpuIds(spuIds);
        Map<Long, SpuSearchVO> spuMap = spuResponseEntity.getData().stream().collect(Collectors.toMap(SpuSearchVO::getSpuId, s -> s));

        for (FlowSpuBO flowSpuBO : spuPage.getList()) {
            ProdEffectRespVO prodEffectRespVO = this.getProdEffectRespVO(flowSpuBO);
            // 商品信息
            if (spuMap.containsKey(flowSpuBO.getSpuId())) {
                SpuSearchVO spuSearchVO = spuMap.get(flowSpuBO.getSpuId());
                prodEffectRespVO.setSpuName(spuSearchVO.getSpuName());
                prodEffectRespVO.setSpuUrl(spuSearchVO.getMainImgUrl());
                prodEffectRespVO.setPrice(spuSearchVO.getPriceFee());
                prodEffectRespVO.setShopName(spuSearchVO.getShopName());
            }
            prodEffectRespList.add(prodEffectRespVO);
        }
        return prodEffectRespPage;
    }

    @Override
    public List<FlowProdEffectDataExcelVO> listProdEffectExcel(PageDTO pageDTO, ProdEffectDTO prodEffectDTO) {
        PageVO<ProdEffectRespVO> productEffect = getProductEffect(pageDTO, prodEffectDTO);
        List<FlowProdEffectDataExcelVO> excelList = BeanUtil.mapAsList(productEffect.getList(), FlowProdEffectDataExcelVO.class);
        for (FlowProdEffectDataExcelVO prodEffectData : excelList) {
            prodEffectData.setPrice(PriceUtil.toDecimalPrice(prodEffectData.getPrice()).longValue());
            prodEffectData.setPlaceOrderAmount(PriceUtil.toDecimalPrice(prodEffectData.getPlaceOrderAmount().longValue()).doubleValue());
            prodEffectData.setPayAmount(PriceUtil.toDecimalPrice(prodEffectData.getPayAmount().longValue()).doubleValue());
            prodEffectData.setRefundSuccessAmount(PriceUtil.toDecimalPrice(prodEffectData.getRefundSuccessAmount().longValue()).doubleValue());
        }
        return excelList;
    }

    @Override
    public List<ShopFlowInfoVO> listShopRankIngByFlow(DateTime endTime, Integer dayCount, Integer limit) {
        Date startTime = DateUtils.getBeforeDay(endTime, -dayCount);
        List<ShopFlowInfoVO> shopFlowInfoVOList = spuAnalyseMongoService.listShopRankIngByFlow(startTime, endTime, limit);
        if (CollUtil.isEmpty(shopFlowInfoVOList)) {
            return shopFlowInfoVOList;
        }
        List<Long> shopIds = shopFlowInfoVOList.stream().map(ShopFlowInfoVO::getShopId).collect(Collectors.toList());
        ServerResponseEntity<List<ShopDetailVO>> listServerResponseEntity = shopDetailFeignClient.listByShopIds(shopIds);
        if (!listServerResponseEntity.isSuccess()) {
            throw new LuckException(listServerResponseEntity.getMsg());
        }
        if (CollUtil.isEmpty(listServerResponseEntity.getData())) {
            return new ArrayList<>(0);
        }
        // 赋值店铺名称
        Map<Long, ShopDetailVO> shopDetailMap = listServerResponseEntity.getData().stream().collect(Collectors.toMap(ShopDetailVO::getShopId, p -> p));
        Iterator<ShopFlowInfoVO> iterator = shopFlowInfoVOList.iterator();
        while (iterator.hasNext()) {
            ShopFlowInfoVO shopFlowInfoVO = iterator.next();
            if (Objects.isNull(shopDetailMap.get(shopFlowInfoVO.getShopId()))) {
                iterator.remove();
                continue;
            }
            shopFlowInfoVO.setShopName(shopDetailMap.get(shopFlowInfoVO.getShopId()).getShopName());
        }
        return shopFlowInfoVOList;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSpuDataBySpuId(Long spuId) {
        if (Objects.isNull(spuId)) {
            return;
        }
        Query query = new Query(Criteria.where("spuId").is(spuId));
        mongoTemplate.remove(query, FlowSpuUserBO.class);
        mongoTemplate.remove(query, FlowSpuBO.class);
    }

    @Override
    public ShopFlowInfoVO getSupplierFlowInfoAndProdRankIng(Integer dayCount, Integer limit) {
        Long supplierId = AuthUserContext.get().getTenantId();
        DateTime endTime = DateUtil.offsetDay(DateUtil.beginOfDay(DateUtil.date()), 1);

        // 商品浏览量
        ShopFlowInfoVO shopFlowInfo = this.getSupplierFlowInfo(supplierId);
        // 商品访客榜单
        shopFlowInfo.setProdRanking(this.listSupplierProdRankIngByFlow(supplierId, DateUtil.offsetDay(endTime, -dayCount), endTime, limit));
        return shopFlowInfo;
    }

    @Override
    public List<SingleProdTrendVO> getSingleProdTend(SingleProdTrendDTO singleProdTrendDTO) {
        if (Objects.isNull(singleProdTrendDTO.getSpuId())) {
            throw new LuckException("趋势图商品id不能为空");
        }
        if (Objects.isNull(singleProdTrendDTO.getStartTime())
                || Objects.isNull(singleProdTrendDTO.getEndTime())) {
            throw new LuckException("趋势图筛选时间不能为空");
        }
        // 获取时间范围内对应spuId的流量数据
        Long startId = this.getFlowSpuId(singleProdTrendDTO.getStartTime(), false);
        Long endId = this.getFlowSpuId(singleProdTrendDTO.getEndTime(), true);
        Query spuFlowQuery = new Query(Criteria.where("spuId").is(singleProdTrendDTO.getSpuId())
                        .and("dateType").is(FlowDateTypeEnum.DAY.value())
                        .and("_id").gte(startId).lt(endId));
        spuFlowQuery = spuFlowQuery.with(Sort.by(Sort.Direction.ASC, "_id"));
        LOG.info("listSpuFlow查询语句为： {}", spuFlowQuery);
        List<FlowSpuBO> flowSpus = mongoTemplate.find(spuFlowQuery, FlowSpuBO.class);
        // 获取时间范围内对应spuId的订单数据
        FlowOrderItemDTO flowOrderItemDTO = new FlowOrderItemDTO();
        BeanUtils.copyProperties(singleProdTrendDTO, flowOrderItemDTO);
        List<FlowOrderItemVO> flowOrderItems = orderFeignClient.listFlowOrderItems(flowOrderItemDTO);
        // 统计时间范围内每日数据
        List<DateVO> days = DateUtils.findEveryDays(singleProdTrendDTO.getStartTime(), singleProdTrendDTO.getEndTime());
        int flowSpuIdx = 0;
        int flowOrderItemIdx = 0;
        SingleProdTrendVO singleProdTrendVO;
        List<SingleProdTrendVO> singleProdTrendVOList = new ArrayList<>(days.size());
        for (DateVO dateVO : days) {
            singleProdTrendVO = new SingleProdTrendVO();
            singleProdTrendVO.setCurrentDay(DateUtils.dateToStrYmd(dateVO.getStartTime()));
            // 统计访客量，浏览量
            flowSpuIdx = this.statisticsSingleProdFlowTrend(singleProdTrendVO, dateVO, flowSpus, flowSpuIdx);
            // 统计支付人数，支付商品件数
            flowOrderItemIdx = this.statisticsSingleProdOrderTrend(singleProdTrendVO, dateVO, flowOrderItems, flowOrderItemIdx);
            singleProdTrendVOList.add(singleProdTrendVO);
        }
        return singleProdTrendVOList;
    }

    private ProdEffectRespVO getProdEffectRespVO(FlowSpuBO flowSpuBO) {
        ProdEffectRespVO prodEffectRespVO = new ProdEffectRespVO();
        prodEffectRespVO.setSpuId(flowSpuBO.getSpuId());
        if (Objects.nonNull(flowSpuBO.getVisit())) {
            prodEffectRespVO.setExpose(flowSpuBO.getVisit());
        }
        if (Objects.nonNull(flowSpuBO.getVisitUser())) {
            prodEffectRespVO.setExposePersonNum(flowSpuBO.getVisitUser());
        }
        if (Objects.nonNull(flowSpuBO.getPlusShopCartUser())) {
            prodEffectRespVO.setAddCartPerson(flowSpuBO.getPlusShopCartUser());
        }
        if (Objects.nonNull(flowSpuBO.getPlusShopCart())) {
            prodEffectRespVO.setAddCart(flowSpuBO.getPlusShopCart());
        }
        if (Objects.nonNull(flowSpuBO.getPlaceOrderUser())) {
            prodEffectRespVO.setPlaceOrderPerson(flowSpuBO.getPlaceOrderUser());
        }
        if (Objects.nonNull(flowSpuBO.getPayUser())) {
            prodEffectRespVO.setPayPerson(flowSpuBO.getPayUser());
        }
        if (Objects.nonNull(flowSpuBO.getPlaceOrderNum())) {
            prodEffectRespVO.setPlaceOrderNum(flowSpuBO.getPlaceOrderNum());
        }
        if (Objects.nonNull(flowSpuBO.getPayNum())) {
            prodEffectRespVO.setPayNum(flowSpuBO.getPayNum());
        }
        if (Objects.nonNull(flowSpuBO.getPlaceOrderAmount())) {
            prodEffectRespVO.setPlaceOrderAmount(PriceUtil.toDoublePrice(flowSpuBO.getPlaceOrderAmount()));
        }
        if (Objects.nonNull(flowSpuBO.getPayAmount())) {
            prodEffectRespVO.setPayAmount(PriceUtil.toDoublePrice(flowSpuBO.getPayAmount()));
        }
        if (Objects.nonNull(flowSpuBO.getApplyRefundOrder())) {
            prodEffectRespVO.setRefundNum(flowSpuBO.getApplyRefundOrder());
        }
        if (Objects.nonNull(flowSpuBO.getApplyRefundUser())) {
            prodEffectRespVO.setRefundPerson(flowSpuBO.getApplyRefundUser());
        }
        if (Objects.nonNull(flowSpuBO.getSuccessRefundOrder())) {
            prodEffectRespVO.setRefundSuccessNum(flowSpuBO.getSuccessRefundOrder());
        }
        if (Objects.nonNull(flowSpuBO.getSuccessRefundUser())) {
            prodEffectRespVO.setRefundSuccessPerson(flowSpuBO.getSuccessRefundUser());
        }
        if (Objects.nonNull(flowSpuBO.getSuccessRefundAmount())) {
            prodEffectRespVO.setRefundSuccessAmount(PriceUtil.toDoublePrice(flowSpuBO.getSuccessRefundAmount()));
        }
        if (Objects.nonNull(flowSpuBO.getRefundRate())) {
            prodEffectRespVO.setRefundSuccessRate(flowSpuBO.getRefundRate());
        }
        return prodEffectRespVO;
    }

    /**
     * 获取供应商商品访问流量排行榜
     *
     * @param supplierId 供应商id
     * @param startTime 代销商品列表
     * @param endTime   结束时间
     * @param limit     条数
     * @return
     */
    public List<ProductFlowInfoVO> listSupplierProdRankIngByFlow(Long supplierId, Date startTime, Date endTime, Integer limit) {

        List<ProductFlowInfoVO> sortedProductFlowInfoVOList = spuAnalyseMongoService.listSupplierProdRankIngByFlow(startTime, endTime, supplierId);

        List<Long> spuIdList = sortedProductFlowInfoVOList.stream().map(ProductFlowInfoVO::getSpuId).collect(Collectors.toList());

        // 获取商品的名称和图片信息
        ServerResponseEntity<List<SpuSearchVO>> spuResponseEntity = searchSpuFeignClient.listSpuBySpuIds(spuIdList);
        if (!spuResponseEntity.isSuccess()) {
            throw new LuckException(spuResponseEntity.getMsg());
        }
        List<SpuSearchVO> spuSearchVOList = spuResponseEntity.getData();
        Map<Long, SpuSearchVO> spuSearchMap = spuSearchVOList.stream().collect(Collectors.toMap(SpuSearchVO::getSpuId, spuSearchVO -> spuSearchVO));

        List<ProductFlowInfoVO> resultProductFlowList = new ArrayList<>(limit);
        for (ProductFlowInfoVO productFlowInfoVO : sortedProductFlowInfoVOList) {
            if (Objects.nonNull(spuSearchMap.get(productFlowInfoVO.getSpuId()))) {
                productFlowInfoVO.setSpuName(spuSearchMap.get(productFlowInfoVO.getSpuId()).getSpuName());
                productFlowInfoVO.setSpuImg(spuSearchMap.get(productFlowInfoVO.getSpuId()).getMainImgUrl());
                productFlowInfoVO.setPayRate(AnalysisUtil.divAverage(productFlowInfoVO.getPayUserCount().intValue(), productFlowInfoVO.getVisitUserCount().intValue(), 4));
                resultProductFlowList.add(productFlowInfoVO);
            }
        }
        return resultProductFlowList;
    }

    /**
     * 获取供应商店铺今天商品浏览量和新老访客数量，代销商品的总流量等于供应商的流量
     *
     * @param supplierId 供应商Id
     * @return
     */
    public ShopFlowInfoVO getSupplierFlowInfo(Long supplierId) {
        Date startTime = DateUtil.beginOfDay(new Date());
        Long todayProductVisitCount = spuAnalyseMongoService.countProductVisitCount(startTime, DateUtil.offsetDay(startTime, 1), supplierId);
        Long yesterdayProductVisitCount = spuAnalyseMongoService.countProductVisitCount(DateUtil.beginOfDay(DateUtil.yesterday()), startTime, supplierId);

        ShopFlowInfoVO shopFlowInfoVO = new ShopFlowInfoVO();
        shopFlowInfoVO.setVisitCount(Objects.isNull(todayProductVisitCount) ? 0 : todayProductVisitCount);
        shopFlowInfoVO.setYesterdayVisitCount(Objects.isNull(yesterdayProductVisitCount) ? 0 : yesterdayProductVisitCount);

        startTime = DateUtil.offsetDay(DateUtil.beginOfDay(DateUtil.date()), -9);
        DateTime endTime = DateUtil.endOfDay(DateUtil.date());

        ShopFlowInfoVO shopUserIds = spuAnalyseMongoService.countVisitUserInPastTenDays(startTime, endTime, supplierId);
        shopFlowInfoVO.setVisitUserCount(shopUserIds.getVisitUserCount());
        shopFlowInfoVO.setNewVisitUserCount(shopUserIds.getNewVisitUserCount());
        shopFlowInfoVO.setOldVisitUserCount(shopUserIds.getOldVisitUserCount());
        return shopFlowInfoVO;
    }

    /**
     * 获取flowSpu类Id
     * @param date 日期
     * @param isEnd 是否为结束日期
     * @return flowSpuId
     */
    private Long getFlowSpuId(Date date, boolean isEnd) {
        // flowSpuId只包含年月日，不包含时分秒，如果是结束时间，需要额外加1天，id才正确
        if (isEnd) {
            date = DateUtils.getBeforeDay(date, 1);
        }
        return FlowUtil.stringToId(FlowUtil.dateToString(date) + FlowDateTypeEnum.DAY.value(), FlowSpuBO.ID_LENGTH);
    }

    /**
     * 统计单商品流量趋势
     * @param singleProdTrendVO 单商品流量趋势
     * @param dateVO 时间间隔
     * @param flowSpus 商品流量集合(有序集合，按id升序)
     * @param flowSpuIdx 商品流量下标
     * @return 商品流量下标
     */
    private int statisticsSingleProdFlowTrend(SingleProdTrendVO singleProdTrendVO,
                                              DateVO dateVO,
                                              List<FlowSpuBO> flowSpus,
                                              int flowSpuIdx) {
        int visitor = 0;
        long browse = 0L;
        if (CollectionUtils.isEmpty(flowSpus)) {
            singleProdTrendVO.setVisitor(visitor);
            singleProdTrendVO.setBrowse(browse);
            return flowSpuIdx;
        }
        // 数据统计
        Long endId = this.getFlowSpuId(dateVO.getEndTime(), true);
        FlowSpuBO flowSpuBO;
        for ( ; flowSpuIdx<flowSpus.size(); flowSpuIdx++) {
            flowSpuBO = flowSpus.get(flowSpuIdx);
            if (flowSpuBO.getId() > endId) {
                break;
            }
            visitor += Objects.isNull(flowSpuBO.getVisitUser()) ? 0 : flowSpuBO.getVisitUser();
            browse +=  Objects.isNull(flowSpuBO.getVisit()) ? 0L : flowSpuBO.getVisit();
        }
        singleProdTrendVO.setVisitor(visitor);
        singleProdTrendVO.setBrowse(browse);
        return flowSpuIdx;
    }

    /**
     * 统计单商品订单趋势
     * @param singleProdTrendVO 单商品流量趋势
     * @param dateVO 时间间隔
     * @param flowOrderItems 订单商品集合(有序集合，按payTime升序)
     * @param flowOrderItemIdx 订单商品下标
     * @return 订单商品下标
     */
    private int statisticsSingleProdOrderTrend(SingleProdTrendVO singleProdTrendVO,
                                              DateVO dateVO,
                                              List<FlowOrderItemVO> flowOrderItems,
                                              int flowOrderItemIdx) {
        int payPerson = 0;
        int payNum = 0;
        if (CollectionUtils.isEmpty(flowOrderItems)) {
            singleProdTrendVO.setPayPerson(payPerson);
            singleProdTrendVO.setPayNum(payNum);
            return flowOrderItemIdx;
        }
        // 数据统计
        FlowOrderItemVO flowOrderItem;
        for ( ; flowOrderItemIdx<flowOrderItems.size(); flowOrderItemIdx++) {
            flowOrderItem = flowOrderItems.get(flowOrderItemIdx);
            if (flowOrderItem.getPayTime().compareTo(dateVO.getEndTime()) > 0) {
                break;
            }
            payPerson ++;
            payNum += Objects.isNull(flowOrderItem.getCount()) ? 0 : flowOrderItem.getCount();
        }
        singleProdTrendVO.setPayPerson(payPerson);
        singleProdTrendVO.setPayNum(payNum);
        return flowOrderItemIdx;
    }
}
