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

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.collection.CollUtil;
import com.czg.exception.CzgException;
import com.czg.order.entity.ShopOrderStatistic;
import com.czg.order.service.ShopOrderStatisticService;
import com.czg.order.vo.*;
import com.czg.service.order.mapper.ShopOrderStatisticMapper;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;


/**
 * 店铺订单统计报表 服务层实现。
 *
 * @author ww
 * @since 2025-11-20
 */
@Slf4j
@Service
public class ShopOrderStatisticServiceImpl extends ServiceImpl<ShopOrderStatisticMapper, ShopOrderStatistic> implements ShopOrderStatisticService {

    @Override
    public ShopOrderStatistic getArchiveTradeData(Long shopId, String rangeType, LocalDate start, LocalDate end) {
        LocalDate currentDate = LocalDate.now();
        if ("today".equals(rangeType)) {
            return getRealTimeDataByDay(shopId, currentDate);
        } else if ("yesterday".equals(rangeType)) {
            return getStatSingleDate(shopId, currentDate.minusDays(1));
        }
        if (start.isAfter(currentDate)) {
            throw new CzgException("开始时间不能晚于当前时间");
        }
        if (start.equals(end)) {
            return getStatSingleDate(shopId, start);
        }
        //包括当前时间
        if (end.isBefore(currentDate)) {
            return getStatDateRange(shopId, start, end);
        } else {
            ShopOrderStatistic realTimeDataByDay = getRealTimeDataByDay(shopId, currentDate);
            ShopOrderStatistic statDateRange = getStatDateRange(shopId, start, end);
            ShopOrderStatistic shopOrderStatistic = ShopOrderStatistic.mergeStatistics(realTimeDataByDay, statDateRange);
            shopOrderStatistic.setShopId(shopId);
            calculateShopOrderStatistic(shopOrderStatistic);
            return shopOrderStatistic;
        }
    }

    @Override
    public List<TotalVo> getDateAmount(Long shopId, Integer day) {
        LocalDate currentDate = LocalDate.now();
        LocalDate startDate;
        TotalVo onlineDataAmount = mapper.getOnlineDataAmount(shopId, currentDate);
        if (onlineDataAmount == null) {
            onlineDataAmount = new TotalVo(currentDate);
        }
        if (day <= 1) {
            return List.of(onlineDataAmount);
        } else {
            startDate = currentDate.minusDays(day - 1);
        }
        List<TotalVo> statDateRange = mapper.getStatDateRange(shopId, startDate, currentDate);
        return TotalVo.mergeAndFillData(onlineDataAmount, statDateRange, startDate, currentDate);
    }

    @Override
    public List<CountPayTypeVo> getSummaryPayTypeData(Long shopId, Integer day) {
        LocalDate currentDate = LocalDate.now();
        LocalDate startDate;
        if (day <= 1) {
            Map<String, BigDecimal> onlinePayTypeDate = mapper.getOnlinePayTypeDate(shopId, currentDate);
            return CountPayTypeVo.realTimeDataByDay(onlinePayTypeDate);
        } else {
            startDate = currentDate.minusDays(day - 1);
        }
        return CountPayTypeVo.mergePayTypeData(
                mapper.getOnlinePayTypeDate(shopId, currentDate),
                mapper.getPayTypeDateRangeRaw(shopId, startDate, currentDate));
    }

    @Override
    public List<ProfitRateVO> profitRateBarChart(Long shopId, Integer day) {
        LocalDate currentDate = LocalDate.now();
        LocalDate startDate;
        ShopOrderStatistic onlineDataAmount = getRealTimeDataByDay(shopId, currentDate);
        ProfitRateVO onlineProfitRateBarChart = new ProfitRateVO(currentDate);
        if (onlineDataAmount != null) {
            onlineProfitRateBarChart.setProfitRate(onlineDataAmount.getProfitRate());
            onlineProfitRateBarChart.setNetProfitRate(onlineDataAmount.getNetProfitRate());
        }
        if (day <= 1) {
            return List.of(onlineProfitRateBarChart);
        } else {
            startDate = currentDate.minusDays(day - 1);
        }
        List<ProfitRateVO> statDateRange = mapper.profitRateBarChart(shopId, startDate, currentDate);
        return ProfitRateVO.mergeAndFillData(onlineProfitRateBarChart, statDateRange, startDate, currentDate);
    }

