package com.spider.quartz.task;

import com.microsoft.playwright.*;
import com.microsoft.playwright.options.LoadState;
import com.microsoft.playwright.options.WaitUntilState;
import com.spider.quartz.domain.ExcelDownloadLog;
import com.spider.quartz.mapper.ExcelDownloadLogMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 欧盟不合格商品爬取任务 - 主要负责网站接入和XML下载
 */
@Component
public class EuropeTask {

    @Autowired
    private ExcelDownloadLogMapper excelDownloadLogMapper;

    @Autowired
    private EuropeXmlProcessor europeXmlProcessor;

    // ===================== 配置常量 =====================
    private static final String RECALL_LIST_URL = "https://ec.europa.eu/safety-gate-alerts/screen/search?resetSearch=true";
    private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";

    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    private static final SimpleDateFormat FILE_DATE_FORMAT = new SimpleDateFormat("yyyyMMdd");

    // 从配置文件中获取存储路径
    @Value("${ruoyi.profile}")
    private String profileDir;

    // 网络配置
    private static final int LIST_PAGE_TIMEOUT = 180000;
    private static final int MAX_RETRIES = 5;

    // 浏览器实例管理
    private Playwright playwright;
    private Browser browser;

    /**
     * 启动爬取任务 - 主要负责网站交互和XML下载
     */
    public void startCrawl(boolean isManual) {
        String triggerType = isManual ? "手动触发" : "定时触发";
        System.out.println("【" + triggerType + "】开始执行欧盟不合格商品数据爬取任务");

        ExcelDownloadLog downloadLog = null;

        try {
            // 1. 初始化存储目录
            initSaveDirs();

            // 2. 初始化Playwright和浏览器
            initPlaywright();

            // 3. 创建下载日志记录
            downloadLog = createDownloadLog();
            if (downloadLog == null) {
                throw new RuntimeException("创建下载日志记录失败，无法继续执行");
            }

            // 4. 下载XML文件（最多重试5次）
            File xmlFile = downloadXmlWithRetry();

            if (xmlFile != null) {
                // 5. 更新下载日志为成功
                updateDownloadLogSuccess(downloadLog, xmlFile.getAbsolutePath());

                // 6. 调用XML处理器解析并保存数据
                europeXmlProcessor.processXmlFile(xmlFile);
            } else {
                // 下载失败，更新下载日志
                updateDownloadLogFailure(downloadLog, "XML文件下载失败，重试" + MAX_RETRIES + "次后仍失败");
            }

            System.out.println("\n==================== 爬取完成 ====================");
            System.out.println("触发类型: " + triggerType);

        } catch (Exception e) {
            System.err.println("【" + triggerType + "】爬取过程异常：" + e.getMessage());
            e.printStackTrace();

            // 更新下载日志为失败
            if (downloadLog != null) {
                updateDownloadLogFailure(downloadLog, "爬取过程异常: " + e.getMessage());
            }
        } finally {
            // 关闭资源
            cleanupResources();
        }
    }

    /**
     * 创建下载日志记录
     */
    private ExcelDownloadLog createDownloadLog() {
        ExcelDownloadLog log = new ExcelDownloadLog();
        log.setType("欧盟");
        log.setDownloadTime(new Date());
        log.setStatus(1L); // 默认设为失败，下载成功后再更新
        log.setIsDeleted(0L); // 未删除

        // 设置创建人和创建部门（根据实际情况设置）
        log.setCreateUser("system");
        log.setCreateDept("system");

        try {
            int result = excelDownloadLogMapper.insertExcelDownloadLog(log);
            if (result > 0) {
                System.out.println("创建下载日志记录成功，ID: " + log.getId());
                return log;
            } else {
                System.err.println("创建下载日志记录失败");
                return null;
            }
        } catch (Exception e) {
            System.err.println("创建下载日志记录异常: " + e.getMessage());
            return null;
        }
    }

