package com.ruoyi.jarvis.service.impl;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.erp.domain.ERPShop;
import com.ruoyi.erp.domain.PublishShop;
import com.ruoyi.erp.request.ERPAccount;
import com.ruoyi.erp.request.ProductCreateRequest;
import com.ruoyi.erp.request.ProductPublishRequest;
import com.ruoyi.jarvis.domain.BatchPublishItem;
import com.ruoyi.jarvis.domain.BatchPublishTask;
import com.ruoyi.jarvis.domain.request.BatchPublishRequest;
import com.ruoyi.jarvis.domain.request.ParseLineReportRequest;
import com.ruoyi.jarvis.mapper.BatchPublishItemMapper;
import com.ruoyi.jarvis.mapper.BatchPublishTaskMapper;
import com.ruoyi.jarvis.service.IBatchPublishService;
import com.ruoyi.jarvis.service.IJDOrderService;
import com.ruoyi.jarvis.service.IOuterIdGeneratorService;
import com.ruoyi.jarvis.util.LineReportParser;
import com.ruoyi.common.utils.http.HttpUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

/**
 * 批量发品Service业务层处理
 * 
 * @author ruoyi
 * @date 2025-01-10
 */
@Service
public class BatchPublishServiceImpl implements IBatchPublishService 
{
    private static final Logger log = LoggerFactory.getLogger(BatchPublishServiceImpl.class);

    @Autowired
    private BatchPublishTaskMapper taskMapper;

    @Autowired
    private BatchPublishItemMapper itemMapper;

    @Autowired
    private IJDOrderService jdOrderService;
    
    @Value("${jarvis.server.jarvis-java.base-url:http://127.0.0.1:6666}")
    private String jarvisJavaBaseUrl;
    
    @Value("${jarvis.server.jarvis-java.jd-api-path:/jd}")
    private String jdApiPath;
    
    private final static String skey = "2192057370ef8140c201079969c956a3";
    
    /**
     * 获取JD接口请求URL
     */
    private String getRequestUrl() {
        return jarvisJavaBaseUrl + jdApiPath + "/";
    }

    @Autowired
    private IOuterIdGeneratorService outerIdGeneratorService;

    // 为了使 @Async 生效，需通过代理调用自身异步方法
    @Autowired
    @Lazy
    private BatchPublishServiceImpl self;