    @Override
    public List<CostLineChartVO> costLineChart(Long shopId, Integer day) {
        LocalDate currentDate = LocalDate.now();
        LocalDate startDate = currentDate;
        if (day == 7) {
            startDate = currentDate.minusDays(6);
        } else if (day == 30) {
            startDate = currentDate.minusDays(29);
        }
        CostLineChartVO onlineCostLineChart = new CostLineChartVO(currentDate);
        BigDecimal productCostAmount = getProductCostAmount(shopId, currentDate);
        onlineCostLineChart.setProductCostAmount(productCostAmount);

        List<CostLineChartVO> statDateRange = mapper.costLineChart(shopId, startDate, currentDate);
        return CostLineChartVO.mergeAndFillData(onlineCostLineChart, statDateRange, startDate, currentDate);
    }


    //u--------------------------------------------------_---------------------------------------------------------

    @Override
    public void statisticAndInsert(Long shopId, LocalDate day) {
        ShopOrderStatistic realTimeData = getRealTimeDataByDay(shopId, day);
        if (realTimeData != null && realTimeData.hasValidData(realTimeData)) {
            ShopOrderStatistic result = getOne(QueryWrapper.create().eq(ShopOrderStatistic::getShopId, shopId).eq(ShopOrderStatistic::getStatisticDate, day));
            if (result != null) {
                removeById(result);
            }
            save(realTimeData);
        }
    }

    @Override
    public ShopOrderStatistic getRealTimeDataByDay(Long shopId, LocalDate day) {
        ShopOrderStatistic result = new ShopOrderStatistic();

        ShopOrderStatistic onlineStat = mapper.getOnlineStatSingleDate(shopId, day);
        ShopOrderStatistic orderStat = mapper.getOrderStatSingleDate(shopId, day);
        //待支付统计
        ShopOrderStatistic unpaidOrder = mapper.getUnpaidOrder(shopId, day);
        //退菜数量/金额
        ShopOrderStatistic refundItem = mapper.getRefundItem(shopId, day);
        ShopOrderStatistic userFlowStat = mapper.getShopUserFlowStatSingleDate(shopId, day);
        Long discountCount = mapper.countDiscountOrder(shopId, day);
        BigDecimal discountAmount = mapper.countDiscountAmount(shopId, day);
        Long tableCount = mapper.countShopTable(shopId);
        Long newMemberCount = mapper.countNewMember(shopId, day);
        //获取商品成本金额
        BigDecimal productCostAmount = getProductCostAmount(shopId, day);
        // 合并结果
        CopyOptions copyOptions = CopyOptions.create().setIgnoreNullValue(true);
        if (onlineStat != null) {
            BeanUtil.copyProperties(onlineStat, result, copyOptions);
        }
        if (orderStat != null) {
            BeanUtil.copyProperties(orderStat, result, copyOptions);
        }
        if (userFlowStat != null) {
            BeanUtil.copyProperties(userFlowStat, result, copyOptions);
        }
        if (unpaidOrder != null) {
            BeanUtil.copyProperties(unpaidOrder, result, copyOptions);
        }
        if (refundItem != null) {
            BeanUtil.copyProperties(refundItem, result, copyOptions);
        }
        //TODO 团购 抖音 美团 统计 走宏哥那获取


        result.setShopId(shopId);
        result.setStatisticDate(day);

        result.setDiscountCount(Optional.ofNullable(discountCount).orElse(0L));
        result.setDiscountAmount(Optional.ofNullable(discountAmount).orElse(BigDecimal.ZERO));
        result.setNewMemberCount(Optional.ofNullable(newMemberCount).orElse(0L));
        result.setTableCount(Optional.ofNullable(tableCount).orElse(0L));
        result.setProductCostAmount(Optional.ofNullable(productCostAmount).orElse(BigDecimal.ZERO));

        //会员充值金额（线上充值+现金充值+霸王餐充值）
        BigDecimal cashRechargeAmount = Objects.requireNonNullElse(result.getCashRechargeAmount(), BigDecimal.ZERO);
        BigDecimal onlineRechargeAmount = Objects.requireNonNullElse(result.getOnlineRechargeAmount(), BigDecimal.ZERO);
        BigDecimal freeRechargeAmount = Objects.requireNonNullElse(onlineStat != null ? onlineStat.getBackDiscountAmount() : null, BigDecimal.ZERO);
        BigDecimal rechargeAmount = cashRechargeAmount.add(onlineRechargeAmount).add(freeRechargeAmount);
        result.setRechargeAmount(rechargeAmount);
        //会员充值退款 充值退款金额（线上退款+现金退款）
        BigDecimal onlineRechargeRefundAmount = Objects.requireNonNullElse(
                (onlineStat != null) ? onlineStat.getOnlineRechargeRefundAmount() : null,
                BigDecimal.ZERO
        );
        BigDecimal userFlowRefundAmount = Objects.requireNonNullElse(
                (userFlowStat != null) ? userFlowStat.getCashRechargeRefundAmount() : null,
                BigDecimal.ZERO
        );
        BigDecimal rechargeRefundAmountTotal = onlineRechargeRefundAmount.add(userFlowRefundAmount);
        result.setOnlineRefundAmount(onlineStat == null ? BigDecimal.ZERO : onlineStat.getOnlineRefundAmount());
        result.setOnlineRechargeRefundAmount(onlineStat == null ? BigDecimal.ZERO : onlineStat.getOnlineRechargeRefundAmount());
        result.setRechargeRefundAmount(rechargeRefundAmountTotal);
        //实付金额 (线上付款 现金支付 会员支付 挂账)
        BigDecimal onlinePayAmount = (onlineStat != null)
                ? Objects.requireNonNullElse(onlineStat.getOnlinePayAmount(), BigDecimal.ZERO)
                : BigDecimal.ZERO;

        BigDecimal cashPayAmount = (orderStat != null)
                ? Objects.requireNonNullElse(orderStat.getCashPayAmount(), BigDecimal.ZERO)
                : BigDecimal.ZERO;

        BigDecimal memberPayAmount = (orderStat != null)
                ? Objects.requireNonNullElse(orderStat.getMemberPayAmount(), BigDecimal.ZERO)
                : BigDecimal.ZERO;

        BigDecimal creditPayAmount = (orderStat != null)
                ? Objects.requireNonNullElse(orderStat.getCreditPayAmount(), BigDecimal.ZERO)
                : BigDecimal.ZERO;
        BigDecimal totalPayAmount = onlinePayAmount
                .add(cashPayAmount)
                .add(memberPayAmount)
                .add(creditPayAmount);
        result.setPayAmount(totalPayAmount);
        calculateShopOrderStatistic(result);
        return result;
    }

