/**
 * @ProjectName: 智慧评标-主观分助手
 * @Package: com.zhpb.gdyd_zhpb_zgf.service
 * @ClassName: PdfContentCache
 * @Description: PDF内容缓存服务，避免重复读取PDF文件
 * @Author: 张志锋
 * @Date: 2025-12-11
 * @Version: 1.0
 * @Copyright: © 2025 智慧评标-主观分助手
 */
package com.zhpb.gdyd_zhpb_zgf.service;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.text.PDFTextStripper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import java.io.File;
import java.nio.file.Path;
import java.util.Map;
import java.util.concurrent.*;

/**
 * PDF内容缓存服务
 * 使用LRU缓存策略，避免重复读取PDF文件
 */
@Service
public class PdfContentCache {

    private static final Logger logger = LoggerFactory.getLogger(PdfContentCache.class);

    // LRU缓存：文件路径 -> 全文内容
    private final Map<String, CachedContent> contentCache;
    
    // 按页缓存：文件路径 -> (页码 -> 页内容)
    private final Map<String, ConcurrentMap<Integer, String>> pageCache;
    
    // 缓存配置
    private static final int MAX_CACHE_ENTRIES = 20;  // 最多缓存20个PDF
    private static final long CACHE_EXPIRE_MS = 10 * 60 * 1000;  // 10分钟过期
    
    // 并行读取配置
    private static final int PARALLEL_PAGE_THRESHOLD = 50;  // 超过50页启用并行读取
    private static final int PAGES_PER_BATCH = 20;  // 每批读取20页
    
    private final ExecutorService pdfReadExecutor;
    
    public PdfContentCache() {
        // 使用LinkedHashMap实现LRU缓存
        this.contentCache = new java.util.LinkedHashMap<String, CachedContent>(MAX_CACHE_ENTRIES, 0.75f, true) {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, CachedContent> eldest) {
                if (size() > MAX_CACHE_ENTRIES) {
                    logger.debug("移除最久未使用的PDF缓存: {}", eldest.getKey());
                    return true;
                }
                return false;
            }
        };
        this.pageCache = new ConcurrentHashMap<>();
        
        // PDF读取线程池
        this.pdfReadExecutor = Executors.newFixedThreadPool(3, r -> {
            Thread t = new Thread(r, "pdf-reader");
            t.setDaemon(true);
            return t;
        });
        
