package com.haha.admin.task;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.haha.entity.*;
import com.haha.mapper.*;
import com.haha.common.constant.OrderConstants;
import com.haha.common.utils.TraceIdUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

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

@Slf4j
@RequiredArgsConstructor
@Component
public class StatisticsTask {

    private final OrderMapper orderMapper;
    private final OrderItemMapper orderItemMapper;
    private final DeviceMapper deviceMapper;
    private final ShopMapper shopMapper;
    private final UserMapper userMapper;
    private final StatCategoryDailyMapper statCategoryDailyMapper;
    private final StatProductDailyMapper statProductDailyMapper;
    private final StatDeviceDailyMapper statDeviceDailyMapper;
    private final StatShopDailyMapper statShopDailyMapper;
    private final StatUserRepurchaseMapper statUserRepurchaseMapper;

    @Scheduled(cron = "0 5 0 * * ?")
    public void aggregateDailyStatistics() {
        // 生成独立的 TraceId 用于日志追踪
        String traceId = TraceIdUtils.generateTraceId();
        TraceIdUtils.setTraceId(traceId);
        
        LocalDate yesterday = LocalDate.now().minusDays(1);
        log.info("开始执行每日统计汇总任务，统计日期：{}", yesterday);
        
        try {
            aggregateCategoryDaily(yesterday);
            aggregateProductDaily(yesterday);
            aggregateDeviceDaily(yesterday);
            aggregateShopDaily(yesterday);
            aggregateUserRepurchase(yesterday);
            
            log.info("每日统计汇总任务执行完成");
        } catch (Exception e) {
            log.error("每日统计汇总任务执行失败", e);
        } finally {
            // 清理 MDC 上下文
            TraceIdUtils.removeTraceId();
        }
    }

