package com.czg.service.order.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import com.czg.config.RabbitPublisher;
import com.czg.exception.CzgException;
import com.czg.order.entity.ShopProdStatistic;
import com.czg.order.param.SaleSummaryCountParam;
import com.czg.order.service.ShopProdStatisticService;
import com.czg.order.vo.SaleSummaryCountVo;
import com.czg.print.ProductReportPrintDTO;
import com.czg.product.service.ShopProdCategoryService;
import com.czg.service.order.mapper.ShopProdStatisticMapper;
import com.czg.utils.CzgStrUtils;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.apache.dubbo.config.annotation.DubboReference;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
import java.util.stream.Stream;

/**
 * 商品统计表 服务层实现。
 *
 * @author ww
 * @since 2025-11-21
 */
@Service
public class ShopProdStatisticServiceImpl extends ServiceImpl<ShopProdStatisticMapper, ShopProdStatistic> implements ShopProdStatisticService {

    @Resource
    private RabbitPublisher rabbitPublisher;
    @DubboReference
    private ShopProdCategoryService shopProdCategoryService;

    @Override
    public List<ShopProdStatistic> getArchiveTradeDataBy10(Long shopId, Integer day) {
        LocalDate currentDate = LocalDate.now();
        LocalDate startDate = currentDate.minusDays(day - 1);
        List<ShopProdStatistic> archiveTradeData = getArchiveTradeData(new SaleSummaryCountParam()
                .setShopId(shopId).setProductName(null).setRangeType("").setBeginDate(startDate)
                .setEndDate(currentDate).setCategoryId(0L));
        // 按照 saleCount 降序排序
        return archiveTradeData.stream()
                .sorted(
                        Comparator.comparing(
                                ShopProdStatistic::getSaleCount,
                                Comparator.nullsLast(BigDecimal::compareTo).reversed()
                        )
                )
                .limit(10)
                .toList();

    }


    @Override
    public SaleSummaryCountVo summaryCount(SaleSummaryCountParam param) {
        LocalDate currentDate = LocalDate.now();
        if ("today".equals(param.getRangeType())) {
            return mapper.summaryCountByDay(param.getShopId(), currentDate, param.getProductName(), param.getCategoryId());
        }
        if ("yesterday".equals(param.getRangeType())) {
            return mapper.summaryCountSingleDate(param.getShopId(), currentDate.minusDays(1), param.getProductName(), param.getCategoryId());
        }

        if (param.getBeginDate().isAfter(currentDate)) {
            throw new CzgException("开始时间不能晚于当前时间");
        }
        if (param.getBeginDate().equals(param.getEndDate())) {
            return mapper.summaryCountSingleDate(param.getShopId(), param.getBeginDate(), param.getProductName(), param.getCategoryId());
        }
        if (param.getEndDate().isBefore(currentDate)) {
            return mapper.summaryCountDateRange(param.getShopId(), param.getBeginDate(), param.getEndDate(), param.getProductName(), param.getCategoryId());
        }
        SaleSummaryCountVo todaySummary = mapper.summaryCountByDay(param.getShopId(), currentDate, param.getProductName(), param.getCategoryId());
        SaleSummaryCountVo dateRangeSummary = mapper.summaryCountDateRange(param.getShopId(), param.getBeginDate(), param.getEndDate(), param.getProductName(), param.getCategoryId());
        return mergeSummaryCountVo(todaySummary, dateRangeSummary);
    }

