package org.xyzh.crontab.task.newsTask;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.xyzh.api.crontab.CrontabService;
import org.xyzh.api.crontab.DataCollectionItemService;
import org.xyzh.api.crontab.EmailDefaultService;
import org.xyzh.api.crontab.EmailRecipientService;
import org.xyzh.api.crontab.TaskMetaService;
import org.xyzh.api.news.resource.ResourceAuditService;
import org.xyzh.api.news.resource.ResourceService;
import org.xyzh.api.system.role.RoleService;
import org.xyzh.common.core.domain.ResultDomain;
import org.xyzh.common.dto.crontab.TbCrontabEmailDefault;
import org.xyzh.common.dto.crontab.TbCrontabEmailRecipient;
import org.xyzh.common.dto.crontab.TbCrontabTask;
import org.xyzh.common.dto.crontab.TbCrontabTaskMeta;
import org.xyzh.common.dto.crontab.TbDataCollectionItem;
import org.xyzh.common.dto.resource.TbResource;
import org.xyzh.common.utils.EmailUtils;
import org.xyzh.common.utils.IDUtils;
import org.xyzh.common.utils.NonUtils;
import org.xyzh.common.vo.DataCollectionItemVO;
import org.xyzh.common.vo.ResourceVO;
import org.xyzh.common.vo.UserDeptRoleVO;
import org.xyzh.crontab.pojo.TaskParams;
import org.xyzh.crontab.task.PythonCommandTask;

import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @description 新闻爬虫定时任务
 * @filename NewsCrawlerTask.java
 * @author yslg
 * @copyright xyzh
 * @since 2025-11-08
 */
@Component("newsCrewerTask")
public class NewsCrawlerTask extends PythonCommandTask {

    @Autowired
    private DataCollectionItemService itemService;
    
    @Autowired
    private TaskMetaService taskMetaService;
    
    @Autowired
    private CrontabService crontabService;
    
    @Autowired
    private EmailDefaultService emailDefaultService;
    
    @Autowired
    private EmailRecipientService emailRecipientService;

    @Autowired
    private ResourceAuditService auditService;
    
    @Autowired
    private EmailUtils emailUtils;

    @Autowired
    private ResourceService resourceService;

    @Autowired
    private RoleService roleService;

    /**
     * 构建Python脚本参数
     */
    @Override
    protected List<String> buildPythonArgs(TaskParams taskParams) throws Exception {
        List<String> args = new ArrayList<>();

        // 1. 从数据库读取scriptPath（通过metaId）
        String metaId = taskParams.getParamAsString("metaId");
        if (metaId == null || metaId.isEmpty()) {
            throw new Exception("metaId参数缺失，无法获取scriptPath");
        }
        
        ResultDomain<TbCrontabTaskMeta> metaResult = taskMetaService.getTaskMetaById(metaId);
        if (!metaResult.isSuccess() || metaResult.getData() == null) {
            throw new Exception("未找到任务元数据: metaId=" + metaId);
        }
        
        String scriptPath = metaResult.getData().getScriptPath();
        if (scriptPath == null || scriptPath.isEmpty()) {
            throw new Exception("任务元数据中scriptPath为空: metaId=" + metaId);
        }
        
        logger.info("从数据库读取scriptPath: {}, metaId: {}", scriptPath, metaId);

        // 2. 生成输出文件名
        String timestamp = String.valueOf(System.currentTimeMillis());
        String outputFile = String.format("output/news_%s.json", timestamp);

        // 保存输出文件路径到params中，供handleResult使用
        taskParams.setParam("_outputFile", outputFile);



        // 4. 构建命令参数
        args.add(scriptPath);           // 动态脚本路径

        // 5. 遍历params，动态构建命令行参数
        if (taskParams.getParams() != null) {
            for (Map.Entry<String, Object> entry : taskParams.getParams().entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();

                // 跳过特殊参数
                if (key.startsWith("_") || key.equals("metaId") ||
                    key.equals("taskId") || key.equals("logId")) {
                    continue;
                }

                // 获取对应的Python参数名
                String pythonArg = "--"+key;
                if (pythonArg != null && value != null) {
                    if (value instanceof Boolean) {
                        // Boolean类型: true时只传参数名，false时不传
                        if ((Boolean) value) {
                            args.add(pythonArg);
                        }
                    } else {
                        // String/Integer类型: 传参数名+值
                        args.add(pythonArg);
                        args.add(value.toString());
                    }
                }
            }
        }

        // 6. 统一添加output参数
        args.add("--output");
        args.add(outputFile);

        logger.info("Python脚本: {}, 命令行参数: {}", scriptPath, String.join(" ", args.subList(1, args.size())));

        return args;
    }

