"""
过滤逻辑模块
负责使用规则预过滤 + LLM精确过滤学术论文中不需要的内容，保留核心学术内容。
"""

from datetime import datetime
import logging
import os
import re
from typing import Any

from ..llm.llm_interface import ChatMessage, LLMFactory, MessageRole


logger = logging.getLogger(__name__)


# ============================================================================
# 预过滤关键词配置（基于79个学术论文markdown分析）
# ============================================================================

# Tier 1: 必须过滤的章节标题（高精度）
SKIP_TITLE_TIER1 = [
    "references",
    "ref",
    "bibliography",
    "acknowledgments",
    "acknowledgements",
    "author contributions",
    "author contribution",
    "conflict of interest",
    "competing interest",
    "data availability",
    "supporting information",
    "supplementary material",
    "appendix",
    "appendices",
]

# Tier 2: 通常应该过滤的章节标题
SKIP_TITLE_TIER2 = [
    "abbreviations",
    "keywords",
    "correspondence",
    "edited by",
    "funding",
    "copyright",
    "peer review",
    "review history",
    "author information",
    "additional information",
    "supplementary figures",
    "supplementary tables",
    "supplemental information",
]

# 内容级过滤模式（检测到这些模式时过滤相关行）
SKIP_CONTENT_PATTERNS = [
    # DOI URLs
    re.compile(r"https://doi\.org/10\.\d+"),
    re.compile(r"doi:\s*10\.\d+", re.IGNORECASE),
    # Email addresses
    re.compile(r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}"),
    # Supplementary figure/table references
    re.compile(r"(Fig\.|Figure|Table)\s+S\d+", re.IGNORECASE),
    # Code repositories
    re.compile(r"github\.com/[a-zA-Z0-9_-]+", re.IGNORECASE),
    # License statements
    re.compile(r"Creative Commons Attribution", re.IGNORECASE),
    re.compile(r"CC BY"),
    # Metadata lines
    re.compile(r"^(Received|Accepted|Published|Submitted)\s*:"),
    re.compile(r"^\s*\*\s*Correspondence:"),
]