    /**
     * 清理文本中的所有URL链接（保留换行符）
     * 
     * @param text 原始文本
     * @return 清理后的文本
     */
    private String cleanUrlsFromText(String text) {
        if (text == null || text.isEmpty()) {
            return text;
        }
        
        // 匹配所有URL（包括http、https）
        // 匹配到空格、换行或中文字符为止
        String cleaned = text.replaceAll("https?://[^\\s\\u4e00-\\u9fa5]+", "");
        
        // 清理URL删除后可能留下的连续空格（但保留换行符）
        // 只将连续的空格或制表符替换为单个空格，不处理换行符
        cleaned = cleaned.replaceAll("[ \\t]+", " ");
        
        // 清理每行开头和结尾的多余空格（但保留换行符）
        cleaned = cleaned.replaceAll("(?m)^[ \\t]+|[ \\t]+$", "");
        
        return cleaned;
    }

/**
 * 清理敏感营销词（保留换行不动）
 */
private String cleanForbiddenPhrases(String text) {
    if (text == null || text.isEmpty()) {
        return text;
    }
    String cleaned = text;
    // 新增：清理【】符号（包括单独出现或成对出现的情况）
    cleaned = cleaned.replaceAll("【", ""); // 移除左括号【
    cleaned = cleaned.replaceAll("】", ""); // 移除右括号】
    // 新增：清理"咨询客服立减""咨询客服""客服"及变体（含空格）
    // 优先处理长组合，避免被拆分后遗漏
    cleaned = cleaned.replaceAll("咨询\\s*客服\\s*立减", ""); // 匹配"咨询客服立减""咨询 客服 立减"等
    cleaned = cleaned.replaceAll("咨询\\s*客服", ""); // 匹配"咨询客服""咨询 客服"等
    cleaned = cleaned.replaceAll("客\\s*服", ""); // 匹配"客服""客 服"等

    // 一、政策补贴及特殊渠道类（长组合优先）
    cleaned = cleaned.replaceAll("咨询客服领\\s*国补", "");
    cleaned = cleaned.replaceAll("政府\\s*补贴", ""); 
    cleaned = cleaned.replaceAll("购车\\s*补贴", "");
    cleaned = cleaned.replaceAll("家电\\s*下乡", "");
    cleaned = cleaned.replaceAll("内部\\s*渠道", "");
    cleaned = cleaned.replaceAll("特殊\\s*通道", "");
    cleaned = cleaned.replaceAll("免税\\s*配额", "");
    cleaned = cleaned.replaceAll("国补", "");
    cleaned = cleaned.replaceAll("补贴", "");

    // 二、虚假宣传及极限词类
    cleaned = cleaned.replaceAll("全网\\s*最低", "");
    cleaned = cleaned.replaceAll("史无前例", "");
    cleaned = cleaned.replaceAll("100%\\s*有效", "");
    cleaned = cleaned.replaceAll("顶级", "");
    cleaned = cleaned.replaceAll("独家", "");
    cleaned = cleaned.replaceAll("免费\\s*送", "");
    cleaned = cleaned.replaceAll("0元\\s*购", "");
    cleaned = cleaned.replaceAll("买一送一", "");
    cleaned = cleaned.replaceAll("7天瘦20斤", "");
    cleaned = cleaned.replaceAll("治愈率100%", "");

    // 三、品牌侵权及假货暗示类
    cleaned = cleaned.replaceAll("高仿", "");
    cleaned = cleaned.replaceAll("A货", "");
    cleaned = cleaned.replaceAll("1:1\\s*复刻", "");
    cleaned = cleaned.replaceAll("原单", "");
    cleaned = cleaned.replaceAll("尾单", "");
    cleaned = cleaned.replaceAll("工厂\\s*货", "");
    cleaned = cleaned.replaceAll("专柜\\s*验货", "");

    // 四、线下导流及规避监管类（多变体覆盖）
    cleaned = cleaned.replaceAll("微\\s*信", "");
    cleaned = cleaned.replaceAll("薇\\s*信", "");
    cleaned = cleaned.replaceAll("V我", "");
    cleaned = cleaned.replaceAll("加\\s*卫星", "");
    cleaned = cleaned.replaceAll("QQ", "");
    cleaned = cleaned.replaceAll("扣扣", "");
    cleaned = cleaned.replaceAll("手机\\s*号", "");
    cleaned = cleaned.replaceAll("淘宝\\s*链接", "");
    cleaned = cleaned.replaceAll("拼多\\s*多", "");
    cleaned = cleaned.replaceAll("抖音\\s*同款", "");

    // 五、低俗及敏感内容类
    cleaned = cleaned.replaceAll("滚", "");
    cleaned = cleaned.replaceAll("垃圾", "");
    cleaned = cleaned.replaceAll("笨蛋", "");
    cleaned = cleaned.replaceAll("SB", "");
    cleaned = cleaned.replaceAll("原味", "");
    cleaned = cleaned.replaceAll("情趣", "");

    // 六、医疗及金融风险类
    cleaned = cleaned.replaceAll("治疗\\s*三高", "");
    cleaned = cleaned.replaceAll("抗癌", "");
    cleaned = cleaned.replaceAll("贷款", "");
    cleaned = cleaned.replaceAll("套现", "");
    cleaned = cleaned.replaceAll("信用卡", "");

    // 七、特殊商品及违禁类
    cleaned = cleaned.replaceAll("军队", "");
    cleaned = cleaned.replaceAll("军工", "");
    cleaned = cleaned.replaceAll("警用", "");
    cleaned = cleaned.replaceAll("香烟", "");
    cleaned = cleaned.replaceAll("电子烟", "");

    // 清理每行首尾空格（保持换行）
    cleaned = cleaned.replaceAll("(?m)^[ \\t]+|[ \\t]+$", "");
    return cleaned;
}

    /**
     * 调用京东接口生成推广内容
     */
    private String generatePromotionContent(Map<String, String> requestBody) {
        try {
            String url = getRequestUrl() + "generatePromotionContent";
            JSONObject param = new JSONObject();
            param.put("skey", skey);
            param.put("promotionContent", requestBody.get("promotionContent"));
            
            log.info("调用京东接口，URL: {}, 参数: {}", url, param.toJSONString());
            String result = HttpUtils.sendJsonPost(url, param.toJSONString());
            log.info("京东接口响应: {}", result);
            
            // 尝试将 priceInfo.price 提取为顶层 price 字段，并清理文案中的 URL
            try {
                Object parsed = JSON.parse(result);
                if (parsed instanceof com.alibaba.fastjson2.JSONArray) {
                    com.alibaba.fastjson2.JSONArray array = (com.alibaba.fastjson2.JSONArray) parsed;
                    for (int i = 0; i < array.size(); i++) {
                        JSONObject item = array.getJSONObject(i);
                        
                        // 提取 price 字段
                        if (item.containsKey("priceInfo") && item.getJSONObject("priceInfo").containsKey("price")) {
                            item.put("price", item.getJSONObject("priceInfo").get("price"));
                        }
                        
                        // 清理 content 字段中的URL与敏感词
                        if (item.containsKey("content")) {
                            String content = item.getString("content");
                            content = cleanUrlsFromText(content);
                            content = cleanForbiddenPhrases(content);
                            item.put("content", content);
                        }
                        
                        // 【新增】清理 wenan 数组中每个文案的 URL
                        if (item.containsKey("wenan")) {
                            Object wenanObj = item.get("wenan");
                            if (wenanObj instanceof com.alibaba.fastjson2.JSONArray) {
                                com.alibaba.fastjson2.JSONArray wenanArray = (com.alibaba.fastjson2.JSONArray) wenanObj;
                                for (int j = 0; j < wenanArray.size(); j++) {
                                    JSONObject wenanItem = wenanArray.getJSONObject(j);
                                    if (wenanItem != null && wenanItem.containsKey("content")) {
                                        String wenanContent = wenanItem.getString("content");
                                        wenanContent = cleanUrlsFromText(wenanContent);
                                        wenanContent = cleanForbiddenPhrases(wenanContent);
                                        wenanItem.put("content", wenanContent);
                                    }
                                }
                            }
                        }
                    }
                    result = array.toJSONString();
                }
            } catch (Exception e) {
                log.warn("处理推广内容响应失败", e);
            }
            
            return result;
        } catch (Exception e) {
            log.error("调用京东推广内容接口失败", e);
            return "";
        }
    }