    /**
     * 处理执行结果
     */
    @Override
    protected void handleResult(TaskParams taskParams, String output) throws Exception {
        String outputFile = taskParams.getParamAsString("_outputFile");
        Path outputPath = Paths.get(getScriptBasePath()).resolve(outputFile);

        if (!Files.exists(outputPath)) {
            logger.warn("输出文件不存在: {}", outputFile);
            return;
        }

        // 读取并解析结果文件
        String jsonContent = Files.readString(outputPath);
        ResultDomain<ArticleStruct> result = JSON.parseObject(jsonContent, new TypeReference<ResultDomain<ArticleStruct>>(){});
        if (!result.isSuccess()) {
            logger.error("爬取新闻失败: {}", result.getMessage());
            return;
        }
        List<ArticleStruct> newsList = result.getDataList();
        logger.info("成功爬取 {} 条新闻", newsList.size());

        // 获取taskId和logId
        String taskId = taskParams.getParamAsString("taskId");
        String logId = taskParams.getParamAsString("logId");

        // 保存新闻数据到数据库
        if (taskId != null && !taskId.isEmpty() && logId != null && !logId.isEmpty()) {
            ResultDomain<TbCrontabTask> taskResult = crontabService.getTaskById(taskId);

            saveNewsToDatabase(newsList, taskResult.getData(), logId);

        } else {
            logger.warn("未提供任务ID或日志ID，跳过数据保存");
        }
    }