class MarkdownFilter:
    """基于LLM的Markdown过滤器"""

    def __init__(
        self,
        model: str | None = None,
        provider: str = "pydantic_ai",
        **llm_kwargs: Any,
    ) -> None:
        """
        初始化过滤器

        默认从环境变量读取配置:
        - OPENAI_MODEL: 模型名称（必填）
        - OPENAI_API_KEY: API密钥
        - OPENAI_BASE_URL: API基础URL

        Args:
            model: 使用的LLM模型名称（默认: 环境变量OPENAI_MODEL）
            provider: LLM提供商（默认: "pydantic_ai"）
            **llm_kwargs: 传递给LLM的额外参数 (如timeout, base_url等)
        """
        # 从环境变量读取配置
        self.model = model or os.getenv("OPENAI_MODEL")
        api_key = os.getenv("OPENAI_API_KEY")
        base_url = os.getenv("OPENAI_BASE_URL")

        if not self.model:
            raise ValueError(
                "model is required. Set OPENAI_MODEL environment variable "
                "or pass model parameter."
            )

        # 使用 OpenAI-compatible 客户端，自动适配 OpenAI 兼容端点
        provider = "openai"
        if api_key:
            llm_kwargs["api_key"] = api_key
        if base_url:
            llm_kwargs["base_url"] = base_url

        self.provider = provider
        self.llm = LLMFactory.create(provider, self.model, **llm_kwargs)
        # 定义过滤提示词
        self.filter_prompt = (
            "You are an expert in analyzing academic papers in Markdown format. "
            "Your task is to extract key sections from a Markdown-formatted "
            "research paper and return them as JSON.\n\n"
            "Return a JSON object with the following structure:\n"
            "{\n"
            '  "title": "paper title or empty string",\n'
            '  "abstract": "abstract content or empty string",\n'
            '  "introduction": "introduction content or empty string",\n'
            '  "methods": "methods content or empty string",\n'
            '  "results": "results content or empty string",\n'
            '  "discussion": "discussion content or empty string",\n'
            '  "conclusion": "conclusion content or empty string"\n'
            "}\n\n"
            "Guidelines:\n"
            "- Extract content EXACTLY as it appears in the original text.\n"
            "- Do NOT modify, paraphrase, or summarize any content.\n"
            "- Include all markdown formatting (headers, lists, code blocks, etc.).\n"
            "- Only include sections that exist in the paper.\n"
            "- Exclude: References/Bibliography, Author affiliations, Acknowledgments, "
            "Appendices, Footnotes, Page numbers, Supplementary materials.\n"
            "- Your response must be ONLY the valid JSON object, no other text.\n\n"
            "Process the following Markdown input:"
        )

    async def filter_paper(self, content: str) -> str:
        """
        使用规则预过滤 + LLM精确过滤论文内容
        Args:
            content: 原始Markdown文档内容
        Returns:
            过滤后的Markdown文档内容
        Raises:
            Exception: 过滤失败时抛出异常
        """
        try:
            logger.info("开始过滤论文内容")
            # 验证输入内容
            if not content or not content.strip():
                logger.warning("输入内容为空，返回空结果")
                return ""

            # Step 1: 规则预过滤（大幅减少token使用）
            prefiltered_content = self._pre_filter(content)
            pre_stats = self.get_filter_statistics(content, prefiltered_content)
            logger.info(
                f"预过滤完成: 减少 {pre_stats.get('chars_reduction_percent', 0):.1f}% "
                f"({pre_stats.get('chars_reduction', 0)} 字符)"
            )

            # Step 2: LLM精确过滤
            try:
                full_prompt = f"{self.filter_prompt}\n\n{prefiltered_content}"
                messages = [
                    ChatMessage(
                        role=MessageRole.SYSTEM,
                        content="You are a JSON API. Return ONLY valid JSON.",
                    ),
                    ChatMessage(role=MessageRole.USER, content=full_prompt),
                ]
                response = await self.llm.chat(messages)
                llm_output = response.content if response and response.content else None
                if not llm_output or not llm_output.strip():
                    logger.warning("LLM返回空内容，使用回退过滤")
                    return self._simple_fallback_filter(prefiltered_content)
                # 清理thinking标签
                llm_output = self._clean_thinking_content(llm_output)
                # 解析JSON并转换为Markdown
                filtered_content = self._json_to_markdown(llm_output)
                if not filtered_content:
                    logger.warning("JSON解析失败，使用回退过滤")
                    return self._simple_fallback_filter(prefiltered_content)
                logger.info("LLM过滤完成")
            except Exception as e:
                logger.error(f"LLM过滤失败: {e}")
                # 回退到简单过滤
                return self._simple_fallback_filter(prefiltered_content)

            logger.info("论文内容过滤完成")
            return filtered_content
        except Exception as e:
            logger.error(f"论文过滤失败: {e}")
            # 最终回退方案
            return self._emergency_filter(content)

    def _simple_fallback_filter(self, content: str) -> str:
        """简单的回退过滤方法"""
        logger.info("使用简单回退过滤方法")
        try:
            lines = content.split("\n")
            filtered_lines = []
            skip_section = False
            found_main_content = False
            for line in lines:
                line_stripped = line.strip()
                if not line_stripped:
                    if not skip_section:
                        filtered_lines.append("")
                    continue
                # 检查是否是需要跳过的章节
                if line_stripped.startswith("#"):
                    skip_section = self._should_skip_section_simple(line_stripped)
                    logger.debug(f"章节: {line_stripped}, 跳过: {skip_section}")
                    # 如果找到主要章节，标记为已找到主要内容
                    if not skip_section and any(
                        keyword in line_stripped.lower()
                        for keyword in [
                            "abstract",
                            "introduction",
                            "methods",
                            "results",
                            "discussion",
                            "conclusion",
                        ]
                    ):
                        found_main_content = True
                # 如果不需要跳过，则保留内容
                if not skip_section:
                    filtered_lines.append(line)
            # 如果找到了主要内容，返回过滤结果
            if found_main_content:
                return "\n".join(filtered_lines)
            else:
                # 如果没有找到主要内容，使用紧急过滤
                logger.warning("未找到主要内容，使用紧急过滤")
                return self._emergency_filter(content)
        except Exception as e:
            logger.error(f"简单回退过滤失败: {e}")
            return self._emergency_filter(content)

    def _pre_filter(self, content: str) -> str:
        """
        规则预过滤：移除明显不需要的内容，大幅减少token使用

        基于79个学术论文markdown分析得出的关键词规则进行过滤。

        Args:
            content: 原始Markdown文档内容
        Returns:
            预过滤后的Markdown文档内容
        """
        logger.info("执行规则预过滤")
        try:
            lines = content.split("\n")
            filtered_lines = []
            skip_section = False
            skip_tier2_section = False

            for line in lines:
                line_stripped = line.strip()

                # 检测是否为空行
                if not line_stripped:
                    if not skip_section and not skip_tier2_section:
                        filtered_lines.append("")
                    continue

                # 检测标题行
                if line_stripped.startswith("#"):
                    skip_section, skip_tier2_section = self._should_skip_heading(
                        line_stripped
                    )

                    # 如果是Tier 1，跳过整个章节
                    if skip_section:
                        logger.debug(f"Tier1 跳过: {line_stripped}")
                        continue
                    # 如果是Tier 2，继续处理但标记
                    if skip_tier2_section:
                        logger.debug(f"Tier2 跳过内容: {line_stripped}")
                        # Tier 2 标题保留（可能是小节），但内容会被过滤

                # 根据当前状态决定是否保留行
                if skip_section:
                    continue

                # Tier 2 模式：只跳过内容行，保留标题
                if skip_tier2_section and not line_stripped.startswith("#"):
                    # 检查内容级过滤模式
                    if self._should_skip_content(line_stripped):
                        logger.debug(f"内容级跳过: {line_stripped[:50]}...")
                        continue

                # 保留该行
                filtered_lines.append(line)

            result = "\n".join(filtered_lines)
            logger.info(f"预过滤完成，保留 {len(filtered_lines)} 行")
            return result

        except Exception as e:
            logger.error(f"预过滤失败: {e}")
            return content

    def _should_skip_heading(self, heading_line: str) -> tuple[bool, bool]:
        """
        判断标题是否应该被跳过

        Returns:
            tuple[bool, bool]: (skip_tier1, skip_tier2)
            - skip_tier1: 整个章节都应该被跳过
            - skip_tier2: 章节内容应该被跳过（但标题保留）
        """
        heading_lower = heading_line.lower()

        # Tier 1 检查（必须过滤）
        for keyword in SKIP_TITLE_TIER1:
            if keyword in heading_lower:
                return True, False

        # Tier 2 检查（通常过滤）
        for keyword in SKIP_TITLE_TIER2:
            if keyword in heading_lower:
                return False, True

        return False, False

    def _should_skip_content(self, content_line: str) -> bool:
        """
        判断内容行是否应该被跳过（基于内容模式匹配）
        """
        return any(pattern.search(content_line) for pattern in SKIP_CONTENT_PATTERNS)

    def _should_skip_section_simple(self, heading_line: str) -> bool:
        """简单判断是否应该跳过某个章节（兼容旧方法）"""
        skip_tier1, skip_tier2 = self._should_skip_heading(heading_line)
        return skip_tier1 or skip_tier2

    def _add_filter_info(self, content: str) -> str:
        """添加过滤信息"""
        timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
        filter_info = f"""
---
*本文档由 MD_LLM_Filter 自动过滤生成*
*过滤时间: {timestamp}*
*使用模型: {self.model}*
*已过滤内容: 参考文献、致谢、附录、作者信息、页码等*
"""
        return content + filter_info

    async def filter_file(self, input_path: str, output_path: str | None = None) -> str:
        """
        过滤文件
        Args:
            input_path: 输入文件路径
            output_path: 输出文件路径，如果为None则自动生成
        Returns:
            输出文件路径
        Raises:
            Exception: 文件操作失败时抛出异常
        """
        try:
            # 读取输入文件
            with open(input_path, encoding="utf-8") as f:
                content = f.read()
            # 过滤内容
            filtered_content = await self.filter_paper(content)
            # 生成输出路径
            if output_path is None:
                base_name = os.path.splitext(input_path)[0]
                output_path = f"{base_name}_filtered.md"
            # 写入输出文件
            with open(output_path, "w", encoding="utf-8") as f:
                f.write(filtered_content)
            logger.info(f"文件过滤完成: {input_path} -> {output_path}")
            return output_path
        except Exception as e:
            logger.error(f"文件过滤失败: {e}")
            raise

    async def batch_filter(
        self, input_files: list[str], output_dir: str | None = None
    ) -> list[str]:
        """
        批量过滤文件
        Args:
            input_files: 输入文件列表
            output_dir: 输出目录，如果为None则使用输入文件
            所在目录
        Returns:
            输出文件路径列表
        """
        output_files = []
        for input_file in input_files:
            try:
                if output_dir:
                    base_name = os.path.splitext(os.path.basename(input_file))[0]
                    output_file = os.path.join(output_dir, f"{base_name}_filtered.md")
                else:
                    output_file = None
                result = await self.filter_file(input_file, output_file)
                output_files.append(result)
            except Exception as e:
                logger.error(f"批量过滤文件失败 {input_file}: {e}")
                continue
        return output_files

    def get_filter_statistics(
        self, original_content: str, filtered_content: str
    ) -> dict[str, Any]:
        """
        获取过滤统计信息
        Args:
            original_content: 原始内容
            filtered_content: 过滤后内容
        Returns:
            统计信息字典
        """
        try:
            original_lines = len(original_content.split("\n"))
            filtered_lines = len(filtered_content.split("\n"))
            original_chars = len(original_content)
            filtered_chars = len(filtered_content)
            stats = {
                "original_lines": original_lines,
                "filtered_lines": filtered_lines,
                "lines_reduction": original_lines - filtered_lines,
                "lines_reduction_percent": round(
                    (original_lines - filtered_lines) / original_lines * 100, 2
                ),
                "original_chars": original_chars,
                "filtered_chars": filtered_chars,
                "chars_reduction": original_chars - filtered_chars,
                "chars_reduction_percent": round(
                    (original_chars - filtered_chars) / original_chars * 100, 2
                ),
                "compression_ratio": round(filtered_chars / original_chars, 3),
            }
            return stats
        except Exception as e:
            logger.error(f"统计信息计算失败: {e}")
            return {}

    def _clean_thinking_content(self, content: str) -> str:
        """
        清理LLM输出中的thinking标签内容

        支持多种thinking标签格式:
        - <think>...</think> (OpenAI, MiniMax, etc.)
        - </thinking>
        - {{think}}...{{/think}}
        - [THINKING]...[/THINKING]
        - 各种变体

        Args:
            content: 原始LLM输出内容
        Returns:
            清理后的内容
        """
        import re

        patterns = [
            # 标准 XML 风格
            r"<think>.*?</think>",
            # HTML 风格
            r"</?thinking>",
            # 双花括号风格
            r"\{\{think\}\}.*?\{\{/think\}\}",
            r"\{\{think\}\}.*?$",
            # 方括号风格
            r"\[THINKING\].*?\[/THINKING\]",
            r"\[THINKING\].*?$",
            # markdown 风格
            r"```think.*?```",
            r"```thinking.*?```",
        ]

        cleaned = content
        for pattern in patterns:
            cleaned = re.sub(
                pattern, "", cleaned, flags=re.DOTALL | re.IGNORECASE | re.MULTILINE
            )

        # 清理JSON无效字符（如反斜杠转义问题）
        # 移除结尾的反斜杠（如果跟在内容后面）
        cleaned = re.sub(r"\\+$", "", cleaned)

        return cleaned.strip()

    def _json_to_markdown(self, json_str: str) -> str | None:
        """
        将LLM返回的JSON转换为Markdown格式

        Args:
            json_str: 清理后的JSON字符串
        Returns:
            Markdown格式的字符串，如果解析失败返回None
        """
        import json

        try:
            # 尝试解析JSON
            data = json.loads(json_str)

            if not isinstance(data, dict):
                logger.error(f"JSON root is not a dict: {type(data)}")
                return None

            # 定义章节顺序
            section_order = [
                "title",
                "abstract",
                "introduction",
                "methods",
                "results",
                "discussion",
                "conclusion",
            ]

            # 标题映射（中文显示）
            section_titles = {
                "title": "Title",
                "abstract": "Abstract",
                "introduction": "Introduction",
                "methods": "Methods",
                "results": "Results",
                "discussion": "Discussion",
                "conclusion": "Conclusion",
            }

            markdown_parts = []

            for section in section_order:
                content = data.get(section, "")
                if content and isinstance(content, str) and content.strip():
                    title = section_titles.get(section, section.capitalize())
                    # 第一层标题使用 #
                    if section == "title":
                        markdown_parts.append(f"# {content.strip()}")
                    else:
                        markdown_parts.append(f"## {title}\n\n{content.strip()}")

            if not markdown_parts:
                logger.warning("JSON中没有有效内容")
                return None

            return "\n\n".join(markdown_parts)

        except json.JSONDecodeError as e:
            logger.error(f"JSON解析失败: {e}")
            # 记录部分内容用于调试（截取前200字符）
            snippet = json_str[:200] if json_str else "(empty)"
            logger.debug(f"JSON原始内容片段: {snippet}...")
            return None
        except Exception as e:
            logger.error(f"JSON转Markdown失败: {e}")
            return None

    def _emergency_filter(self, content: str) -> str:
        """紧急过滤方法"""
        logger.warning("使用紧急过滤方法")
        try:
            # 最简单的过滤：只保留标题和第一段
            lines = content.split("\n")
            filtered_lines = []
            # 保留第一个标题
            for line in lines:
                if line.strip().startswith("#"):
                    filtered_lines.append(line)
                    break
            # 保留第一段非空内容
            in_first_paragraph = False
            for line in lines:
                if line.strip() and not line.strip().startswith("#"):
                    if not in_first_paragraph:
                        filtered_lines.append("")
                        in_first_paragraph = True
                    filtered_lines.append(line)
                elif in_first_paragraph and not line.strip():
                    break
            result = "\n".join(filtered_lines)
            return result
        except Exception as e:
            logger.error(f"紧急过滤也失败: {e}")
            return "# 过滤失败\n\n原始内容处理出现问题，请检查输入文件。"
