package com.haha.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.haha.common.constant.CommonConstants;
import com.haha.common.enums.*;
import com.haha.common.exception.BusinessException;
import com.haha.common.vo.StatusLabel;
import com.haha.entity.*;
import com.haha.entity.dto.TimedDiscountCreateDTO;
import com.haha.entity.dto.TimedDiscountQueryDTO;
import com.haha.entity.dto.TimedDiscountRecordQueryDTO;
import com.haha.entity.dto.TimedDiscountUpdateDTO;
import com.haha.mapper.*;
import com.haha.service.TimedDiscountService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

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
@Service
@RequiredArgsConstructor
public class TimedDiscountServiceImpl extends ServiceImpl<TimedDiscountActivityMapper, TimedDiscountActivity> implements TimedDiscountService {

    private final TimedDiscountActivityMapper activityMapper;
    private final TimedDiscountShopMapper discountShopMapper;
    private final TimedDiscountDeviceMapper discountDeviceMapper;
    private final TimedDiscountProductMapper discountProductMapper;
    private final TimedDiscountRecordMapper recordMapper;
    private final PriceAdjustmentLogMapper priceAdjustmentLogMapper;
    private final TimedDiscountStatisticsMapper statisticsMapper;
    private final DeviceInventoryMapper deviceInventoryMapper;
    private final ProductMapper productMapper;
    private final DeviceMapper deviceMapper;
    private final ShopMapper shopMapper;

    @Override
    public IPage<TimedDiscountActivity> getPage(TimedDiscountQueryDTO queryDTO) {
        queryDTO.validate();
        Page<TimedDiscountActivity> page = new Page<>(queryDTO.getPage(), queryDTO.getPageSize());

        LambdaQueryWrapper<TimedDiscountActivity> wrapper = new LambdaQueryWrapper<>();
        wrapper.like(StringUtils.hasText(queryDTO.getActivityName()), TimedDiscountActivity::getActivityName, queryDTO.getActivityName());
        wrapper.eq(queryDTO.getStatus() != null, TimedDiscountActivity::getStatus, queryDTO.getStatus());
        wrapper.eq(queryDTO.getDiscountType() != null, TimedDiscountActivity::getDiscountType, queryDTO.getDiscountType());
        wrapper.eq(TimedDiscountActivity::getDeleted, CommonConstants.NOT_DELETED);
        wrapper.orderByDesc(TimedDiscountActivity::getCreateTime);

        IPage<TimedDiscountActivity> result = this.page(page, wrapper);
        result.getRecords().forEach(this::fillLabels);

        return result;
    }