    /**
     * 将新闻数据保存到数据库
     */
    private void saveNewsToDatabase(List<ArticleStruct> newsList, TbCrontabTask task, String logId) {
        String taskId = task.getTaskId();
        logger.info("开始保存 {} 条新闻到数据库，任务ID: {}，日志ID: {}", newsList.size(), taskId, logId);

        try {
            ResultDomain<TbCrontabTaskMeta> metaResult = taskMetaService.getTaskMetaByTaskId(taskId);
            if (!metaResult.isSuccess() || metaResult.getData() == null) {
                throw new Exception("未找到任务元数据: taskId=" + taskId);
            }
            TbCrontabTaskMeta taskMeta = metaResult.getData();

            Date now = new Date();
            SimpleDateFormat parser = new SimpleDateFormat("yyyy年MM月dd日HH:mm");
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            List<TbDataCollectionItem> itemList = new ArrayList<>();
            List<TbDataCollectionItem> notPassList = new ArrayList<>();
            List<TbDataCollectionItem> passList = new ArrayList<>();
            for (ArticleStruct news : newsList) {
                try {
                    TbDataCollectionItem item = new TbDataCollectionItem();

                    // 基本信息
                    item.setID(IDUtils.generateID());
                    item.setTaskId(taskId);
                    item.setLogId(logId);
                    item.setTitle(news.getTitle());
                    item.setExecuteStatus(news.getExecuteStatus());
                    item.setExecuteMessage(news.getExecuteMessage());
                    // 拼接HTML内容
                    if (news.getContentRows() != null && !news.getContentRows().isEmpty()) {
                        StringBuilder html = new StringBuilder();
                        for (ArticleStruct.RowStruct row : news.getContentRows()) {
                            if (row != null && row.getContent() != null) {
                                html.append(row.getContent());
                            }
                        }
                        item.setContent(html.toString());
                    }

                    item.setSource(news.getSource());
                    item.setSourceUrl(news.getUrl());
                    item.setAuthor(news.getAuthor());

                    // 发布时间，兼容多种格式
                    String publishTimeStr = news.getPublishTime();
                    if (publishTimeStr != null && !publishTimeStr.isEmpty()) {
                        try {
                            Date parsed;
                            if (publishTimeStr.contains("年")) {
                                // 原始格式：yyyy年MM月dd日HH:mm
                                parsed = parser.parse(publishTimeStr);
                            } else if (publishTimeStr.length() == 10 && publishTimeStr.charAt(4) == '-' && publishTimeStr.charAt(7) == '-') {
                                // 仅日期：yyyy-MM-dd
                                parsed = new SimpleDateFormat("yyyy-MM-dd").parse(publishTimeStr);
                            } else {
                                // 其它情况按原parser尝试
                                parsed = parser.parse(publishTimeStr);
                            }
                            item.setPublishTime(dateFormat.parse(dateFormat.format(parsed)));
                        } catch (Exception e) {
                            logger.warn("解析发布时间失败: {}", publishTimeStr);
                            item.setPublishTime(now);
                        }
                    } else {
                        item.setPublishTime(now);
                    }

                    // 状态和时间
                    item.setStatus(0); // 未处理
                    item.setCrawlTime(now);
                    ResultDomain<Boolean> pass = auditService.auditText(item.getContent());
                    if(pass.isSuccess() && pass.getData()){
                        item.setIsAudited(true);
                        passList.add(item);
                    }else{
                        item.setIsAudited(false);
                        notPassList.add(item);
                    }
                    itemList.add(item);
                } catch (Exception e) {
                    logger.error("转换新闻数据失败: ", e);
                }
            }

            // 批量保存
            Set<String> insertedUrls = new HashSet<>();
            ResultDomain<TbDataCollectionItem> passDataResult = new ResultDomain<>();
            if (!passList.isEmpty()) {
                passDataResult = itemService.batchCreateItems(passList);
                if (passDataResult.isSuccess()) {
                    logger.info("成功保存 {} 条新闻到数据库", passList.size());
                    insertedUrls.addAll(passDataResult.getDataList().stream().map(TbDataCollectionItem::getSourceUrl).toList());
                } else {
                    logger.error("保存新闻到数据库失败: {}", passDataResult.getMessage());
                }
            } else {
                logger.warn("没有有效的新闻数据需要保存");
            }
            ResultDomain<TbDataCollectionItem> notPassDataResult = new ResultDomain<>();
            if (!notPassList.isEmpty()) {
                notPassDataResult = itemService.batchCreateItems(notPassList);
                if (notPassDataResult.isSuccess()) {
                    logger.info("成功保存 {} 条新闻到数据库", notPassList.size());
                    insertedUrls.addAll(notPassDataResult.getDataList().stream().map(TbDataCollectionItem::getSourceUrl).toList());
                } else {
                    logger.error("保存新闻到数据库失败: {}", notPassDataResult.getMessage());
                }
            } else {
                logger.warn("没有有效的新闻数据需要保存");
            }

            // 自动发布并记录成功发布的 URL 集合
            Set<String> publishedUrls = new HashSet<>();
            if (taskMeta.getAutoPublish().booleanValue()){
                publishedUrls = publishNewsToArticle(passDataResult.getDataList(), task, logId);
            }

            Set<String> notPathUrls = new HashSet<>(notPassList.stream().map(TbDataCollectionItem::getSourceUrl).toList());
            // 发送邮件通知，包含自动发布与新增信息
            sendEmailNotification(task.getTaskId(), task, newsList, insertedUrls, publishedUrls, notPathUrls);

        } catch (Exception e) {
            logger.error("保存新闻数据到数据库异常: ", e);
        }
    }