    @Override
    public void summaryPrint(SaleSummaryCountParam param) {
        List<Map<String, Object>> list = new ArrayList<>();
        // 1. 获取统计数据
        Map<Long, String> categoryMap = shopProdCategoryService.getCategoryIdNameMap(param.getShopId());
        if (param.getBeginDate() != null && param.getEndDate() != null) {
            // 计算相差天数
            long days = java.time.temporal.ChronoUnit.DAYS.between(param.getBeginDate(), param.getEndDate());
            List<LocalDate> dateList = LongStream.rangeClosed(0, days)
                    .mapToObj(param.getBeginDate()::plusDays)
                    .toList();
            for (LocalDate localDate : dateList) {
                Map<String, Object> printDTO = new HashMap<>();
                SaleSummaryCountParam queryDay = new SaleSummaryCountParam()
                        .setShopId(param.getShopId())
                        .setOperator(param.getOperator())
                        .setBeginDate(localDate)
                        .setEndDate(localDate);
                ProductReportPrintDTO print = buildProductReportPrint(queryDay, categoryMap);
                printDTO.put("date", localDate);
                printDTO.put("data", print);
                list.add(printDTO);
            }
        }
        if (CollUtil.isNotEmpty(list)) {
            rabbitPublisher.sendOtherPrintMsg(param.getShopId(), list, "PRODUCT_REPORT");
        }
    }


    private ProductReportPrintDTO buildProductReportPrint(SaleSummaryCountParam param, Map<Long, String> categoryMap) {
        SaleSummaryCountVo saleSummaryCountVo = summaryCount(param);
        List<ShopProdStatistic> prodStatisticList = getArchiveTradeData(param);

        ProductReportPrintDTO printDTO = new ProductReportPrintDTO();
        String statisticsTime = LocalDateTimeUtil.format(param.getBeginDate(), "yyyy/MM/dd");

        printDTO.setStatisticsTime(statisticsTime);
        printDTO.setOperator(param.getOperator());
        if (saleSummaryCountVo != null) {
            printDTO.setTotalProductCount(saleSummaryCountVo.getSaleCount());
            printDTO.setTotalActualAmount(saleSummaryCountVo.getTotalAmount());
        } else {
            printDTO.setTotalProductCount(BigDecimal.ZERO);
            printDTO.setTotalActualAmount(BigDecimal.ZERO);
        }
        if (CollUtil.isNotEmpty(prodStatisticList)) {
            // 4. 先按分类ID分组商品
            Map<Long, List<ShopProdStatistic>> groupByCategory = prodStatisticList.stream()
                    .collect(Collectors.groupingBy(ShopProdStatistic::getCategoryId));

            // 5. 组装成新结构 List<CategoryItem>
            List<ProductReportPrintDTO.CategoryItem> categoryItems = new ArrayList<>();

            for (Map.Entry<Long, List<ShopProdStatistic>> entry : groupByCategory.entrySet()) {
                Long categoryId = entry.getKey();
                List<ShopProdStatistic> productList = entry.getValue();

                // 分类名称
                String categoryName = categoryMap.getOrDefault(categoryId, "未分类");

                // 商品列表
                List<ProductReportPrintDTO.ProductItem> productItems = new ArrayList<>();
                BigDecimal categoryTotalNumber = BigDecimal.ZERO;
                BigDecimal categoryTotalActual = BigDecimal.ZERO;
                BigDecimal categoryTotalSales = BigDecimal.ZERO;

                for (ShopProdStatistic s : productList) {
                    ProductReportPrintDTO.ProductItem item = new ProductReportPrintDTO.ProductItem();
                    item.setProductName(s.getProductName());
                    item.setNumber(s.getValidSaleCount());
                    item.setActualAmount(s.getValidSaleAmount());
                    item.setSalesAmount(s.getSaleAmount());
                    productItems.add(item);

                    // 分类汇总累加
                    categoryTotalNumber = categoryTotalNumber.add(s.getValidSaleCount());
                    categoryTotalActual = categoryTotalActual.add(s.getValidSaleAmount());
                    categoryTotalSales = categoryTotalSales.add(s.getSaleAmount());
                }

                // 构建分类项
                ProductReportPrintDTO.CategoryItem categoryItem = new ProductReportPrintDTO.CategoryItem();
                categoryItem.setCategoryName(categoryName);
                categoryItem.setNumber(categoryTotalNumber);
                categoryItem.setActualAmount(categoryTotalActual);
                categoryItem.setSalesAmount(categoryTotalSales);
                categoryItem.setProductItems(productItems);

                categoryItems.add(categoryItem);
            }
            printDTO.setItems(categoryItems);
        }
        return printDTO;
    }