        logger.info("PDF内容缓存服务初始化完成，最大缓存: {} 个文件，过期时间: {} 分钟", 
            MAX_CACHE_ENTRIES, CACHE_EXPIRE_MS / 60000);
    }
    
    /**
     * 获取PDF全文内容（带缓存）
     */
    public String getFullContent(String filePath) {
        return getFullContent(filePath, 100);  // 默认最多读取100页
    }
    
    /**
     * 获取PDF全文内容（带缓存）
     */
    public synchronized String getFullContent(String filePath, int maxPages) {
        // 检查缓存
        CachedContent cached = contentCache.get(filePath);
        if (cached != null && !cached.isExpired()) {
            logger.debug("PDF缓存命中: {}", filePath);
            return cached.content;
        }
        
        // 缓存未命中，读取PDF
        logger.info("PDF缓存未命中，开始读取: {}", filePath);
        long startTime = System.currentTimeMillis();
        
        String content = readPdfContent(filePath, maxPages);
        
        // 存入缓存
        contentCache.put(filePath, new CachedContent(content, System.currentTimeMillis()));
        
        long elapsed = System.currentTimeMillis() - startTime;
        logger.info("PDF读取完成: {}, 耗时: {}ms, 内容长度: {} 字符", 
            filePath, elapsed, content.length());
        
        return content;
    }
    
    /**
     * 获取PDF指定页范围的内容（带缓存）
     */
    public String getPageContent(String filePath, int startPage, int endPage) {
        String cacheKey = filePath;
        pageCache.computeIfAbsent(cacheKey, k -> new ConcurrentHashMap<>());
        
        StringBuilder result = new StringBuilder();
        
        for (int page = startPage; page <= endPage; page++) {
            ConcurrentMap<Integer, String> pages = pageCache.get(cacheKey);
            String pageContent = pages.get(page);
            
            if (pageContent == null) {
                // 页面未缓存，需要读取
                pageContent = readSinglePage(filePath, page);
                pages.put(page, pageContent);
            }
            
            result.append(pageContent).append("\n");
        }
        
        return result.toString();
    }
    
    /**
     * 预加载PDF内容（异步）
     */
    public CompletableFuture<String> preloadAsync(String filePath, int maxPages) {
        return CompletableFuture.supplyAsync(() -> getFullContent(filePath, maxPages), pdfReadExecutor);
    }
    
    /**
     * 并行读取大PDF（按页范围分批）
     */
    public String readLargePdfParallel(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            logger.warn("PDF文件不存在: {}", filePath);
            return "";
        }
        
        try (PDDocument document = Loader.loadPDF(file)) {
            int totalPages = document.getNumberOfPages();
            
            if (totalPages <= PARALLEL_PAGE_THRESHOLD) {
                // 小PDF直接读取
                return readPdfContent(filePath, totalPages);
            }
            
            logger.info("大PDF并行读取: {}, 总页数: {}", filePath, totalPages);
            long startTime = System.currentTimeMillis();
            
            // 计算批次
            int batchCount = (totalPages + PAGES_PER_BATCH - 1) / PAGES_PER_BATCH;
            
            // 并行读取各批次
            CompletableFuture<String>[] futures = new CompletableFuture[batchCount];
            
            for (int i = 0; i < batchCount; i++) {
                int batchStart = i * PAGES_PER_BATCH + 1;
                int batchEnd = Math.min((i + 1) * PAGES_PER_BATCH, totalPages);
                
                final int start = batchStart;
                final int end = batchEnd;
                
                futures[i] = CompletableFuture.supplyAsync(() -> {
                    try {
                        return readPageRange(filePath, start, end);
                    } catch (Exception e) {
                        logger.error("读取PDF页面范围失败: pages={}-{}", start, end, e);
                        return "";
                    }
                }, pdfReadExecutor);
            }
            
            // 等待所有批次完成并合并结果
            CompletableFuture.allOf(futures).join();
            
            StringBuilder fullContent = new StringBuilder();
            for (CompletableFuture<String> future : futures) {
                fullContent.append(future.get());
            }
            
            long elapsed = System.currentTimeMillis() - startTime;
            logger.info("大PDF并行读取完成: {}, 耗时: {}ms", filePath, elapsed);
            
            return fullContent.toString();
            
        } catch (Exception e) {
            logger.error("并行读取大PDF失败: {}", filePath, e);
            return readPdfContent(filePath, 100);  // 降级到普通读取
        }
    }
    
    /**
     * 清除指定文件的缓存
     */
    public synchronized void evict(String filePath) {
        contentCache.remove(filePath);
        pageCache.remove(filePath);
        logger.debug("清除PDF缓存: {}", filePath);
    }
    
    /**
     * 清除所有缓存
     */
    public synchronized void clearAll() {
        contentCache.clear();
        pageCache.clear();
        logger.info("清除所有PDF缓存");
    }
    
    /**
     * 获取缓存统计信息
     */
    public Map<String, Object> getCacheStats() {
        Map<String, Object> stats = new ConcurrentHashMap<>();
        stats.put("fullContentCacheSize", contentCache.size());
        stats.put("pageCacheSize", pageCache.size());
        stats.put("maxCacheEntries", MAX_CACHE_ENTRIES);
        stats.put("cacheExpireMinutes", CACHE_EXPIRE_MS / 60000);
        return stats;
    }
    
    // ==================== 私有方法 ====================
    
    private String readPdfContent(String filePath, int maxPages) {
        File file = new File(filePath);
        if (!file.exists()) {
            logger.warn("PDF文件不存在: {}", filePath);
            return "";
        }
        
        try (PDDocument document = Loader.loadPDF(file)) {
            PDFTextStripper stripper = new PDFTextStripper();
            stripper.setStartPage(1);
            stripper.setEndPage(Math.min(document.getNumberOfPages(), maxPages));
            stripper.setSortByPosition(true);
            
            String text = stripper.getText(document);
            return text != null ? text.trim() : "";
            
        } catch (Exception e) {
            logger.error("读取PDF内容失败: {}", filePath, e);
            return "";
        }
    }
    
    private String readPageRange(String filePath, int startPage, int endPage) {
        File file = new File(filePath);
        if (!file.exists()) {
            return "";
        }
        
        try (PDDocument document = Loader.loadPDF(file)) {
            PDFTextStripper stripper = new PDFTextStripper();
            stripper.setStartPage(startPage);
            stripper.setEndPage(endPage);
            stripper.setSortByPosition(true);
            
            String text = stripper.getText(document);
            return text != null ? text.trim() : "";
            
        } catch (Exception e) {
            logger.error("读取PDF页面范围失败: {}, pages={}-{}", filePath, startPage, endPage, e);
            return "";
        }
    }
    
    private String readSinglePage(String filePath, int page) {
        return readPageRange(filePath, page, page);
    }
    
    /**
     * 缓存内容包装类
     */
    private static class CachedContent {
        final String content;
        final long timestamp;
        
        CachedContent(String content, long timestamp) {
            this.content = content;
            this.timestamp = timestamp;
        }
        
        boolean isExpired() {
            return System.currentTimeMillis() - timestamp > CACHE_EXPIRE_MS;
        }
    }
}