    /**
     * 发送邮件通知
     */
    private void sendEmailNotification(String taskId, TbCrontabTask task, List<ArticleStruct> newsList,
                                       Set<String> insertedUrls,
                                       Set<String> publishedUrls,
                                       Set<String> notPassUrls
                                       ) {
        try {
            List<String> recipients = new ArrayList<>();
            
            // 2. 如果使用默认接收人，查询默认接收人列表
            if (Boolean.TRUE.equals(task.getDefaultRecipient()) && task.getMetaId() != null) {
                ResultDomain<TbCrontabEmailDefault> defaultResult = emailDefaultService.getDefaultByMetaId(task.getMetaId());
                if (defaultResult.isSuccess() && defaultResult.getDataList() != null) {
                    for (TbCrontabEmailDefault defaultRecipient : defaultResult.getDataList()) {
                        if (defaultRecipient.getUserEmail() != null && !defaultRecipient.getUserEmail().isEmpty()) {
                            recipients.add(defaultRecipient.getUserEmail());
                        }
                    }
                }
            }
            
            // 3. 查询额外接收人
            ResultDomain<TbCrontabEmailRecipient> recipientResult = emailRecipientService.getRecipientsByTaskId(taskId);
            if (recipientResult.isSuccess() && recipientResult.getDataList() != null) {
                for (TbCrontabEmailRecipient recipient : recipientResult.getDataList()) {
                    if (recipient.getEmail() != null && !recipient.getEmail().isEmpty()) {
                        recipients.add(recipient.getEmail());
                    }
                }
            }
            
            // 4. 去重 
            recipients = recipients.stream().distinct().collect(Collectors.toList());
            
            if (recipients.isEmpty()) {
                logger.info("任务 {} 没有配置接收人，跳过邮件发送", task.getTaskName());
                return;
            }
            
            // 5. 构建邮件内容
            String subject = "【新闻爬虫通知】" + task.getTaskName() + " 执行完成";
            String content = buildEmailContent(task.getTaskName(), newsList, insertedUrls, publishedUrls, notPassUrls);
            
            // 6. 发送邮件
            int successCount = 0;
            for (String email : recipients) {
                if (emailUtils.sendHtmlEmail(email, subject, content)) {
                    successCount++;
                }
            }
            
            logger.info("邮件发送完成，成功发送: {}/{}，任务: {}", successCount, recipients.size(), task.getTaskName());
            
        } catch (Exception e) {
            logger.error("发送邮件通知异常，但不影响任务执行: ", e);
        }
    }
    