    @Override
    public List<ShopProdStatistic> getArchiveTradeData(SaleSummaryCountParam param) {
        LocalDate currentDate = LocalDate.now();
        param.setProductName(CzgStrUtils.getStrOrNull(param.getProductName()));
        if ("today".equals(param.getRangeType())) {
            return getRealTimeDataByDay(param.getShopId(), currentDate, param.getProductName(), param.getCategoryId());
        }

        if ("yesterday".equals(param.getRangeType())) {
            return getProdStatSingleDate(param.getShopId(), currentDate.minusDays(1),
                    param.getProductName(), param.getCategoryId());
        }

        if (param.getBeginDate().isAfter(currentDate)) {
            throw new CzgException("开始时间不能晚于当前时间");
        }
        if (param.getBeginDate().equals(param.getEndDate())) {
            return getProdStatSingleDate(param.getShopId(), param.getBeginDate(), param.getProductName(),
                    param.getCategoryId());
        }
        if (param.getEndDate().isBefore(currentDate)) {
            return getProdStatDateRange(param.getShopId(), param.getBeginDate(), param.getEndDate(),
                    param.getProductName(), param.getCategoryId());
        }
        List<ShopProdStatistic> realTimeDataByDay = getRealTimeDataByDay(param.getShopId(), currentDate,
                param.getProductName(), param.getCategoryId());
        List<ShopProdStatistic> dateRange = getProdStatDateRange(param.getShopId(), param.getBeginDate(),
                param.getEndDate(), param.getProductName(), param.getCategoryId());

        return mergeProdStatistic(realTimeDataByDay, dateRange);
    }


    @Override
    public void statisticAndInsert(Long shopId, LocalDate day, Long categoryId) {
        List<ShopProdStatistic> realTimeData = getRealTimeDataByDay(shopId, day, null, categoryId);
        if (CollUtil.isNotEmpty(realTimeData)) {
            boolean exists = exists(QueryWrapper.create().eq(ShopProdStatistic::getShopId, shopId).eq(ShopProdStatistic::getCreateDay, day));
            if (exists) {
                remove(QueryWrapper.create().eq(ShopProdStatistic::getShopId, shopId).eq(ShopProdStatistic::getCreateDay, day));
            }
            mapper.insertBatchSelective(realTimeData, 50);
        }
    }

    @Override
    public List<ShopProdStatistic> getRealTimeDataByDay(Long shopId, LocalDate day, String productName, Long categoryId) {
        List<ShopProdStatistic> shopProdStatistics = mapper.selectProStatByDay(shopId, day, productName, categoryId);
        // 过滤掉没有有效数据的记录
        shopProdStatistics = shopProdStatistics.stream()
                .filter(ShopProdStatistic::isValid)
                .peek(ShopProdStatistic::initValidData)
                .toList();
        return shopProdStatistics;
    }

    @Override
    public List<ShopProdStatistic> getProdStatSingleDate(Long shopId, LocalDate day, String productName, Long categoryId) {
        return mapper.getProdStatSingleDate(shopId, day, productName, categoryId);
    }

    @Override
    public List<ShopProdStatistic> getProdStatDateRange(Long shopId, LocalDate start, LocalDate end, String productName, Long categoryId) {
        return mapper.getProdStatDateRange(shopId, start, end, productName, categoryId);
    }

    private SaleSummaryCountVo mergeSummaryCountVo(SaleSummaryCountVo todaySummary, SaleSummaryCountVo dateRangeSummary) {
        if (todaySummary == null) {
            todaySummary = new SaleSummaryCountVo();
        }
        if (dateRangeSummary == null) {
            dateRangeSummary = new SaleSummaryCountVo();
        }

        return new SaleSummaryCountVo(
                safeAdd(todaySummary.getTotalAmount(), dateRangeSummary.getTotalAmount()),
                safeAdd(todaySummary.getRefundAmount(), dateRangeSummary.getRefundAmount()),
                safeAdd(todaySummary.getSaleCount(), dateRangeSummary.getSaleCount()),
                safeAdd(todaySummary.getRefundCount(), dateRangeSummary.getRefundCount())
        );
    }