    @Override
    public TimedDiscountActivity getDetail(Long id) {
        TimedDiscountActivity activity = this.getById(id);
        if (activity == null || activity.getDeleted() == CommonConstants.DELETED) {
            throw new BusinessException(404, "活动不存在");
        }
        fillLabels(activity);
        activity.setShopIds(getActivityShopIds(id));
        activity.setProductIds(getActivityProductIds(id));
        
        if (StringUtils.hasText(activity.getProductTypes())) {
            activity.setProductTypeList(Arrays.asList(activity.getProductTypes().split(",")));
        }
        if (StringUtils.hasText(activity.getPriorityProductTypes())) {
            activity.setPriorityProductTypeList(Arrays.asList(activity.getPriorityProductTypes().split(",")));
        }
        return activity;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TimedDiscountActivity create(TimedDiscountCreateDTO dto, Long creatorId, String creatorName) {
        TimedDiscountActivity activity = new TimedDiscountActivity();
        activity.setActivityName(dto.getActivityName());
        activity.setActivityDesc(dto.getActivityDesc());
        activity.setStartTime(dto.getStartTime());
        activity.setEndTime(dto.getEndTime());
        activity.setDiscountType(dto.getDiscountType());
        activity.setDiscountValue(dto.getDiscountValue());
        activity.setMinDiscountPrice(dto.getMinDiscountPrice());
        activity.setApplyScope(dto.getApplyScope());
        activity.setDeviceScope(dto.getDeviceScope() != null ? dto.getDeviceScope() : 1);
        activity.setProductScope(dto.getProductScope());
        activity.setProductTypes(dto.getProductTypes());
        activity.setPriorityProductTypes(dto.getPriorityProductTypes());
        activity.setMinStock(dto.getMinStock() != null ? dto.getMinStock() : 1);
        activity.setMaxStock(dto.getMaxStock());
        activity.setStatus(NightDiscountStatusEnum.ENABLED.getCode());
        activity.setAutoExecute(dto.getAutoExecute() != null ? dto.getAutoExecute() : 1);
        activity.setNotifyOnExecute(dto.getNotifyOnExecute() != null ? dto.getNotifyOnExecute() : 0);
        activity.setCreatorId(creatorId);
        activity.setCreatorName(creatorName);
        activity.setDeleted(CommonConstants.NOT_DELETED);

        this.save(activity);

        saveActivityShops(activity.getId(), dto.getShopIds(), dto.getApplyScope());
        saveActivityDevices(activity.getId(), dto.getDeviceIds(), dto.getDeviceScope());
        saveActivityProducts(activity.getId(), dto.getProductIds(), dto.getProductScope());

        log.info("创建定时折扣活动成功: id={}, name={}", activity.getId(), activity.getActivityName());
        return activity;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public TimedDiscountActivity update(TimedDiscountUpdateDTO dto) {
        TimedDiscountActivity activity = this.getById(dto.getId());
        if (activity == null || activity.getDeleted() == CommonConstants.DELETED) {
            throw new BusinessException(404, "活动不存在");
        }

        activity.setActivityName(dto.getActivityName());
        activity.setActivityDesc(dto.getActivityDesc());
        activity.setStartTime(dto.getStartTime());
        activity.setEndTime(dto.getEndTime());
        activity.setDiscountType(dto.getDiscountType());
        activity.setDiscountValue(dto.getDiscountValue());
        activity.setMinDiscountPrice(dto.getMinDiscountPrice());
        activity.setApplyScope(dto.getApplyScope());
        activity.setDeviceScope(dto.getDeviceScope());
        activity.setProductScope(dto.getProductScope());
        activity.setProductTypes(dto.getProductTypes());
        activity.setPriorityProductTypes(dto.getPriorityProductTypes());
        activity.setMinStock(dto.getMinStock());
        activity.setMaxStock(dto.getMaxStock());
        activity.setAutoExecute(dto.getAutoExecute());
        activity.setNotifyOnExecute(dto.getNotifyOnExecute());

        this.updateById(activity);

        deleteActivityShops(activity.getId());
        deleteActivityDevices(activity.getId());
        deleteActivityProducts(activity.getId());
        saveActivityShops(activity.getId(), dto.getShopIds(), dto.getApplyScope());
        saveActivityDevices(activity.getId(), dto.getDeviceIds(), dto.getDeviceScope());
        saveActivityProducts(activity.getId(), dto.getProductIds(), dto.getProductScope());

        log.info("更新定时折扣活动成功: id={}", activity.getId());
        return activity;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean updateStatus(Long id, Integer status) {
        TimedDiscountActivity activity = this.getById(id);
        if (activity == null || activity.getDeleted() == CommonConstants.DELETED) {
            throw new BusinessException(404, "活动不存在");
        }

        activity.setStatus(status);
        boolean result = this.updateById(activity);

        if (result) {
            log.info("更新定时折扣活动状态成功: id={}, status={}", id, status);
        }
        return result;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteActivity(Long id) {
        TimedDiscountActivity activity = this.getById(id);
        if (activity == null) {
            throw new BusinessException(404, "活动不存在");
        }

        activity.setDeleted(CommonConstants.DELETED);
        boolean result = this.updateById(activity);

        if (result) {
            deleteActivityShops(id);
            deleteActivityProducts(id);
            log.info("删除定时折扣活动成功: id={}", id);
        }
        return result;
    }

    @Override
    public List<Long> getActivityShopIds(Long activityId) {
        LambdaQueryWrapper<TimedDiscountShop> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountShop::getActivityId, activityId);
        return discountShopMapper.selectList(wrapper).stream()
                .map(TimedDiscountShop::getShopId)
                .collect(Collectors.toList());
    }

    @Override
    public List<String> getActivityDeviceIds(Long activityId) {
        LambdaQueryWrapper<TimedDiscountDevice> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountDevice::getActivityId, activityId);
        return discountDeviceMapper.selectList(wrapper).stream()
                .map(TimedDiscountDevice::getDeviceId)
                .collect(Collectors.toList());
    }

    @Override
    public List<Long> getActivityProductIds(Long activityId) {
        LambdaQueryWrapper<TimedDiscountProduct> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountProduct::getActivityId, activityId);
        return discountProductMapper.selectList(wrapper).stream()
                .map(TimedDiscountProduct::getProductId)
                .collect(Collectors.toList());
    }

    @Override
    public List<TimedDiscountActivity> getEnabledActivities() {
        LambdaQueryWrapper<TimedDiscountActivity> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountActivity::getStatus, NightDiscountStatusEnum.ENABLED.getCode());
        wrapper.eq(TimedDiscountActivity::getDeleted, CommonConstants.NOT_DELETED);
        wrapper.eq(TimedDiscountActivity::getAutoExecute, 1);
        return this.list(wrapper);
    }