    /**
     * 解析线报消息，提取商品列表
     */
    @Override
    public List<Map<String, Object>> parseLineReport(ParseLineReportRequest request) {
        String message = request.getMessage();
        log.info("开始解析线报消息，消息长度: {}", message.length());

        // 提取SKUID和URL
        List<String> urls = LineReportParser.extractJdUrls(message);
        List<String> skuids = LineReportParser.extractSkuids(message);
        // 解析每个URL对应的价格（如有）
        Map<String, Double> urlPriceMap = LineReportParser.extractPriceForUrls(message);
        
        log.info("提取到 {} 个URL, {} 个SKUID", urls.size(), skuids.size());
        log.info("提取的URLs: {}", urls);
        log.info("提取的SKUIDs: {}", skuids);

        // 查询商品详情（保证与输入URL数量一致且顺序一致；失败使用占位）
        List<Map<String, Object>> products = new ArrayList<>();
        Set<String> processedSkuids = new HashSet<>();

        for (int i = 0; i < urls.size(); i++) {
            String url = urls.get(i);
            try {
                log.info("正在处理第 {}/{} 个URL: {}", i + 1, urls.size(), url);
                Map<String, Object> productInfo = queryProductInfoWithRetry(url, 2, 200);

                if (productInfo == null) {
                    log.warn("URL解析失败，使用占位返回: {}", url);
                    Map<String, Object> placeholder = new HashMap<>();
                    placeholder.put("skuid", null);
                    placeholder.put("productName", "未解析：" + url);
                    placeholder.put("price", null);
                    placeholder.put("productImage", null);
                    placeholder.put("shopName", null);
                    placeholder.put("shopId", null);
                    placeholder.put("commission", null);
                    placeholder.put("commissionShare", null);
                    placeholder.put("commissionInfo", null);
                    placeholder.put("materialUrl", url);
                    placeholder.put("images", Collections.emptyList());
                    placeholder.put("wenan", Collections.emptyList());
                    placeholder.put("_raw", Collections.singletonMap("sourceUrl", url));
                    placeholder.put("parseFailed", true);
                    products.add(placeholder);
                    continue;
                }

                // 如果文本中包含该URL的显式价格，则覆盖价格字段
                if (urlPriceMap.containsKey(url)) {
                    productInfo.put("price", urlPriceMap.get(url));
                    productInfo.put("_priceSource", "text");
                }
                // 不对URL阶段做去重，保持与输入一致（允许同款多条）
                products.add(productInfo);
                Object skuObj = productInfo.get("skuid");
                if (skuObj != null) {
                    processedSkuids.add(String.valueOf(skuObj));
                }
            } catch (Exception e) {
                log.error("查询商品信息异常（占位返回），URL: {}", url, e);
                Map<String, Object> placeholder = new HashMap<>();
                placeholder.put("skuid", null);
                placeholder.put("productName", "未解析：" + url);
                placeholder.put("price", null);
                placeholder.put("productImage", null);
                placeholder.put("shopName", null);
                placeholder.put("shopId", null);
                placeholder.put("commission", null);
                placeholder.put("commissionShare", null);
                placeholder.put("commissionInfo", null);
                placeholder.put("materialUrl", url);
                placeholder.put("images", Collections.emptyList());
                placeholder.put("wenan", Collections.emptyList());
                placeholder.put("_raw", Collections.singletonMap("sourceUrl", url));
                placeholder.put("parseFailed", true);
                products.add(placeholder);
            }
        }

        // 额外处理文本中单独出现的SKUID（避免重复）
        for (String skuid : skuids) {
            if (!processedSkuids.contains(skuid)) {
                try {
                    Map<String, Object> productInfo = queryProductInfoWithRetry(skuid, 2, 200);
                    if (productInfo != null) {
                        products.add(productInfo);
                        processedSkuids.add(skuid);
                    } else {
                        Map<String, Object> placeholder = new HashMap<>();
                        placeholder.put("skuid", skuid);
                        placeholder.put("productName", "未解析：" + skuid);
                        placeholder.put("price", null);
                        placeholder.put("productImage", null);
                        placeholder.put("shopName", null);
                        placeholder.put("shopId", null);
                        placeholder.put("commission", null);
                        placeholder.put("commissionShare", null);
                        placeholder.put("commissionInfo", null);
                        placeholder.put("materialUrl", null);
                        placeholder.put("images", Collections.emptyList());
                        placeholder.put("wenan", Collections.emptyList());
                        placeholder.put("_raw", Collections.singletonMap("skuid", skuid));
                        placeholder.put("parseFailed", true);
                        products.add(placeholder);
                    }
                } catch (Exception e) {
                    log.error("查询商品信息失败（占位返回），SKUID: {}", skuid, e);
                    Map<String, Object> placeholder = new HashMap<>();
                    placeholder.put("skuid", skuid);
                    placeholder.put("productName", "未解析：" + skuid);
                    placeholder.put("price", null);
                    placeholder.put("productImage", null);
                    placeholder.put("shopName", null);
                    placeholder.put("shopId", null);
                    placeholder.put("commission", null);
                    placeholder.put("commissionShare", null);
                    placeholder.put("commissionInfo", null);
                    placeholder.put("materialUrl", null);
                    placeholder.put("images", Collections.emptyList());
                    placeholder.put("wenan", Collections.emptyList());
                    placeholder.put("_raw", Collections.singletonMap("skuid", skuid));
                    placeholder.put("parseFailed", true);
                    products.add(placeholder);
                }
            }
        }

        log.info("解析完成，共获取 {} 个商品信息", products.size());
        return products;
    }