    /**
     * 构建邮件HTML内容
     */
    private String buildEmailContent(String taskName, List<ArticleStruct> newsList,
                                     Set<String> insertedUrls,
                                     Set<String> publishedUrls,
                                     Set<String> notPathUrls) {
        StringBuilder html = new StringBuilder();
        html.append("<!DOCTYPE html>")
            .append("<html>")
            .append("<head>")
            .append("<meta charset='UTF-8'>")
            .append("<style>")
            .append("body { font-family: 'Microsoft YaHei', Arial, sans-serif; background-color: #f5f5f5; margin: 0; padding: 20px; }")
            .append(".container { max-width: 800px; margin: 0 auto; background-color: #ffffff; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); overflow: hidden; }")
            .append(".header { background: linear-gradient(135deg, #C62828 0%, #E53935 100%); padding: 30px; text-align: center; color: #ffffff; }")
            .append(".header h1 { margin: 0; font-size: 24px; }")
            .append(".content { padding: 30px; }")
            .append(".summary { background-color: #f8f9fa; border-left: 4px solid #C62828; padding: 15px; margin: 20px 0; }")
            .append(".news-list { margin-top: 20px; }")
            .append(".news-item { border-bottom: 1px solid #e0e0e0; padding: 15px 0; }")
            .append(".news-item:last-child { border-bottom: none; }")
            .append(".news-title { font-size: 16px; font-weight: bold; color: #333; margin-bottom: 8px; }")
            .append(".news-meta { font-size: 14px; color: #666; }")
            .append(".news-link { color: #C62828; text-decoration: none; }")
            .append(".footer { background-color: #f8f9fa; padding: 20px; text-align: center; color: #999; font-size: 12px; }")
            .append("</style>")
            .append("</head>")
            .append("<body>")
            .append("<div class='container'>")
            .append("<div class='header'>")
            .append("<h1>新闻爬虫执行通知</h1>")
            .append("</div>")
            .append("<div class='content'>");
        
        // 摘要信息
        int insertedCount = (insertedUrls != null) ? insertedUrls.size() : 0;
        int totalCount = newsList.size();
        html.append("<div class='summary'>")
            .append("<p><strong>任务名称：</strong>").append(taskName).append("</p>")
            .append("<p><strong>执行时间：</strong>").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())).append("</p>")
            .append("<p><strong>爬取数量：</strong>").append(totalCount).append(" 条</p>")
            .append("<p><strong>入库情况：</strong>")
            .append("本次新增 ").append(insertedCount).append(" 条，已有历史记录的新闻不会重复入库。</p>")
            .append("</div>");

        // 新闻列表（包含自动发布标记）
        html.append("<div class='news-list'>")
            .append("<h3>爬取内容：</h3>");
        
        int count = newsList.size(); // 最多显示10条
        for (int i = 0; i < count; i++) {
            ArticleStruct news = newsList.get(i);
            html.append("<div class='news-item'>")
                .append("<div class='news-title'>").append(news.getTitle() != null ? news.getTitle() : "无标题").append("</div>")
                .append("<div class='news-meta'>")
                .append("来源：").append(news.getSource() != null ? news.getSource() : "未知")
                .append(" | ")
                .append("发布时间：").append(news.getPublishTime() != null ? news.getPublishTime() : "未知");

            if (news.getUrl() != null && !news.getUrl().isEmpty()) {
                html.append(" | <a href='").append(news.getUrl()).append("' class='news-link' target='_blank'>查看原文</a>");
            }


            // 入库标记（新增 / 历史已存在）
            if (news.getUrl() != null && !news.getUrl().isEmpty() && insertedUrls != null) {
                if (insertedUrls.contains(news.getUrl())) {
                    html.append(" | <span style='color:#2e7d32;font-weight:bold;'>【本次新增】</span>");
                } else {
                    html.append(" | <span style='color:#666;'>【历史已存在，未重复入库】</span>");
                }
            }

            // 如果该未审核通过，追加标记
            if (notPathUrls != null && !notPathUrls.isEmpty()
                    && news.getUrl() != null && notPathUrls.contains(news.getUrl())) {
                html.append(" | <span style='color:#ff0000;font-weight:bold;'>【未通过审核】</span>");
            }

            // 如果该新闻已自动发布，追加标记
            if (publishedUrls != null && !publishedUrls.isEmpty()
                    && news.getUrl() != null && publishedUrls.contains(news.getUrl())) {
                html.append(" | <span style='color:#C62828;font-weight:bold;'>【已自动发布】</span>");
            }

            html.append("</div>")
                .append("</div>");
        }
        
        html.append("</div>"); // news-list
        html.append("</div>"); // content
        
        // 页脚
        html.append("<div class='footer'>")
            .append("<p>此邮件由系统自动发送，请勿回复</p>")
            .append("<p>Copyright © 红色思政智能体平台</p>")
            .append("</div>")
            .append("</div>") // container
            .append("</body>")
            .append("</html>");
        
        return html.toString();
    }

    // 自动发布功能，把采集的数据发布到文章资源表
    private Set<String> publishNewsToArticle(List<TbDataCollectionItem> itemList, TbCrontabTask task, String logId) {
        if (itemList == null || itemList.isEmpty()) {
            logger.info("自动发布：itemList 为空，跳过发布，任务ID: {}，日志ID: {}", task.getTaskId(), logId);
            return new HashSet<>();
        }

        logger.info("自动发布开始，共 {} 条，任务ID: {}，日志ID: {}", itemList.size(), task.getTaskId(), logId);
        List<UserDeptRoleVO> userDeptRoleVOs = null;
        try {
            ResultDomain<UserDeptRoleVO> roleResult = roleService.getDeptRolesByUserId(task.getCreator());
            if (roleResult != null && roleResult.isSuccess()) {
                userDeptRoleVOs = roleResult.getDataList();
            }
        } catch (Exception e) {
            logger.warn("根据任务创建者获取部门角色失败，taskId: {}，creator: {}，原因: {}", task.getTaskId(), task.getCreator(), e.getMessage());
        }
        Date now = new Date();
        int successCount = 0;
        // 记录 url -> resourceId，用于后续更新采集表
        Map<String, String> urlToResourceId = new HashMap<>();

        for (TbDataCollectionItem item : itemList) {
            if(item.getContent().isEmpty()) continue;
            try {
                TbResource resource = new TbResource();
                resource.setID(IDUtils.generateID());
                resource.setResourceID(IDUtils.generateID());
                resource.setTitle(item.getTitle());
                // 创建者使用任务创建者
                resource.setCreator(task.getCreator());

                // 内容直接使用采集表中的 HTML
                resource.setContent(item.getContent());

                resource.setSummary(null);
                resource.setCoverImage(null);
                // 统一打标签为专题报告（tag_article_005）
                resource.setTagID("tag_article_005");
                resource.setAuthor(item.getAuthor());
                resource.setSource(item.getSource());
                resource.setSourceUrl(item.getSourceUrl());
                resource.setCreator(task.getCreator());
                // 发布时间：优先使用采集表中的时间
                Date publishTime = item.getPublishTime() != null ? item.getPublishTime() : now;
                resource.setPublishTime(publishTime);

                // 状态：已发布
                resource.setStatus(1);
                resource.setIsAudited(true);
                resource.setViewCount(0);
                resource.setLikeCount(0);
                resource.setCollectCount(0);
                resource.setIsRecommend(false);
                resource.setIsBanner(false);
                resource.setCreateTime(now);
                resource.setUpdateTime(now);
                resource.setDeleted(false);

                // 逐条创建资源，避免整批回滚
                ResourceVO vo = new ResourceVO();
                vo.setResource(resource);

                ResultDomain<ResourceVO> createResult = resourceService.createResource(vo, userDeptRoleVOs);
                if (createResult.isSuccess()) {
                    successCount++;
                    // 记录 url -> resourceId，用于回写采集表
                    if (item.getSourceUrl() != null && !item.getSourceUrl().isEmpty()) {
                        urlToResourceId.put(item.getSourceUrl(), resource.getResourceID());
                    }
                } else {
                    logger.warn("自动发布：创建资源失败，标题: {}，原因: {}", item.getTitle(), createResult.getMessage());
                }
            } catch (Exception e) {
                logger.error("自动发布单条新闻异常，标题: {}", item.getTitle(), e);
            }
        }

        logger.info("自动发布完成，共 {} 条，成功 {} 条，任务ID: {}，日志ID: {}", itemList.size(), successCount, task.getTaskId(), logId);

        // 成功创建资源后，同步更新采集表状态
        try {
            TbDataCollectionItem filter = new TbDataCollectionItem();
            filter.setTaskId(task.getTaskId());
            filter.setLogId(logId);

            ResultDomain<DataCollectionItemVO> itemResult = itemService.getItemList(filter);
            if (itemResult.isSuccess() && itemResult.getDataList() != null) {
                for (DataCollectionItemVO itemVO : itemResult.getDataList()) {
                    // 只处理未转换的采集项
                    if (itemVO.getStatus() != null && itemVO.getStatus() != 0) {
                        continue;
                    }

                    String sourceUrl = itemVO.getSourceUrl();
                    if (sourceUrl == null || sourceUrl.isEmpty()) {
                        continue;
                    }

                    String resourceId = urlToResourceId.get(sourceUrl);
                    if (resourceId == null) {
                        continue;
                    }

                    TbDataCollectionItem update = new TbDataCollectionItem();
                    update.setID(itemVO.getId());
                    update.setStatus(1); // 已转换为资源
                    update.setResourceId(resourceId);
                    update.setProcessTime(now);
                    update.setProcessor(task.getCreator());

                    itemService.updateItem(update);
                }
            }
        } catch (Exception e) {
            logger.error("自动发布后更新采集表状态异常，任务ID: {}，日志ID: {}", task.getTaskId(), logId, e);
        }

        return urlToResourceId.keySet();
    }
}