    /**
     * 获取商品总成本价
     */
    private BigDecimal getProductCostAmount(Long shopId, LocalDate day) {
        BigDecimal productCostAmount = BigDecimal.ZERO;
        //获取orderDetail信息  productId skuId 数量
        List<ProductCostAmountVO> orderDetailProduct = mapper.getOrderDetailProduct(shopId, day);
        orderDetailProduct = orderDetailProduct.stream()
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollUtil.isEmpty(orderDetailProduct)) {
            return productCostAmount;
        }
        //获取商品sku成本价  productId skuId price
        List<ProductCostAmountVO> skuCostAmountList = mapper.getSkuCostAmount(shopId);
//        //获取商品耗材成本价  productId price
        List<ProductCostAmountVO> consCostAmountList = mapper.getConsCostAmount(shopId);
        if (CollUtil.isEmpty(skuCostAmountList) && CollUtil.isEmpty(consCostAmountList)) {
            return productCostAmount;
        }
        Map<Long, BigDecimal> skuCostAmountMap = skuCostAmountList.stream()
                .collect(Collectors.toMap(ProductCostAmountVO::getSkuId, ProductCostAmountVO::getCostAmount));
        Map<Long, BigDecimal> consCostAmountMap = consCostAmountList.stream()
                .collect(Collectors.toMap(ProductCostAmountVO::getProductId, ProductCostAmountVO::getCostAmount));
        for (ProductCostAmountVO productCostAmountVO : orderDetailProduct) {
            if (productCostAmountVO == null || productCostAmountVO.getCount() <= 0) {
                continue;
            }
            if (skuCostAmountMap.containsKey(productCostAmountVO.getSkuId())) {
                BigDecimal costAmount = skuCostAmountMap.get(productCostAmountVO.getSkuId());
                productCostAmount = productCostAmount.add(costAmount.multiply(new BigDecimal(productCostAmountVO.getCount())).setScale(2, RoundingMode.HALF_UP));
            } else if (consCostAmountMap.containsKey(productCostAmountVO.getProductId())) {
                BigDecimal costAmount = consCostAmountMap.get(productCostAmountVO.getProductId());
                productCostAmount = productCostAmount.add(costAmount.multiply(new BigDecimal(productCostAmountVO.getCount())).setScale(2, RoundingMode.HALF_UP));
            }
        }
        return productCostAmount;
    }

    /**
     * 统一入口方法
     * 计算 客单价 翻台率 净利润 净利率 毛利润 毛利率
     */
    private void calculateShopOrderStatistic(ShopOrderStatistic result) {
        calculateProfitInfo(result);
        calculateAvgPayAmount(result);
        calculateTurnoverRate(result);
    }


    /**
     * 计算毛利润、毛利率（净利润、净利率与之一致）
     */
    private void calculateProfitInfo(ShopOrderStatistic result) {
        // 初始化商品成本金额（避免null）
        if (result.getProductCostAmount() == null) {
            result.setProductCostAmount(BigDecimal.ZERO);
        }

        // 计算毛利润（订单实付金额-商品成本）
        BigDecimal profitAmount = result.getPayAmount().subtract(result.getProductCostAmount());
        result.setProfitAmount(profitAmount);

        // 计算毛利率（(订单实付金额-商品成本)/订单实付金额*100%）
        BigDecimal profitRate = BigDecimal.ZERO;
        if (result.getPayAmount().compareTo(BigDecimal.ZERO) > 0) {
            profitRate = profitAmount.divide(result.getPayAmount(), 4, RoundingMode.HALF_DOWN)
                    .multiply(BigDecimal.valueOf(100));
        }
        result.setProfitRate(profitRate);

        // 净利润、净利率目前和毛利润、毛利率一致
        result.setNetProfitAmount(profitAmount);
        result.setNetProfitRate(profitRate);
    }

    /**
     * 计算客单价
     * 客单价 = 实付金额 / 就餐人数（就餐人数为0时直接使用实付金额）
     */
    private void calculateAvgPayAmount(ShopOrderStatistic result) {
        BigDecimal avgPayAmount;
        // 校验就餐人数有效性
        if (result.getCustomerCount() != null && result.getCustomerCount() > 0) {
            avgPayAmount = result.getPayAmount().divide(
                    new BigDecimal(result.getCustomerCount()), 2, RoundingMode.HALF_DOWN
            );
        } else {
            avgPayAmount = result.getPayAmount();
        }
        result.setAvgPayAmount(avgPayAmount);
    }

    /**
     * 计算翻台率
     * 翻台率 = （订单数-桌台数）/桌台数*100%（差值≤0时翻台率为0）
     */
    private void calculateTurnoverRate(ShopOrderStatistic result) {
        BigDecimal turnoverRate = BigDecimal.ZERO;

        // 校验桌台数有效性
        if (result.getTableCount() != null && result.getTableCount() > 0) {
            long orderTableDifference = result.getOrderCount() - result.getTableCount();
            // 仅当差值大于0时计算翻台率
            if (orderTableDifference > 0) {
                turnoverRate = new BigDecimal(orderTableDifference)
                        .divide(new BigDecimal(result.getTableCount()), 2, RoundingMode.HALF_DOWN);
            }
        }
        result.setTurnoverRate(turnoverRate);
    }


    @Override
    public ShopOrderStatistic getStatSingleDate(Long shopId, LocalDate day) {
        ShopOrderStatistic result = getOne(QueryWrapper.create().eq(ShopOrderStatistic::getShopId, shopId).eq(ShopOrderStatistic::getStatisticDate, day));
        if (result == null) {
            result = new ShopOrderStatistic();
            result.setShopId(shopId);
            result.setStatisticDate(day);
            result.init();
        }
        return result;
    }

    @Override
    public ShopOrderStatistic getStatDateRange(Long shopId, LocalDate start, LocalDate end) {
        ShopOrderStatistic result = mapper.countStatistic(shopId, start, end);
        if (result == null) {
            result = new ShopOrderStatistic();
            result.setStatisticDate(start);
            result.init();
        }
        result.setShopId(shopId);
        return result;
    }

}