    @Override
    public IPage<TimedDiscountRecord> getRecordPage(TimedDiscountRecordQueryDTO queryDTO) {
        queryDTO.validate();
        Page<TimedDiscountRecord> page = new Page<>(queryDTO.getPage(), queryDTO.getPageSize());

        LambdaQueryWrapper<TimedDiscountRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(queryDTO.getActivityId() != null, TimedDiscountRecord::getActivityId, queryDTO.getActivityId());
        wrapper.ge(queryDTO.getExecuteDateStart() != null, TimedDiscountRecord::getExecuteDate, queryDTO.getExecuteDateStart());
        wrapper.le(queryDTO.getExecuteDateEnd() != null, TimedDiscountRecord::getExecuteDate, queryDTO.getExecuteDateEnd());
        wrapper.eq(queryDTO.getShopId() != null, TimedDiscountRecord::getShopId, queryDTO.getShopId());
        wrapper.eq(StringUtils.hasText(queryDTO.getDeviceId()), TimedDiscountRecord::getDeviceId, queryDTO.getDeviceId());
        wrapper.eq(queryDTO.getStatus() != null, TimedDiscountRecord::getStatus, queryDTO.getStatus());
        wrapper.orderByDesc(TimedDiscountRecord::getExecuteTime);

        IPage<TimedDiscountRecord> result = recordMapper.selectPage(page, wrapper);
        result.getRecords().forEach(this::fillRecordLabels);

        return result;
    }

    @Override
    public TimedDiscountRecord getRecordDetail(Long recordId) {
        TimedDiscountRecord record = recordMapper.selectById(recordId);
        if (record != null) {
            fillRecordLabels(record);
        }
        return record;
    }

    @Override
    public IPage<PriceAdjustmentLog> getPriceAdjustmentLogs(Long recordId, int page, int pageSize) {
        Page<PriceAdjustmentLog> pageObj = new Page<>(page, pageSize);
        LambdaQueryWrapper<PriceAdjustmentLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PriceAdjustmentLog::getRecordId, recordId);
        wrapper.orderByAsc(PriceAdjustmentLog::getAdjustTime);

        IPage<PriceAdjustmentLog> result = priceAdjustmentLogMapper.selectPage(pageObj, wrapper);
        result.getRecords().forEach(this::fillPriceLogLabels);