    /**
     * 更新下载日志为成功
     */
    private void updateDownloadLogSuccess(ExcelDownloadLog log, String xmlPath) {
        if (log == null) {
            System.err.println("下载日志记录为空，无法更新");
            return;
        }

        log.setStatus(0L); // 成功
        log.setExcelPath(xmlPath); // 保存XML文件路径到excel_path字段

        try {
            int result = excelDownloadLogMapper.updateExcelDownloadLog(log);
            if (result > 0) {
                System.out.println("更新下载日志为成功状态，XML路径: " + xmlPath);
            } else {
                System.err.println("更新下载日志失败");
            }
        } catch (Exception e) {
            System.err.println("更新下载日志异常: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 更新下载日志为失败
     */
    private void updateDownloadLogFailure(ExcelDownloadLog log, String errorMessage) {
        if (log == null) {
            System.err.println("下载日志记录为空，无法更新");
            return;
        }

        log.setStatus(1L); // 失败

        try {
            int result = excelDownloadLogMapper.updateExcelDownloadLog(log);
            if (result > 0) {
                System.out.println("更新下载日志为失败状态: " + errorMessage);
            } else {
                System.err.println("更新下载日志失败");
            }
        } catch (Exception e) {
            System.err.println("更新下载日志异常: " + e.getMessage());
            e.printStackTrace();
        }
    }

    /**
     * 带重试的XML文件下载
     */
    private File downloadXmlWithRetry() {
        int retryCount = 0;

        while (retryCount < MAX_RETRIES) {
            try {
                System.out.println("第 " + (retryCount + 1) + " 次尝试下载XML文件...");

                File xmlFile = downloadXmlFile();
                if (xmlFile != null) {
                    System.out.println("XML文件下载成功");
                    return xmlFile;
                }

            } catch (Exception e) {
                System.err.println("第 " + (retryCount + 1) + " 次下载失败: " + e.getMessage());
            }

            retryCount++;

            if (retryCount < MAX_RETRIES) {
                try {
                    int delay = 5000 * retryCount; // 递增延迟
                    System.out.println("等待 " + delay + " 毫秒后重试...");
                    Thread.sleep(delay);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    break;
                }
            }
        }

        System.err.println("XML文件下载失败，已达最大重试次数: " + MAX_RETRIES);
        return null;
    }

    /**
     * 下载XML文件 - 核心下载逻辑
     */
    private File downloadXmlFile() {
        BrowserContext context = null;
        Page page = null;

        try {
            context = browser.newContext();
            page = context.newPage();
            setEnhancedBrowserConfig(page);

            System.out.println("导航到欧盟安全门搜索页面: " + RECALL_LIST_URL);
            page.navigate(RECALL_LIST_URL, new Page.NavigateOptions()
                    .setTimeout(LIST_PAGE_TIMEOUT)
                    .setWaitUntil(WaitUntilState.NETWORKIDLE));

            // 等待页面加载完成
            page.waitForLoadState(LoadState.NETWORKIDLE);
            Thread.sleep(5000);

            System.out.println("页面加载完成，开始查找下载按钮...");

            // 等待下载按钮出现
            page.waitForSelector("a.ecl-link.ecl-link--standalone.ecl-popover__toggle",
                    new Page.WaitForSelectorOptions().setTimeout(30000));

            // 点击下载按钮
            System.out.println("点击下载按钮...");
            page.click("a.ecl-link.ecl-link--standalone.ecl-popover__toggle");

            // 等待弹出菜单出现
            page.waitForSelector("div.ecl-popover__container",
                    new Page.WaitForSelectorOptions().setTimeout(30000));

            Thread.sleep(2000);

            // 点击"Export to XML"选项
            System.out.println("点击Export to XML选项...");
            page.click("a.ecl-popover__link:has-text('Export to XML')");

            // 等待下载开始
            System.out.println("等待XML文件下载...");
            Download download = page.waitForDownload(() -> {
                // 下载会在点击后自动开始
            });

            // 生成保存路径
            String timestamp = FILE_DATE_FORMAT.format(new Date()) + "_" + System.currentTimeMillis();
            String xmlFileName = "EU_Recalls_" + timestamp + ".xml";
            String xmlFilePath = getXmlSaveDir() + File.separator + xmlFileName;

            // 保存文件
            download.saveAs(new File(xmlFilePath).toPath());
            System.out.println("XML文件已保存: " + xmlFilePath);

            File xmlFile = new File(xmlFilePath);
            if (xmlFile.exists() && xmlFile.length() > 0) {
                System.out.println("XML文件验证成功，文件大小: " + xmlFile.length() + " 字节");
                return xmlFile;
            } else {
                System.err.println("XML文件验证失败");
                return null;
            }

        } catch (Exception e) {
            System.err.println("下载XML文件失败: " + e.getMessage());

            // 保存页面截图用于调试
            if (page != null) {
                try {
                    String screenshotPath = getDataSaveDir() + File.separator + "debug_screenshot_" + System.currentTimeMillis() + ".png";
                    page.screenshot(new Page.ScreenshotOptions().setPath(new File(screenshotPath).toPath()));
                    System.out.println("已保存调试截图: " + screenshotPath);
                } catch (Exception ex) {
                    System.err.println("保存截图失败: " + ex.getMessage());
                }
            }

            return null;
        } finally {
            closeResources(page, context);
        }
    }

    // ===================== 辅助方法 =====================

    /**
     * 获取数据存储目录
     */
    private String getDataSaveDir() {
        return profileDir + File.separator + "EU";
    }

    /**
     * 获取XML保存目录
     */
    private String getXmlSaveDir() {
        return getDataSaveDir() + File.separator + "XML";
    }

    /**
     * 初始化存储目录
     */
    private void initSaveDirs() {
        String dataSaveDir = getDataSaveDir();
        File dataDir = new File(dataSaveDir);
        if (!dataDir.exists() && !dataDir.mkdirs()) {
            System.err.println("数据目录创建失败：" + dataSaveDir);
            throw new RuntimeException("数据目录创建失败: " + dataSaveDir);
        }

        String xmlSaveDir = getXmlSaveDir();
        File xmlDir = new File(xmlSaveDir);
        if (!xmlDir.exists() && !xmlDir.mkdirs()) {
            System.err.println("XML目录创建失败：" + xmlSaveDir);
            throw new RuntimeException("XML目录创建失败: " + xmlSaveDir);
        }

        System.out.println("欧盟数据存储目录初始化完成：" + dataSaveDir);
        System.out.println("欧盟XML存储目录初始化完成：" + xmlSaveDir);
    }

    /**
     * 初始化Playwright和浏览器
     */
    private void initPlaywright() {
        try {
            playwright = Playwright.create();
            List<String> browserArgs = Arrays.asList(
                    "--disable-gpu",
                    "--disable-extensions",
                    "--no-sandbox",
                    "--disable-dev-shm-usage",
                    "--lang=en-US",
                    "--disable-blink-features=AutomationControlled",
                    "--user-agent=" + USER_AGENT
            );

            BrowserType.LaunchOptions launchOptions = new BrowserType.LaunchOptions()
                    .setHeadless(true)
                    .setArgs(browserArgs)
                    .setTimeout(180000);

            browser = playwright.chromium().launch(launchOptions);
            System.out.println("Playwright和浏览器初始化完成");
        } catch (Exception e) {
            System.err.println("Playwright初始化失败：" + e.getMessage());
            throw new RuntimeException("Playwright初始化失败", e);
        }
    }

    /**
     * 配置浏览器
     */
    private void setEnhancedBrowserConfig(Page page) {
        Map<String, String> headers = new HashMap<>();
        headers.put("User-Agent", USER_AGENT);
        headers.put("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8");
        headers.put("Accept-Language", "en-US,en;q=0.9");
        headers.put("Accept-Encoding", "gzip, deflate, br");
        headers.put("Referer", "https://ec.europa.eu/");
        page.setExtraHTTPHeaders(headers);

        page.addInitScript("() => {\n" +
                "  Object.defineProperty(navigator, 'webdriver', { get: () => undefined });\n" +
                "  delete navigator.__proto__.webdriver;\n" +
                "  Object.defineProperty(navigator, 'plugins', { get: () => [1, 2, 3, 4, 5] });\n" +
                "  Object.defineProperty(navigator, 'languages', { get: () => ['en-US', 'en'] });\n" +
                "}");

        // 设置视口大小
        page.setViewportSize(1920, 1080);
    }

    /**
     * 清理资源
     */
    private void cleanupResources() {
        if (browser != null) {
            try {
                browser.close();
                System.out.println("浏览器已关闭");
            } catch (Exception e) {
                System.err.println("关闭浏览器时出错：" + e.getMessage());
            }
        }

        if (playwright != null) {
            try {
                playwright.close();
                System.out.println("Playwright 已关闭");
            } catch (Exception e) {
                System.err.println("关闭 Playwright 时出错：" + e.getMessage());
            }
        }
    }

    /**
     * 安全关闭资源
     */
    private void closeResources(Page page, BrowserContext context) {
        if (page != null) {
            try {
                page.close();
            } catch (Exception e) {
                // 静默处理
            }
        }
        if (context != null) {
            try {
                context.close();
            } catch (Exception e) {
                // 静默处理
            }
        }
    }

    /**
     * 定时任务调用的方法
     */
    public void startCrawlBySchedule() {
        try {
            System.out.println("========== 定时任务开始执行 欧盟不合格商品数据爬取 ==========");
            System.out.println("执行时间: " + DATE_FORMAT.format(new Date()));

            startCrawl(false);

            System.out.println("========== 定时任务执行完成 ==========");
        } catch (Exception e) {
            System.err.println("定时任务执行异常: " + e.getMessage());
            e.printStackTrace();
        }
    }
}