    private void aggregateCategoryDaily(LocalDate statDate) {
        LocalDateTime startDateTime = statDate.atStartOfDay();
        LocalDateTime endDateTime = statDate.atTime(LocalTime.MAX);
        
        List<OrderItem> items = orderItemMapper.selectList(
                new LambdaQueryWrapper<OrderItem>()
                        .ge(OrderItem::getPayTime, startDateTime)
                        .le(OrderItem::getPayTime, endDateTime)
        );
        
        Map<String, List<OrderItem>> categoryMap = items.stream()
                .filter(item -> item.getProductType() != null)
                .collect(Collectors.groupingBy(OrderItem::getProductType));
        
        for (Map.Entry<String, List<OrderItem>> entry : categoryMap.entrySet()) {
            String category = entry.getKey();
            List<OrderItem> categoryItems = entry.getValue();
            
            StatCategoryDaily stat = new StatCategoryDaily();
            stat.setStatDate(statDate);
            stat.setCategory(category);
            stat.setShopId(null);
            stat.setQuantity(categoryItems.stream().mapToInt(i -> i.getQuantity() != null ? i.getQuantity() : 0).sum());
            stat.setOrderCount((int) categoryItems.stream().map(OrderItem::getOrderId).distinct().count());
            
            BigDecimal salesAmount = categoryItems.stream()
                    .map(i -> i.getTotalAmount() != null ? i.getTotalAmount() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            stat.setSalesAmount(salesAmount);
            
            BigDecimal costAmount = categoryItems.stream()
                    .map(i -> {
                        if (i.getCostPrice() != null && i.getQuantity() != null) {
                            return i.getCostPrice().multiply(BigDecimal.valueOf(i.getQuantity()));
                        }
                        return BigDecimal.ZERO;
                    })
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            stat.setCostAmount(costAmount);
            stat.setProfitAmount(salesAmount.subtract(costAmount));
            stat.setCreateTime(LocalDateTime.now());
            
            statCategoryDailyMapper.insert(stat);
        }
        
        log.info("品类统计汇总完成，共{}条记录", categoryMap.size());
    }

    private void aggregateProductDaily(LocalDate statDate) {
        LocalDateTime startDateTime = statDate.atStartOfDay();
        LocalDateTime endDateTime = statDate.atTime(LocalTime.MAX);
        
        List<OrderItem> items = orderItemMapper.selectList(
                new LambdaQueryWrapper<OrderItem>()
                        .ge(OrderItem::getPayTime, startDateTime)
                        .le(OrderItem::getPayTime, endDateTime)
        );
        
        Map<Long, List<OrderItem>> productMap = items.stream()
                .filter(item -> item.getProductId() != null)
                .collect(Collectors.groupingBy(OrderItem::getProductId));
        
        int count = 0;
        for (Map.Entry<Long, List<OrderItem>> entry : productMap.entrySet()) {
            List<OrderItem> productItems = entry.getValue();
            OrderItem first = productItems.get(0);
            
            Map<String, List<OrderItem>> deviceGroup = productItems.stream()
                    .filter(i -> i.getDeviceId() != null)
                    .collect(Collectors.groupingBy(OrderItem::getDeviceId));
            
            if (deviceGroup.isEmpty()) {
                StatProductDaily stat = createProductStat(statDate, first, productItems, null, null);
                statProductDailyMapper.insert(stat);
                count++;
            } else {
                for (Map.Entry<String, List<OrderItem>> deviceEntry : deviceGroup.entrySet()) {
                    StatProductDaily stat = createProductStat(statDate, first, deviceEntry.getValue(), 
                            deviceEntry.getKey(), null);
                    statProductDailyMapper.insert(stat);
                    count++;
                }
            }
        }
        
        log.info("商品统计汇总完成，共{}条记录", count);
    }

    private StatProductDaily createProductStat(LocalDate statDate, OrderItem first, 
            List<OrderItem> items, String deviceId, Long shopId) {
        StatProductDaily stat = new StatProductDaily();
        stat.setStatDate(statDate);
        stat.setProductId(first.getProductId());
        stat.setProductCode(first.getProductCode());
        stat.setProductName(first.getProductName());
        stat.setCategory(first.getProductType());
        stat.setShopId(shopId != null ? shopId : first.getShopId());
        stat.setDeviceId(deviceId);
        stat.setQuantity(items.stream().mapToInt(i -> i.getQuantity() != null ? i.getQuantity() : 0).sum());
        stat.setOrderCount((int) items.stream().map(OrderItem::getOrderId).distinct().count());
        
        BigDecimal salesAmount = items.stream()
                .map(i -> i.getTotalAmount() != null ? i.getTotalAmount() : BigDecimal.ZERO)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        stat.setSalesAmount(salesAmount);
        
        BigDecimal costAmount = items.stream()
                .map(i -> {
                    if (i.getCostPrice() != null && i.getQuantity() != null) {
                        return i.getCostPrice().multiply(BigDecimal.valueOf(i.getQuantity()));
                    }
                    return BigDecimal.ZERO;
                })
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        stat.setCostAmount(costAmount);
        stat.setProfitAmount(salesAmount.subtract(costAmount));
        stat.setCreateTime(LocalDateTime.now());
        
        return stat;
    }

    private void aggregateDeviceDaily(LocalDate statDate) {
        LocalDateTime startDateTime = statDate.atStartOfDay();
        LocalDateTime endDateTime = statDate.atTime(LocalTime.MAX);
        
        List<Order> orders = orderMapper.selectList(
                new LambdaQueryWrapper<Order>()
                        .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
                        .ge(Order::getPayTime, startDateTime)
                        .le(Order::getPayTime, endDateTime)
        );
        
        Map<String, List<Order>> deviceOrderMap = orders.stream()
                .filter(o -> o.getDeviceId() != null)
                .collect(Collectors.groupingBy(Order::getDeviceId));
        
        List<Device> devices = deviceMapper.selectList(null);
        Map<String, Device> deviceMap = devices.stream()
                .collect(Collectors.toMap(Device::getDeviceId, d -> d, (a, b) -> a));
        
        int count = 0;
        for (Device device : devices) {
            StatDeviceDaily stat = new StatDeviceDaily();
            stat.setStatDate(statDate);
            stat.setDeviceId(device.getDeviceId());
            stat.setDeviceName(device.getName());
            stat.setShopId(device.getShopId());
            
            if (device.getShopId() != null) {
                Shop shop = shopMapper.selectById(device.getShopId());
                if (shop != null) {
                    stat.setShopName(shop.getName());
                }
            }
            
            List<Order> deviceOrders = deviceOrderMap.getOrDefault(device.getDeviceId(), Collections.emptyList());
            stat.setOrderCount(deviceOrders.size());
            stat.setUserCount((int) deviceOrders.stream().map(Order::getUserId).filter(Objects::nonNull).distinct().count());
            
            BigDecimal salesAmount = deviceOrders.stream()
                    .map(o -> o.getTotalAmount() != null ? o.getTotalAmount() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            stat.setSalesAmount(salesAmount);
            stat.setCostAmount(BigDecimal.ZERO);
            stat.setProfitAmount(salesAmount);
            stat.setCreateTime(LocalDateTime.now());
            
            statDeviceDailyMapper.insert(stat);
            count++;
        }
        
        log.info("设备统计汇总完成，共{}条记录", count);
    }

    private void aggregateShopDaily(LocalDate statDate) {
        LocalDateTime startDateTime = statDate.atStartOfDay();
        LocalDateTime endDateTime = statDate.atTime(LocalTime.MAX);
        
        List<Order> orders = orderMapper.selectList(
                new LambdaQueryWrapper<Order>()
                        .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
                        .ge(Order::getPayTime, startDateTime)
                        .le(Order::getPayTime, endDateTime)
        );
        
        Map<Long, List<Order>> shopOrderMap = orders.stream()
                .filter(o -> o.getShopId() != null)
                .collect(Collectors.groupingBy(Order::getShopId));
        
        List<Shop> shops = shopMapper.selectList(null);
        List<Device> devices = deviceMapper.selectList(null);
        
        Map<Long, Long> shopDeviceCount = devices.stream()
                .filter(d -> d.getShopId() != null)
                .collect(Collectors.groupingBy(Device::getShopId, Collectors.counting()));
        
        Set<Long> allUserIds = orders.stream()
                .map(Order::getUserId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        
        Map<Long, LocalDate> userFirstOrderDate = new HashMap<>();
        for (Long userId : allUserIds) {
            List<Order> userOrders = orderMapper.selectList(
                    new LambdaQueryWrapper<Order>()
                            .eq(Order::getUserId, userId)
                            .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
                            .orderByAsc(Order::getPayTime)
                            .last("LIMIT 1")
            );
            if (!userOrders.isEmpty() && userOrders.get(0).getPayTime() != null) {
                userFirstOrderDate.put(userId, userOrders.get(0).getPayTime().toLocalDate());
            }
        }
        
        int count = 0;
        for (Shop shop : shops) {
            StatShopDaily stat = new StatShopDaily();
            stat.setStatDate(statDate);
            stat.setShopId(shop.getId());
            stat.setShopName(shop.getName());
            stat.setProvince(shop.getProvince());
            stat.setCity(shop.getCity());
            stat.setDistrict(shop.getDistrict());
            stat.setDeviceCount(shopDeviceCount.getOrDefault(shop.getId(), 0L).intValue());
            
            List<Order> shopOrders = shopOrderMap.getOrDefault(shop.getId(), Collections.emptyList());
            stat.setOrderCount(shopOrders.size());
            
            Set<Long> shopUserIds = shopOrders.stream()
                    .map(Order::getUserId)
                    .filter(Objects::nonNull)
                    .collect(Collectors.toSet());
            stat.setUserCount(shopUserIds.size());
            
            int newUserCount = 0;
            for (Long userId : shopUserIds) {
                LocalDate firstDate = userFirstOrderDate.get(userId);
                if (firstDate != null && firstDate.equals(statDate)) {
                    newUserCount++;
                }
            }
            stat.setNewUserCount(newUserCount);
            
            BigDecimal salesAmount = shopOrders.stream()
                    .map(o -> o.getTotalAmount() != null ? o.getTotalAmount() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            stat.setSalesAmount(salesAmount);
            stat.setCostAmount(BigDecimal.ZERO);
            stat.setProfitAmount(salesAmount);
            
            BigDecimal profitRate = salesAmount.compareTo(BigDecimal.ZERO) > 0 
                    ? BigDecimal.valueOf(100)
                    : BigDecimal.ZERO;
            stat.setProfitRate(profitRate);
            stat.setCreateTime(LocalDateTime.now());
            
            statShopDailyMapper.insert(stat);
            count++;
        }
        
        log.info("门店统计汇总完成，共{}条记录", count);
    }

    private void aggregateUserRepurchase(LocalDate statDate) {
        LocalDateTime startDateTime = statDate.atStartOfDay();
        LocalDateTime endDateTime = statDate.atTime(LocalTime.MAX);
        
        List<Order> todayOrders = orderMapper.selectList(
                new LambdaQueryWrapper<Order>()
                        .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
                        .ge(Order::getPayTime, startDateTime)
                        .le(Order::getPayTime, endDateTime)
        );
        
        Map<Long, List<Order>> userOrderMap = todayOrders.stream()
                .filter(o -> o.getUserId() != null)
                .collect(Collectors.groupingBy(Order::getUserId));
        
        int count = 0;
        for (Map.Entry<Long, List<Order>> entry : userOrderMap.entrySet()) {
            Long userId = entry.getKey();
            List<Order> todayUserOrders = entry.getValue();
            
            List<Order> allUserOrders = orderMapper.selectList(
                    new LambdaQueryWrapper<Order>()
                            .eq(Order::getUserId, userId)
                            .eq(Order::getPayStatus, OrderConstants.PAY_STATUS_PAID)
                            .orderByAsc(Order::getPayTime)
            );
            
            if (allUserOrders.isEmpty()) continue;
            
            StatUserRepurchase stat = new StatUserRepurchase();
            stat.setStatDate(statDate);
            stat.setUserId(userId);
            stat.setShopId(null);
            stat.setOrderCount(todayUserOrders.size());
            stat.setTotalOrderCount(allUserOrders.size());
            
            Order firstOrder = allUserOrders.get(0);
            Order lastOrder = allUserOrders.get(allUserOrders.size() - 1);
            stat.setFirstOrderDate(firstOrder.getPayTime() != null ? firstOrder.getPayTime().toLocalDate() : null);
            stat.setLastOrderDate(lastOrder.getPayTime() != null ? lastOrder.getPayTime().toLocalDate() : null);
            
            BigDecimal totalAmount = allUserOrders.stream()
                    .map(o -> o.getTotalAmount() != null ? o.getTotalAmount() : BigDecimal.ZERO)
                    .reduce(BigDecimal.ZERO, BigDecimal::add);
            stat.setTotalAmount(totalAmount);
            
            BigDecimal avgOrderAmount = allUserOrders.size() > 0 
                    ? totalAmount.divide(BigDecimal.valueOf(allUserOrders.size()), 2, RoundingMode.HALF_UP)
                    : BigDecimal.ZERO;
            stat.setAvgOrderAmount(avgOrderAmount);
            
            if (allUserOrders.size() >= 2) {
                long totalDays = 0;
                for (int i = 1; i < allUserOrders.size(); i++) {
                    if (allUserOrders.get(i-1).getPayTime() != null && allUserOrders.get(i).getPayTime() != null) {
                        long days = java.time.temporal.ChronoUnit.DAYS.between(
                                allUserOrders.get(i-1).getPayTime().toLocalDate(),
                                allUserOrders.get(i).getPayTime().toLocalDate()
                        );
                        totalDays += days;
                    }
                }
                stat.setRepurchaseDays((int) (totalDays / (allUserOrders.size() - 1)));
            } else {
                stat.setRepurchaseDays(0);
            }
            
            stat.setCreateTime(LocalDateTime.now());
            
            statUserRepurchaseMapper.insert(stat);
            count++;
        }
        
        log.info("用户复购统计汇总完成，共{}条记录", count);
    }
}