        return result;
    }

    @Override
    public IPage<TimedDiscountStatistics> getStatisticsPage(Long activityId, int page, int pageSize) {
        Page<TimedDiscountStatistics> pageObj = new Page<>(page, pageSize);
        LambdaQueryWrapper<TimedDiscountStatistics> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountStatistics::getActivityId, activityId);
        wrapper.orderByDesc(TimedDiscountStatistics::getStatDate);

        return statisticsMapper.selectPage(pageObj, wrapper);
    }

    @Override
    public Map<String, Object> getActivityStatistics(Long activityId) {
        Map<String, Object> stats = new HashMap<>();
        
        LambdaQueryWrapper<TimedDiscountStatistics> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountStatistics::getActivityId, activityId);
        List<TimedDiscountStatistics> statList = statisticsMapper.selectList(wrapper);

        int totalAdjustedProducts = statList.stream().mapToInt(TimedDiscountStatistics::getTotalAdjustedProducts).sum();
        int totalSoldQuantity = statList.stream().mapToInt(TimedDiscountStatistics::getTotalSoldQuantity).sum();
        BigDecimal totalOriginalValue = statList.stream().map(TimedDiscountStatistics::getOriginalValue).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalActualSales = statList.stream().map(TimedDiscountStatistics::getActualSales).reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal totalSavedAmount = statList.stream().map(TimedDiscountStatistics::getSavedAmount).reduce(BigDecimal.ZERO, BigDecimal::add);

        stats.put("totalAdjustedProducts", totalAdjustedProducts);
        stats.put("totalSoldQuantity", totalSoldQuantity);
        stats.put("totalOriginalValue", totalOriginalValue);
        stats.put("totalActualSales", totalActualSales);
        stats.put("totalSavedAmount", totalSavedAmount);
        stats.put("executionCount", statList.size());

        return stats;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void executeDiscountActivity(Long activityId) {
        TimedDiscountActivity activity = this.getById(activityId);
        if (activity == null || activity.getStatus() != NightDiscountStatusEnum.ENABLED.getCode()) {
            log.warn("活动不存在或未启用: activityId={}", activityId);
            return;
        }

        LocalTime now = LocalTime.now();
        if (now.isBefore(activity.getStartTime())) {
            log.info("未到活动开始时间: activityId={}, startTime={}", activityId, activity.getStartTime());
            return;
        }

        long startTime = System.currentTimeMillis();
        log.info("开始执行定时折扣活动: activityId={}, name={}", activityId, activity.getActivityName());

        try {
            List<Long> shopIds = getTargetShopIds(activity);
            List<String> productTypes = getProductTypes(activity);
            List<String> priorityTypes = getPriorityProductTypes(activity);

            // 批量查询门店，避免 N+1
            Map<Long, Shop> shopMap = new HashMap<>();
            if (!shopIds.isEmpty()) {
                List<Shop> shops = shopMapper.selectBatchIds(shopIds);
                if (shops != null) {
                    shopMap = shops.stream().collect(Collectors.toMap(Shop::getId, s -> s));
                }
            }

            int totalProducts = 0;
            int priorityProducts = 0;
            int totalStock = 0;
            BigDecimal originalValue = BigDecimal.ZERO;
            BigDecimal discountValue = BigDecimal.ZERO;

            for (Long shopId : shopIds) {
                Shop shop = shopMap.get(shopId);
                String shopName = shop != null ? shop.getName() : "";

                List<Device> devices = getDevicesByShop(shopId);
                for (Device device : devices) {
                    List<Map<String, Object>> inventoryList = getEligibleInventory(device.getDeviceId(), activity, productTypes);

                    for (Map<String, Object> inventory : inventoryList) {
                        Long productId = (Long) inventory.get("productId");
                        Integer stock = (Integer) inventory.get("stock");
                        Product product = (Product) inventory.get("product");

                        if (product == null || product.getRetailPrice() == null) {
                            continue;
                        }

                        BigDecimal originalPrice = BigDecimal.valueOf(product.getRetailPrice());
                        BigDecimal discountedPrice = calculateDiscountedPrice(originalPrice, activity);
                        boolean isPriority = isPriorityProduct(product.getType(), priorityTypes);

                        PriceAdjustmentLog logEntry = new PriceAdjustmentLog();
                        logEntry.setActivityId(activityId);
                        logEntry.setShopId(shopId);
                        logEntry.setShopName(shopName);
                        logEntry.setDeviceId(device.getDeviceId());
                        logEntry.setDeviceName(device.getName());
                        logEntry.setProductId(productId);
                        logEntry.setProductName(product.getName());
                        logEntry.setProductType(product.getType());
                        logEntry.setStock(stock);
                        logEntry.setOriginalPrice(originalPrice);
                        logEntry.setDiscountType(activity.getDiscountType());
                        logEntry.setDiscountValue(activity.getDiscountValue());
                        logEntry.setDiscountedPrice(discountedPrice);
                        logEntry.setIsPriority(isPriority ? 1 : 0);
                        logEntry.setAdjustTime(LocalDateTime.now());
                        logEntry.setRestoreStatus(RestoreStatusEnum.NOT_RESTORED.getCode());
                        logEntry.setSaleQuantity(0);

                        priceAdjustmentLogMapper.insert(logEntry);

                        totalProducts++;
                        if (isPriority) {
                            priorityProducts++;
                        }
                        totalStock += stock;
                        originalValue = originalValue.add(originalPrice.multiply(BigDecimal.valueOf(stock)));
                        discountValue = discountValue.add(discountedPrice.multiply(BigDecimal.valueOf(stock)));
                    }
                }
            }

            TimedDiscountRecord record = new TimedDiscountRecord();
            record.setActivityId(activityId);
            record.setActivityName(activity.getActivityName());
            record.setExecuteDate(LocalDate.now());
            record.setExecuteTime(LocalDateTime.now());
            record.setTotalProducts(totalProducts);
            record.setPriorityProducts(priorityProducts);
            record.setTotalStock(totalStock);
            record.setOriginalValue(originalValue);
            record.setDiscountValue(discountValue);
            record.setSavedValue(originalValue.subtract(discountValue));
            record.setStatus(ExecuteStatusEnum.SUCCESS.getCode());
            record.setExecuteDuration((int) (System.currentTimeMillis() - startTime));

            recordMapper.insert(record);

            log.info("定时折扣活动执行完成: activityId={}, totalProducts={}, savedValue={}", 
                    activityId, totalProducts, record.getSavedValue());

        } catch (Exception e) {
            log.error("定时折扣活动执行失败: activityId={}", activityId, e);
            
            TimedDiscountRecord record = new TimedDiscountRecord();
            record.setActivityId(activityId);
            record.setActivityName(activity.getActivityName());
            record.setExecuteDate(LocalDate.now());
            record.setExecuteTime(LocalDateTime.now());
            record.setStatus(ExecuteStatusEnum.FAILED.getCode());
            record.setErrorMessage(e.getMessage());
            record.setExecuteDuration((int) (System.currentTimeMillis() - startTime));
            recordMapper.insert(record);
        }
    }

    @Override
    public void executeAllEnabledActivities() {
        List<TimedDiscountActivity> activities = getEnabledActivities();
        for (TimedDiscountActivity activity : activities) {
            try {
                executeDiscountActivity(activity.getId());
            } catch (Exception e) {
                log.error("执行定时折扣活动异常: activityId={}", activity.getId(), e);
            }
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void restorePrices() {
        log.info("开始恢复商品价格");

        LambdaQueryWrapper<PriceAdjustmentLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PriceAdjustmentLog::getRestoreStatus, RestoreStatusEnum.NOT_RESTORED.getCode());
        wrapper.lt(PriceAdjustmentLog::getAdjustTime, LocalDateTime.now().minusHours(12));

        List<PriceAdjustmentLog> logs = priceAdjustmentLogMapper.selectList(wrapper);
        for (PriceAdjustmentLog logEntry : logs) {
            logEntry.setRestoreStatus(RestoreStatusEnum.RESTORED.getCode());
            logEntry.setRestoreTime(LocalDateTime.now());
            priceAdjustmentLogMapper.updateById(logEntry);
        }

        log.info("商品价格恢复完成: count={}", logs.size());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSalesStatistics() {
        log.info("开始更新销售统计");

        LocalDate today = LocalDate.now();
        LambdaQueryWrapper<TimedDiscountRecord> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountRecord::getExecuteDate, today);
        List<TimedDiscountRecord> records = recordMapper.selectList(wrapper);

        for (TimedDiscountRecord record : records) {
            updateRecordStatistics(record);
        }

        log.info("销售统计更新完成");
    }

    private void updateRecordStatistics(TimedDiscountRecord record) {
        LambdaQueryWrapper<PriceAdjustmentLog> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(PriceAdjustmentLog::getRecordId, record.getId());
        List<PriceAdjustmentLog> logs = priceAdjustmentLogMapper.selectList(wrapper);

        int totalSold = logs.stream().mapToInt(PriceAdjustmentLog::getSaleQuantity).sum();
        BigDecimal totalSales = logs.stream()
                .filter(l -> l.getSaleAmount() != null)
                .map(PriceAdjustmentLog::getSaleAmount)
                .reduce(BigDecimal.ZERO, BigDecimal::add);

        TimedDiscountStatistics stats = new TimedDiscountStatistics();
        stats.setActivityId(record.getActivityId());
        stats.setStatDate(record.getExecuteDate());
        stats.setShopId(record.getShopId());
        stats.setShopName(record.getShopName());
        stats.setTotalAdjustedProducts(record.getTotalProducts());
        stats.setPriorityProducts(record.getPriorityProducts());
        stats.setTotalAdjustedStock(record.getTotalStock());
        stats.setTotalSoldQuantity(totalSold);
        stats.setOriginalValue(record.getOriginalValue());
        stats.setActualSales(totalSales);
        stats.setSavedAmount(record.getSavedValue());

        if (record.getTotalStock() > 0) {
            BigDecimal sellThroughRate = BigDecimal.valueOf(totalSold)
                    .divide(BigDecimal.valueOf(record.getTotalStock()), 4, RoundingMode.HALF_UP)
                    .multiply(BigDecimal.valueOf(100));
            stats.setSellThroughRate(sellThroughRate);
        }

        LambdaQueryWrapper<TimedDiscountStatistics> existWrapper = new LambdaQueryWrapper<>();
        existWrapper.eq(TimedDiscountStatistics::getActivityId, record.getActivityId());
        existWrapper.eq(TimedDiscountStatistics::getStatDate, record.getExecuteDate());
        existWrapper.eq(TimedDiscountStatistics::getShopId, record.getShopId());
        TimedDiscountStatistics exist = statisticsMapper.selectOne(existWrapper);

        if (exist != null) {
            stats.setId(exist.getId());
            statisticsMapper.updateById(stats);
        } else {
            statisticsMapper.insert(stats);
        }
    }

    private List<Long> getTargetShopIds(TimedDiscountActivity activity) {
        if (activity.getApplyScope() == ApplyScopeEnum.ALL_SHOP.getCode()) {
            LambdaQueryWrapper<Shop> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(Shop::getStatus, CommonStatus.ENABLED.getCode());
            return shopMapper.selectList(wrapper).stream().map(Shop::getId).collect(Collectors.toList());
        }
        return getActivityShopIds(activity.getId());
    }

    private List<String> getProductTypes(TimedDiscountActivity activity) {
        if (activity.getProductScope() == ApplyScopeEnum.ALL_SHOP.getCode()) {
            return null;
        }
        if (StringUtils.hasText(activity.getProductTypes())) {
            return Arrays.asList(activity.getProductTypes().split(","));
        }
        return Collections.emptyList();
    }

    private List<String> getPriorityProductTypes(TimedDiscountActivity activity) {
        if (StringUtils.hasText(activity.getPriorityProductTypes())) {
            return Arrays.asList(activity.getPriorityProductTypes().split(","));
        }
        return Collections.emptyList();
    }

    private List<Device> getDevicesByShop(Long shopId) {
        LambdaQueryWrapper<Device> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(Device::getShopId, shopId);
        return deviceMapper.selectList(wrapper);
    }

    private List<Map<String, Object>> getEligibleInventory(String deviceId, TimedDiscountActivity activity, List<String> productTypes) {
        LambdaQueryWrapper<DeviceInventory> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(DeviceInventory::getDeviceId, deviceId);
        wrapper.ge(DeviceInventory::getStock, activity.getMinStock());
        if (activity.getMaxStock() != null) {
            wrapper.le(DeviceInventory::getStock, activity.getMaxStock());
        }

        List<DeviceInventory> inventoryList = deviceInventoryMapper.selectList(wrapper);
        if (inventoryList.isEmpty()) {
            return new ArrayList<>();
        }

        // 批量查询所有关联商品，避免 N+1
        List<Long> productIds = inventoryList.stream()
                .map(DeviceInventory::getProductId).distinct().collect(Collectors.toList());
        Map<Long, Product> productMap = new HashMap<>();
        List<Product> products = productMapper.selectBatchIds(productIds);
        if (products != null) {
            productMap = products.stream().collect(Collectors.toMap(Product::getId, p -> p));
        }

        List<Map<String, Object>> result = new ArrayList<>();
        for (DeviceInventory inventory : inventoryList) {
            Product product = productMap.get(inventory.getProductId());
            if (product == null) continue;
            if (productTypes != null && !productTypes.isEmpty()
                    && !productTypes.contains(product.getType())) {
                continue;
            }
            Map<String, Object> map = new HashMap<>();
            map.put("productId", inventory.getProductId());
            map.put("stock", inventory.getStock());
            map.put("product", product); // 顺便带出 product 供上层使用
            result.add(map);
        }
        return result;
    }

    private BigDecimal calculateDiscountedPrice(BigDecimal originalPrice, TimedDiscountActivity activity) {
        BigDecimal discountedPrice;

        if (activity.getDiscountType() == DiscountTypeEnum.FIXED_DISCOUNT.getCode()) {
            discountedPrice = originalPrice.multiply(activity.getDiscountValue());
        } else if (activity.getDiscountType() == DiscountTypeEnum.FIXED_PRICE.getCode()) {
            discountedPrice = activity.getDiscountValue();
        } else {
            discountedPrice = originalPrice.multiply(activity.getDiscountValue());
        }

        discountedPrice = discountedPrice.setScale(2, RoundingMode.HALF_UP);

        if (activity.getMinDiscountPrice() != null) {
            if (discountedPrice.compareTo(activity.getMinDiscountPrice()) < 0) {
                discountedPrice = activity.getMinDiscountPrice();
            }
        }

        return discountedPrice;
    }

    private boolean isPriorityProduct(String productType, List<String> priorityTypes) {
        if (priorityTypes == null || priorityTypes.isEmpty()) {
            return false;
        }
        return priorityTypes.contains(productType);
    }

    private void saveActivityShops(Long activityId, List<Long> shopIds, Integer applyScope) {
        if (applyScope != null && applyScope == ApplyScopeEnum.SPECIFIED_SHOP.getCode()
                && shopIds != null && !shopIds.isEmpty()) {
            for (Long shopId : shopIds) {
                TimedDiscountShop discountShop = new TimedDiscountShop();
                discountShop.setActivityId(activityId);
                discountShop.setShopId(shopId);
                discountShopMapper.insert(discountShop);
            }
        }
    }

    private void saveActivityDevices(Long activityId, List<String> deviceIds, Integer deviceScope) {
        if (deviceScope != null && deviceScope == ApplyScopeEnum.SPECIFIED_SHOP.getCode()
                && deviceIds != null && !deviceIds.isEmpty()) {
            for (String deviceId : deviceIds) {
                TimedDiscountDevice discountDevice = new TimedDiscountDevice();
                discountDevice.setActivityId(activityId);
                discountDevice.setDeviceId(deviceId);
                discountDeviceMapper.insert(discountDevice);
            }
        }
    }

    private void saveActivityProducts(Long activityId, List<Long> productIds, Integer productScope) {
        if (productScope != null && productScope == 3
                && productIds != null && !productIds.isEmpty()) {
            for (Long productId : productIds) {
                TimedDiscountProduct discountProduct = new TimedDiscountProduct();
                discountProduct.setActivityId(activityId);
                discountProduct.setProductId(productId);
                discountProductMapper.insert(discountProduct);
            }
        }
    }

    private void deleteActivityShops(Long activityId) {
        LambdaQueryWrapper<TimedDiscountShop> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountShop::getActivityId, activityId);
        discountShopMapper.delete(wrapper);
    }

    private void deleteActivityDevices(Long activityId) {
        LambdaQueryWrapper<TimedDiscountDevice> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountDevice::getActivityId, activityId);
        discountDeviceMapper.delete(wrapper);
    }

    private void deleteActivityProducts(Long activityId) {
        LambdaQueryWrapper<TimedDiscountProduct> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(TimedDiscountProduct::getActivityId, activityId);
        discountProductMapper.delete(wrapper);
    }

    private void fillLabels(TimedDiscountActivity activity) {
        StatusLabel statusLabel = NightDiscountStatusEnum.getLabelByCode(activity.getStatus());
        activity.setStatusLabel(statusLabel.getLabel());
        activity.setStatusColor(statusLabel.getColor());

        StatusLabel discountTypeLabel = DiscountTypeEnum.getLabelByCode(activity.getDiscountType());
        activity.setDiscountTypeLabel(discountTypeLabel.getLabel());

        StatusLabel applyScopeLabel = ApplyScopeEnum.getLabelByCode(activity.getApplyScope());
        activity.setApplyScopeLabel(applyScopeLabel.getLabel());

        if (activity.getProductScope() != null) {
            if (activity.getProductScope() == ApplyScopeEnum.ALL_SHOP.getCode()) {
                activity.setProductScopeLabel("全部商品");
            } else if (activity.getProductScope() == ApplyScopeEnum.SPECIFIED_SHOP.getCode()) {
                activity.setProductScopeLabel("指定分类");
            } else {
                activity.setProductScopeLabel("指定商品");
            }
        }
    }

    private void fillRecordLabels(TimedDiscountRecord record) {
        StatusLabel statusLabel = ExecuteStatusEnum.getLabelByCode(record.getStatus());
        record.setStatusLabel(statusLabel.getLabel());
        record.setStatusColor(statusLabel.getColor());
    }

    private void fillPriceLogLabels(PriceAdjustmentLog log) {
        StatusLabel restoreStatusLabel = RestoreStatusEnum.getLabelByCode(log.getRestoreStatus());
        log.setRestoreStatusLabel(restoreStatusLabel.getLabel());
        log.setRestoreStatusColor(restoreStatusLabel.getColor());

        StatusLabel discountTypeLabel = DiscountTypeEnum.getLabelByCode(log.getDiscountType());
        log.setDiscountTypeLabel(discountTypeLabel.getLabel());
    }
}