    /**
     * 合并实时数据和日期范围数据
     *
     * @param realTimeDataByDay 实时数据
     * @param dateRange         日期范围数据
     * @return 合并后的数据
     */
    private List<ShopProdStatistic> mergeProdStatistic(List<ShopProdStatistic> realTimeDataByDay, List<ShopProdStatistic> dateRange) {
        // 1. 使用防御性编程，确保非空列表
        List<ShopProdStatistic> realTimeData = Optional.ofNullable(realTimeDataByDay).orElseGet(ArrayList::new);
        List<ShopProdStatistic> rangeData = Optional.ofNullable(dateRange).orElseGet(ArrayList::new);

        calculateValidData(realTimeData);
        calculateValidData(rangeData);

        return Stream.concat(realTimeData.stream(), rangeData.stream())
                .filter(Objects::nonNull)
                .collect(Collectors.toMap(
                        ShopProdStatistic::getProdId,
                        Function.identity(),
                        this::mergeStatistics
                ))
                .values()
                .stream()
                .peek(this::recalculateValidData)
                .toList();
    }

    /**
     * 计算有效销售数据
     */
    private void calculateValidData(List<ShopProdStatistic> statistics) {
        if (statistics == null || statistics.isEmpty()) {
            return;
        }

        statistics.forEach(stat -> {
            if (stat != null) {
                stat.setValidSaleCount(safeSubtract(stat.getSaleCount(), stat.getRefundCount()));
                stat.setValidSaleAmount(safeSubtract(stat.getValidSaleAmount(), stat.getRefundAmount()));
            }
        });
    }

    /**
     * 合并两个统计对象
     */
    private ShopProdStatistic mergeStatistics(ShopProdStatistic stat1, ShopProdStatistic stat2) {
        // 使用第一个非空对象作为基准
        ShopProdStatistic baseStat = Optional.ofNullable(stat1).orElse(stat2);
        ShopProdStatistic otherStat = stat1 == null ? null : stat2;

        if (otherStat == null) {
            return baseStat;
        }

        ShopProdStatistic merged = new ShopProdStatistic();

        // 设置基本信息（优先使用非空值）
        merged.setId(baseStat.getId());
        merged.setShopId(baseStat.getShopId());
        merged.setProdId(baseStat.getProdId());
        merged.setProductName(
                Optional.ofNullable(baseStat.getProductName())
                        .orElse(otherStat.getProductName())
        );

        // 合并数值字段
        merged.setSaleCount(safeAdd(baseStat.getSaleCount(), otherStat.getSaleCount()));
        merged.setSaleAmount(safeAdd(baseStat.getSaleAmount(), otherStat.getSaleAmount()));
        merged.setRefundCount(safeAdd(baseStat.getRefundCount(), otherStat.getRefundCount()));
        merged.setRefundAmount(safeAdd(baseStat.getRefundAmount(), otherStat.getRefundAmount()));

        return merged;
    }

    /**
     * 重新计算合并后的有效数据
     */
    private void recalculateValidData(ShopProdStatistic stat) {
        if (stat != null) {
            stat.setValidSaleCount(safeSubtract(stat.getSaleCount(), stat.getRefundCount()));
            stat.setValidSaleAmount(safeSubtract(stat.getSaleAmount(), stat.getRefundAmount()));
        }
    }

    /**
     * 安全的BigDecimal加法（处理null值）
     */
    private BigDecimal safeAdd(BigDecimal num1, BigDecimal num2) {
        BigDecimal safeNum1 = Optional.ofNullable(num1).orElse(BigDecimal.ZERO);
        BigDecimal safeNum2 = Optional.ofNullable(num2).orElse(BigDecimal.ZERO);
        return safeNum1.add(safeNum2);
    }

    /**
     * 安全的BigDecimal减法（处理null值）
     */
    private BigDecimal safeSubtract(BigDecimal num1, BigDecimal num2) {
        BigDecimal safeNum1 = Optional.ofNullable(num1).orElse(BigDecimal.ZERO);
        BigDecimal safeNum2 = Optional.ofNullable(num2).orElse(BigDecimal.ZERO);
        return safeNum1.subtract(safeNum2);
    }
}