    /**
     * 查询商品信息
     */
    private Map<String, Object> queryProductInfo(String urlOrSkuid) {
        try {
            // 调用JDOrder服务的generatePromotionContent接口
            Map<String, String> requestBody = new HashMap<>();
            requestBody.put("promotionContent", urlOrSkuid);
            
            String result = generatePromotionContent(requestBody);
            
            if (StringUtils.isEmpty(result)) {
                return null;
            }

            // 解析返回结果
            Object resultObj = JSON.parse(result);
            if (resultObj instanceof List) {
                List<?> list = (List<?>) resultObj;
                if (!list.isEmpty() && list.get(0) instanceof Map) {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> productMap = (Map<String, Object>) list.get(0);
                    return normalizeProductInfo(productMap);
                }
            } else if (resultObj instanceof Map) {
                @SuppressWarnings("unchecked")
                Map<String, Object> productMap = (Map<String, Object>) resultObj;
                return normalizeProductInfo(productMap);
            }

            return null;
        } catch (Exception e) {
            log.error("查询商品信息异常", e);
            return null;
        }
    }

    /**
     * 带重试的商品信息查询
     * @param urlOrSkuid 传入URL或SKUID
     * @param retries 重试次数
     * @param backoffMillis 两次尝试之间等待毫秒
     */
    private Map<String, Object> queryProductInfoWithRetry(String urlOrSkuid, int retries, long backoffMillis) {
        int attempts = 0;
        while (attempts <= retries) {
            Map<String, Object> result = queryProductInfo(urlOrSkuid);
            if (result != null) {
                return result;
            }
            attempts++;
            if (attempts <= retries) {
                try {
                    Thread.sleep(backoffMillis);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }
        return null;
    }

    /**
     * 规范化商品信息
     */
    private Map<String, Object> normalizeProductInfo(Map<String, Object> productMap) {
        Map<String, Object> result = new HashMap<>();
        
        // 提取基本信息
        result.put("skuid", getValueFromMap(productMap, "spuid", "skuId", "sku"));
        result.put("productName", getValueFromMap(productMap, "skuName", "cleanSkuName", "title", "name"));
        result.put("price", getValueFromMap(productMap, "price", "opPrice", "jdPrice"));
        result.put("productImage", getFirstImage(productMap));
        result.put("shopName", getValueFromMap(productMap, "shopName"));
        result.put("shopId", getValueFromMap(productMap, "shopId"));
        result.put("commission", getValueFromMap(productMap, "commission"));
        result.put("commissionShare", getValueFromMap(productMap, "commissionShare"));
        result.put("commissionInfo", getValueFromMap(productMap, "commissionShare", "commission"));
        result.put("materialUrl", getValueFromMap(productMap, "materialUrl", "url"));
        
        // 【新增】提取图片数组
        Object imagesObj = productMap.get("images");
        if (imagesObj instanceof List) {
            result.put("images", imagesObj);
        } else {
            // 如果没有images数组，则创建只包含主图的列表
            List<String> imageList = new ArrayList<>();
            String mainImage = getFirstImage(productMap);
            if (mainImage != null) {
                imageList.add(mainImage);
            }
            result.put("images", imageList);
        }
        
        // 【新增】提取文案数组
        Object wenanObj = productMap.get("wenan");
        if (wenanObj instanceof List) {
            result.put("wenan", wenanObj);
        } else {
            result.put("wenan", new ArrayList<>());
        }
        
        // 保留原始数据
        result.put("_raw", productMap);
        
        return result;
    }

    /**
     * 从Map中获取第一个非空值
     */
    private Object getValueFromMap(Map<String, Object> map, String... keys) {
        for (String key : keys) {
            Object value = map.get(key);
            if (value != null && StringUtils.isNotEmpty(value.toString())) {
                return value;
            }
        }
        return null;
    }

    /**
     * 获取第一张图片
     */
    private String getFirstImage(Map<String, Object> map) {
        Object images = map.get("images");
        if (images instanceof List) {
            List<?> imageList = (List<?>) images;
            if (!imageList.isEmpty()) {
                return imageList.get(0).toString();
            }
        }
        
        Object mainImage = map.get("mainImage");
        if (mainImage != null) {
            return mainImage.toString();
        }
        
        return null;
    }

    /**
     * 批量发品（支持多账号、多商品）
     */
    @Override
    @Transactional
    public Long batchPublish(BatchPublishRequest request) {
        // 计算总的子账号数
        int totalSubAccounts = request.getAccountConfigs().stream()
            .mapToInt(config -> config.getSubAccounts().size())
            .sum();
        
        log.info("开始批量发品任务，商品数: {}, 主账号数: {}, 总子账号数: {}", 
                 request.getProducts().size(), 
                 request.getAccountConfigs().size(),
                 totalSubAccounts);

        // 获取当前用户
        SysUser currentUser = SecurityUtils.getLoginUser().getUser();

        // 创建任务记录
        BatchPublishTask task = new BatchPublishTask();
        task.setTaskName(request.getTaskName());
        task.setOriginalMessage(request.getOriginalMessage());
        task.setTotalProducts(request.getProducts().size());
        task.setSelectedProducts(request.getProducts().size());
        // 保存账号配置信息
        task.setTargetAccounts(JSON.toJSONString(request.getAccountConfigs()));
        task.setStatus(0); // 待处理
        task.setSuccessCount(0);
        task.setFailCount(0);
        task.setCommonParams(JSON.toJSONString(request.getCommonParams()));
        task.setCreateUserId(currentUser.getUserId());
        task.setCreateUserName(currentUser.getUserName());
        task.setCreateTime(new Date());

        taskMapper.insertBatchPublishTask(task);
        Long taskId = task.getId();

        // 创建明细记录 - 为每个商品×每个主账号×每个子账号创建一条记录
        List<BatchPublishItem> items = new ArrayList<>();
        
        for (BatchPublishRequest.ProductItem product : request.getProducts()) {
            for (BatchPublishRequest.AccountConfig accountConfig : request.getAccountConfigs()) {
                String accountAppid = accountConfig.getTargetAccount();
                String accountRemark = getAccountRemark(accountAppid);
                
                for (String subAccount : accountConfig.getSubAccounts()) {
                    BatchPublishItem item = new BatchPublishItem();
                    item.setTaskId(taskId);
                    item.setSkuid(product.getSkuid());
                    item.setProductName(product.getProductName());
                    item.setTargetAccount(accountAppid);
                    item.setAccountRemark(accountRemark + "-" + subAccount);
                    item.setSubAccount(subAccount); // 设置子账号
                    item.setStatus(0); // 待发布
                    
                    // 【修改】使用发布价格，如果没有则使用京东原价
                    Double priceToSave = product.getPublishPrice() != null ? 
                                        product.getPublishPrice() : 
                                        (product.getPrice() != null ? product.getPrice() : 0.0);
                    item.setPublishPrice(Math.round(priceToSave * 100)); // 转分保存
                    
                    item.setDelaySeconds(request.getDelaySeconds());
                    item.setCreateTime(new Date());
                    items.add(item);
                }
            }
        }

        if (!items.isEmpty()) {
            itemMapper.batchInsertBatchPublishItem(items);
            // 批量插入无法回填ID，重新查询一次，拿到带ID的明细列表
            items = itemMapper.selectBatchPublishItemByTaskId(taskId);
        }

        // 异步执行发品任务（通过代理调用，确保@Async生效）
        self.asyncBatchPublish(taskId, items, request);

        return taskId;
    }

    /**
     * 获取账号备注名
     */
    private String getAccountRemark(String appid) {
        for (ERPAccount account : ERPAccount.values()) {
            if (account.getApiKey().equals(appid)) {
                return account.getRemark();
            }
        }
        return appid;
    }

    /**
     * 异步执行批量发品
     */
    @Async
    public void asyncBatchPublish(Long taskId, List<BatchPublishItem> items, BatchPublishRequest request) {
        log.info("开始异步执行批量发品任务: {}", taskId);

        // 更新任务状态为处理中
        BatchPublishTask task = new BatchPublishTask();
        task.setId(taskId);
        task.setStatus(1); // 处理中
        taskMapper.updateBatchPublishTask(task);

        int successCount = 0;
        int failCount = 0;

        // 逐个发品
        for (BatchPublishItem item : items) {
            try {
                // 更新明细状态为发布中
                item.setStatus(1);
                appendItemLogSafe(item.getId(), "【开始发布】设置状态=发布中");
                itemMapper.updateBatchPublishItem(item);

                // 调用发品接口
                appendItemLogSafe(item.getId(), "调用发品接口...");
                boolean success = publishProduct(item, request);

                if (success) {
                    successCount++;
                    appendItemLogSafe(item.getId(), "发品成功，调度上架(" + item.getDelaySeconds() + "s)");
                    // 发品成功，调度延迟上架
                    schedulePublish(item.getId(), item.getDelaySeconds());
                } else {
                    failCount++;
                    appendItemLogSafe(item.getId(), "发品失败");
                }
            } catch (Exception e) {
                log.error("发品失败，商品: {}, 账号: {}", item.getProductName(), item.getAccountRemark(), e);
                item.setStatus(3); // 发布失败
                item.setErrorMessage("发品异常: " + e.getMessage());
                itemMapper.updateBatchPublishItem(item);
                appendItemLogSafe(item.getId(), "发品异常: " + e.getMessage());
                failCount++;
            }
        }

        // 更新任务状态为已完成
        task.setStatus(2); // 已完成
        task.setSuccessCount(successCount);
        task.setFailCount(failCount);
        task.setCompleteTime(new Date());
        taskMapper.updateBatchPublishTask(task);

        log.info("批量发品任务完成: {}, 成功: {}, 失败: {}", taskId, successCount, failCount);
    }

    /**
     * 发布单个商品
     */
    private boolean publishProduct(BatchPublishItem item, BatchPublishRequest request) {
        log.info("开始发品: 商品={}, 账号={}", item.getProductName(), item.getAccountRemark());

        try {
            // 获取ERP账号
            ERPAccount account = getAccountByAppid(item.getTargetAccount());
            if (account == null) {
                throw new RuntimeException("未找到ERP账号: " + item.getTargetAccount());
            }

            // 获取对应的商品配置（包含自定义价格和文案）；如果不存在，使用回退策略
            BatchPublishRequest.ProductItem productConfig = null;
            if (request != null && request.getProducts() != null) {
                productConfig = request.getProducts().stream()
                    .filter(p -> item.getSkuid().equals(p.getSkuid()))
                    .findFirst()
                    .orElse(null);
            }

            // 获取通用参数
            BatchPublishRequest.CommonParams commonParams = request.getCommonParams();

            // 1. 组装 ERPShop
            ERPShop erpShop = new ERPShop();
            erpShop.setChannelCatid(commonParams.getChannelCatId());
            erpShop.setItemBizType(commonParams.getItemBizType());
            erpShop.setSpBizType(commonParams.getSpBizType());
            
            // 【修改】使用自定义的发布价格
            Double publishPrice = null;
            if (productConfig != null) {
                publishPrice = productConfig.getPublishPrice() != null ? productConfig.getPublishPrice() : productConfig.getPrice();
            }
            if (publishPrice == null) {
                // 回退到明细中的价格（分转元）
                publishPrice = item.getPublishPrice() != null ? item.getPublishPrice() / 100.0 : 0.0;
            }
            erpShop.setPrice(Math.round(publishPrice * 100)); // 价格转分
            
            erpShop.setExpressFee(Math.round(commonParams.getExpressFee() * 100)); // 邮费转分
            erpShop.setStock(commonParams.getStock());
            
            // 自动生成商家编码
            String outerId = outerIdGeneratorService.generateOuterId(item.getSkuid());
            if (StringUtils.isEmpty(outerId)) {
                outerId = "BATCH_" + item.getSkuid() + "_" + System.currentTimeMillis();
            }
            erpShop.setOuterid(outerId);
            erpShop.setStuffStatus(commonParams.getStuffStatus());

            // 2. 发布店铺（必填）
            PublishShop shop = new PublishShop();
            // 使用子账号作为会员名
            shop.setUserName(item.getSubAccount() != null ? item.getSubAccount() : commonParams.getUserName());
            shop.setProvince(commonParams.getProvince());
            shop.setCity(commonParams.getCity());
            shop.setDistrict(commonParams.getDistrict());
            // 标题清理敏感词并限制长度（最多60个字符）
            String title = item.getProductName();
            title = cleanForbiddenPhrases(title);
            shop.setTitle(truncateByCodePoints(title, 60));
            
            // 【修改】使用用户选择的文案
            String content = productConfig != null ? getSelectedWenanContent(productConfig) : null;
            if (StringUtils.isEmpty(content)) {
                // 如果没有选择文案，使用默认描述
                content = "【正品保障】" + item.getProductName() + "\n\n" +
                         "SKUID: " + item.getSkuid() + "\n" +
                         "店铺信息: " + ((productConfig != null && productConfig.getShopName() != null) ? productConfig.getShopName() : "京东商城");
            }
            // 内容清理敏感词
            content = cleanForbiddenPhrases(content);
            shop.setContent(content);
            
            // 【修改】使用商品配置中的图片数组
            List<String> images = productConfig != null ? productConfig.getImages() : null;
            if (images == null || images.isEmpty()) {
                // 如果没有图片，使用主图
                images = new ArrayList<>();
                if (productConfig != null && productConfig.getProductImage() != null) {
                    images.add(productConfig.getProductImage());
                }
            }
            shop.setImages(images);
            
            shop.setWhiteImages(commonParams.getWhiteImages());
            shop.setServiceSupport(commonParams.getServiceSupport());
            
            List<PublishShop> publishShops = new ArrayList<>();
            publishShops.add(shop);
            erpShop.setPublishShop(publishShops);

            // 3. 属性（如果有）
            if (StringUtils.isNotEmpty(commonParams.getChannelPv())) {
                try {
                    Object channelPv = JSON.parse(commonParams.getChannelPv());
                    // TODO: 解析并设置属性
                } catch (Exception e) {
                    log.warn("解析商品属性失败", e);
                }
            }

            // 4. 调用ERP发品接口
            ProductCreateRequest createRequest = new ProductCreateRequest(account);
            JSONObject body = JSONObject.parseObject(JSON.toJSONString(erpShop));
            createRequest.setRequestBody(body);
            String resp = createRequest.getResponseBody();
            
            // 5. 解析响应
            JSONObject responseData = JSONObject.parseObject(resp);
            if (responseData != null && responseData.getInteger("code") == 0) {
                // 发品成功
                JSONObject data = responseData.getJSONObject("data");
                if (data != null) {
                    Long productId = data.getLong("product_id");
                    Integer productStatus = data.getInteger("product_status");
                    
                    item.setStatus(2); // 发布成功
                    item.setProductId(productId);
                    item.setProductStatus(productStatus);
                    item.setOuterId(outerId);
                    item.setErrorMessage(null);
                    itemMapper.updateBatchPublishItem(item);
                    
                    log.info("发品成功: 商品ID={}, 商家编码={}", productId, outerId);
                    return true;
                }
            }
            
            // 发品失败
            String errorMsg = responseData != null ? responseData.getString("msg") : "发品失败";
            throw new RuntimeException(errorMsg);

        } catch (Exception e) {
            log.error("发品失败", e);
            item.setStatus(3); // 发布失败
            item.setErrorMessage(e.getMessage());
            itemMapper.updateBatchPublishItem(item);
            return false;
        }
    }

    /**
     * 根据appid获取ERP账号
     */
    private ERPAccount getAccountByAppid(String appid) {
        for (ERPAccount account : ERPAccount.values()) {
            if (account.getApiKey().equals(appid)) {
                return account;
            }
        }
        return null;
    }

    /**
     * 获取选中的文案内容
     */
    private String getSelectedWenanContent(BatchPublishRequest.ProductItem productConfig) {
        if (productConfig.getWenan() == null || productConfig.getWenan().isEmpty()) {
            return null;
        }
        
        Integer selectedIndex = productConfig.getSelectedWenanIndex();
        if (selectedIndex == null) {
            selectedIndex = 0; // 默认使用第一个
        }
        
        if (selectedIndex < 0 || selectedIndex >= productConfig.getWenan().size()) {
            log.warn("文案索引越界: {}, 文案数量: {}", selectedIndex, productConfig.getWenan().size());
            selectedIndex = 0;
        }
        
        BatchPublishRequest.WenanItem wenanItem = productConfig.getWenan().get(selectedIndex);
        return wenanItem != null ? wenanItem.getContent() : null;
    }

    /**
     * 安全按字符数截断字符串（按Unicode code point，避免截断表情导致乱码）
     */
    private String truncateByCodePoints(String text, int maxChars) {
        if (text == null) {
            return null;
        }
        if (maxChars <= 0) {
            return "";
        }
        final int length = text.codePointCount(0, text.length());
        if (length <= maxChars) {
            return text;
        }
        int endIndex = text.offsetByCodePoints(0, maxChars);
        return text.substring(0, endIndex);
    }


    /**
     * 延迟上架商品
     */
    @Override
    public void schedulePublish(Long itemId, Integer delaySeconds) {
        log.info("调度延迟上架: itemId={}, delay={}秒", itemId, delaySeconds);

        CompletableFuture.runAsync(() -> {
            try {
                // 延迟指定秒数
                TimeUnit.SECONDS.sleep(delaySeconds);

                // 执行上架
                doPublish(itemId);
            } catch (InterruptedException e) {
                log.error("延迟上架被中断", e);
                Thread.currentThread().interrupt();
            }
        });
    }

    /**
     * 执行上架操作
     */
    private void doPublish(Long itemId) {
        log.info("开始上架商品: itemId={}", itemId);

        BatchPublishItem item = itemMapper.selectBatchPublishItemById(itemId);
        if (item == null) {
            log.error("商品明细不存在: {}", itemId);
            return;
        }

        try {
            // 更新状态为上架中
            item.setStatus(4);
            itemMapper.updateBatchPublishItem(item);
            appendItemLogSafe(item.getId(), "开始上架，设置状态=上架中");

            // 检查商品ID是否存在
            if (item.getProductId() == null) {
                throw new RuntimeException("商品ID不存在，无法上架");
            }

            // 获取ERP账号
            ERPAccount account = getAccountByAppid(item.getTargetAccount());
            if (account == null) {
                throw new RuntimeException("未找到ERP账号: " + item.getTargetAccount());
            }

            // 获取任务信息（获取会员名）
            BatchPublishTask task = taskMapper.selectBatchPublishTaskById(item.getTaskId());
            if (task == null) {
                throw new RuntimeException("任务不存在");
            }

            // 解析通用参数
            BatchPublishRequest.CommonParams commonParams = JSON.parseObject(
                task.getCommonParams(), 
                BatchPublishRequest.CommonParams.class
            );

            // 调用ERP上架接口
            ProductPublishRequest publishRequest = new ProductPublishRequest(account);
            publishRequest.setProductId(item.getProductId());
            // 【修复】使用商品对应的子账号，而不是通用参数中的第一个子账号
            publishRequest.setUserName(item.getSubAccount() != null ? item.getSubAccount() : commonParams.getUserName());
            publishRequest.setSpecifyPublishTime(null); // 立即上架
            
            String resp = publishRequest.getResponseBody();
            
            // 解析响应
            JSONObject responseData = JSONObject.parseObject(resp);
            if (responseData != null && responseData.getInteger("code") == 0) {
                // 上架成功
                item.setStatus(5); // 已上架
                item.setPublishTime(new Date());
                item.setErrorMessage(null);
                itemMapper.updateBatchPublishItem(item);
                appendItemLogSafe(item.getId(), "上架成功");
                
                log.info("上架成功: itemId={}, productId={}", itemId, item.getProductId());
            } else {
                // 上架失败
                String errorMsg = responseData != null ? responseData.getString("msg") : "上架失败";
                throw new RuntimeException(errorMsg);
            }

        } catch (Exception e) {
            log.error("上架失败", e);
            item.setStatus(6); // 上架失败
            item.setErrorMessage("上架异常: " + e.getMessage());
            itemMapper.updateBatchPublishItem(item);
            appendItemLogSafe(item.getId(), "上架异常: " + e.getMessage());
        }
    }

    private void appendItemLogSafe(Long itemId, String line) {
        try {
            if (itemId == null) return;
            BatchPublishItem current = itemMapper.selectBatchPublishItemById(itemId);
            if (current == null) return;
            StringBuilder sb = new StringBuilder();
            if (current.getExecLog() != null) {
                sb.append(current.getExecLog());
                if (!current.getExecLog().endsWith("\n")) sb.append('\n');
            }
            sb.append('[').append(new java.text.SimpleDateFormat("HH:mm:ss").format(new java.util.Date())).append("] ").append(line);
            String newLog = sb.toString();
            // 限制日志长度，避免过长（例如保留最近5000字符）
            if (newLog.length() > 5000) {
                newLog = newLog.substring(newLog.length() - 5000);
            }
            current.setExecLog(newLog);
            itemMapper.updateBatchPublishItem(current);
        } catch (Exception ignore) {}
    }

    /**
     * 查询批量发品任务
     */
    @Override
    public BatchPublishTask getTaskById(Long taskId) {
        return taskMapper.selectBatchPublishTaskById(taskId);
    }

    /**
     * 查询批量发品明细列表
     */
    @Override
    public List<BatchPublishItem> getItemsByTaskId(Long taskId) {
        return itemMapper.selectBatchPublishItemByTaskId(taskId);
    }

    /**
     * 查询批量发品任务列表
     */
    @Override
    public List<BatchPublishTask> selectTaskList(BatchPublishTask task) {
        return taskMapper.selectBatchPublishTaskList(task);
    }

    @Override
    public void retryTask(Long taskId) {
        BatchPublishTask task = taskMapper.selectBatchPublishTaskById(taskId);
        if (task == null) {
            throw new RuntimeException("任务不存在: " + taskId);
        }

        List<BatchPublishItem> allItems = itemMapper.selectBatchPublishItemByTaskId(taskId);
        
        // 分类处理：需要重新发布的 和 需要重新上架的
        List<BatchPublishItem> itemsToRepublish = new ArrayList<>();  // 待发布(0)/发布失败(3)
        List<BatchPublishItem> itemsToRelist = new ArrayList<>();     // 发布成功(2)/上架失败(6)
        
        for (BatchPublishItem it : allItems) {
            if (it.getStatus() != null) {
                if (it.getStatus() == 0 || it.getStatus() == 3) {
                    // 待发布或发布失败，需要重新发布
                    itemsToRepublish.add(it);
                } else if (it.getStatus() == 2 || it.getStatus() == 6) {
                    // 发布成功但未上架，或上架失败，需要重新上架
                    itemsToRelist.add(it);
                }
            }
        }

        if (itemsToRepublish.isEmpty() && itemsToRelist.isEmpty()) {
            log.info("任务{} 无需重试，未发现待处理的明细", taskId);
            return;
        }

        // 处理需要重新发布的商品
        if (!itemsToRepublish.isEmpty()) {
            BatchPublishRequest req = new BatchPublishRequest();
            try {
                BatchPublishRequest.CommonParams commonParams = JSON.parseObject(
                    task.getCommonParams(), BatchPublishRequest.CommonParams.class);
                req.setCommonParams(commonParams);
            } catch (Exception e) {
                log.warn("解析任务通用参数失败，将使用默认参数: {}", task.getCommonParams(), e);
            }
            log.info("开始重新发布任务{} 的 {} 条明细", taskId, itemsToRepublish.size());
            self.asyncBatchPublish(taskId, itemsToRepublish, req);
        }

        // 处理需要重新上架的商品（直接上架，不需要重新发布）
        if (!itemsToRelist.isEmpty()) {
            log.info("开始重新上架任务{} 的 {} 条明细", taskId, itemsToRelist.size());
            for (BatchPublishItem item : itemsToRelist) {
                // 重置状态为发布成功，准备上架
                item.setStatus(2);
                item.setErrorMessage(null);
                itemMapper.updateBatchPublishItem(item);
                appendItemLogSafe(item.getId(), "【重试】准备重新上架");
                
                // 立即调度上架（延迟1秒，避免过快）
                schedulePublish(item.getId(), 1);
            }
        }
    }
}

