#!/usr/bin/env python3
"""
MD_LLM_Filter CLI 主程序
支持以下功能：
- 单文件过滤：-i 参数
- 目录批量过滤：-m 参数
- 输出指定：-o 参数
- 自动添加_filtered后缀
"""

import argparse
import asyncio
import logging
import os
from pathlib import Path
import sys
from typing import Any

from tqdm import tqdm

from ..core.filter import MarkdownFilter
from ..utils.text_processing import get_document_stats


# 配置日志
logging.basicConfig(
    level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
)
logger = logging.getLogger(__name__)


def find_markdown_files(directory: Path) -> list[Path]:
    """
    在目录中查找所有markdown文件
    Args:
        directory: 目录路径
    Returns:
        markdown文件路径列表
    """
    markdown_files = []
    # 支持的markdown文件扩展名
    markdown_extensions = {".md", ".markdown", ".mdown", ".mkdn", ".mkd"}
    try:
        for file_path in directory.rglob("*"):
            if file_path.is_file() and file_path.suffix.lower() in markdown_extensions:
                markdown_files.append(file_path)
    except Exception as e:
        logger.error(f"扫描目录失败 {directory}: {e}")
    return sorted(markdown_files)


def create_output_path(input_path: Path, output_dir: Path | None = None) -> Path:
    """
    创建输出文件路径
    Args:
        input_path: 输入文件路径
        output_dir: 输出目录，如果为None则使用输入文件所在目录
    Returns:
        输出文件路径
    """
    if output_dir:
        # 如果指定了输出目录，保持相对路径结构
        output_file = output_dir / f"{input_path.stem}_filtered.md"
    else:
        # 在输入文件所在目录创建_filtered文件
        output_file = input_path.parent / f"{input_path.stem}_filtered.md"
    return output_file


async def process_single_file(
    input_file: Path,
    output_file: Path | None = None,
    filter_obj: MarkdownFilter | None = None,
    show_stats: bool = False,
    verbose: bool = False,
    no_llm: bool = False,
) -> tuple[bool, dict | None]:
    """
    处理单个文件
    Args:
        input_file: 输入文件路径
        output_file: 输出文件路径，如果为None则自动生成
        filter_obj: 过滤器对象
        show_stats: 是否显示统计信息
        verbose: 是否详细输出
        no_llm: 是否仅使用预过滤（不调用LLM）
    Returns:
        tuple[bool, dict | None]: (是否成功, 统计信息字典)
    """
    stats: dict | None = None
    try:
        logger.info(f"[{input_file.name}] 开始处理")
        # 生成输出文件路径
        if output_file is None:
            output_file = create_output_path(input_file)
        # 读取输入文件
        with open(input_file, encoding="utf-8") as f:
            content = f.read()
        # 收集统计信息（不立即打印）
        if show_stats:
            original_stats = get_document_stats(content)
            stats = {
                "file": input_file.name,
                "original_lines": original_stats["total_lines"],
                "original_chars": original_stats["characters"],
                "original_words": original_stats["words"],
            }
        # 执行过滤
        if filter_obj is None:
            filter_obj = MarkdownFilter()
        if no_llm:
            logger.info(f"[{input_file.name}] 使用规则预过滤（无LLM）")
            filtered_content = filter_obj._pre_filter(content)
        else:
            filtered_content = await filter_obj.filter_paper(content)
        # 保存输出文件
        with open(output_file, "w", encoding="utf-8") as f:
            f.write(filtered_content)
        logger.info(f"[{input_file.name}] 过滤完成: {output_file}")
        # 收集过滤后统计信息
        if show_stats and stats:
            filtered_stats = get_document_stats(filtered_content)
            filter_stats = filter_obj.get_filter_statistics(content, filtered_content)
            stats["filtered_lines"] = filtered_stats["total_lines"]
            stats["filtered_chars"] = filtered_stats["characters"]
            stats["filtered_words"] = filtered_stats["words"]
            stats["lines_reduction"] = filter_stats["lines_reduction"]
            stats["lines_reduction_percent"] = filter_stats["lines_reduction_percent"]
            stats["chars_reduction"] = filter_stats["chars_reduction"]
            stats["chars_reduction_percent"] = filter_stats["chars_reduction_percent"]
            stats["compression_ratio"] = filter_stats["compression_ratio"]
        return True, stats
    except Exception as e:
        logger.error(f"[{input_file.name}] 处理失败: {e}")
        if verbose:
            logger.exception("详细错误信息:")
        return False, None


async def process_directory(
    input_dir: Path,
    output_dir: Path | None = None,
    filter_obj: MarkdownFilter | None = None,
    show_stats: bool = False,
    verbose: bool = False,
    no_llm: bool = False,
    resume: bool = True,
) -> int:
    """
    处理目录中的所有markdown文件（并行处理）
    Args:
        input_dir: 输入目录路径
        output_dir: 输出目录路径，如果为None则使用输入目录
        filter_obj: 过滤器对象
        show_stats: 是否显示统计信息
        verbose: 是否详细输出
        no_llm: 是否仅使用预过滤（不调用LLM）
        resume: 是否启用续传（跳过已存在的输出文件）
    Returns:
        成功处理的文件数量
    """
    try:
        # 查找所有markdown文件
        markdown_files = find_markdown_files(input_dir)
        if not markdown_files:
            logger.warning(f"在目录 {input_dir} 中未找到markdown文件")
            return 0
        logger.info(f"找到 {len(markdown_files)} 个markdown文件")
        # 创建输出目录
        if output_dir:
            output_dir.mkdir(parents=True, exist_ok=True)

        # 准备文件参数列表（支持续传）
        skipped_files: list[Path] = []
        file_args = []
        for input_file in markdown_files:
            if output_dir:
                relative_path = input_file.relative_to(input_dir)
                output_file = output_dir / f"{relative_path.stem}_filtered.md"
                output_file.parent.mkdir(parents=True, exist_ok=True)
                # 检查是否需要跳过（续传模式下输出文件已存在）
                if resume and output_file.exists():
                    skipped_files.append(output_file)
                    continue
            else:
                output_file = None
            file_args.append((input_file, output_file))

        # 续传提醒
        if resume and skipped_files:
            logger.info(f"续传模式: 跳过 {len(skipped_files)} 个已存在的文件")

        # 并发数：LLM模式5个，预过滤模式20个
        max_concurrency = 20 if no_llm else 5
        semaphore = asyncio.Semaphore(max_concurrency)

        async def process_one(
            args: tuple[Path, Path | None], idx: int, total: int
        ) -> tuple[bool, Path, dict | None]:
            input_file, output_file = args
            if verbose:
                logger.info(f"处理文件: {input_file.name} ({idx + 1}/{total})")
            async with semaphore:
                success, stats = await process_single_file(
                    input_file, output_file, filter_obj, show_stats, verbose, no_llm
                )
            return success, input_file, stats

        # 创建任务列表
        tasks = [
            process_one(args, idx, len(file_args)) for idx, args in enumerate(file_args)
        ]

        # 创建进度条
        progress_bar = tqdm(
            total=len(markdown_files),
            desc="处理文件",
            unit="文件",
            disable=verbose,
        )

        # 并行执行
        results: list[tuple[bool, Path, dict | None]] = []
        for coro in asyncio.as_completed(tasks):
            result = await coro
            results.append(result)
            progress_bar.update(1)

        progress_bar.close()

        # 统计成功数
        success_count = sum(1 for success, _, _ in results if success)
        skipped = len(skipped_files)
        failed = len(markdown_files) - success_count - skipped
        if skipped > 0:
            logger.info(f"跳过 {skipped} 个已存在的文件")
        if failed > 0:
            logger.warning(f"失败 {failed} 个文件")

        # 打印统计信息汇总
        if show_stats:
            all_stats = [s for _, _, s in results if s]
            if all_stats:
                print("\n" + "=" * 60)
                print("📊 过滤统计汇总")
                print("=" * 60)
                for s in all_stats:
                    print(f"\n📄 {s['file']}")
                    print(
                        f"   原始: {s['original_lines']:,}行 {s['original_chars']:,}字符 {s['original_words']:,}单词"
                    )
                    print(
                        f"   过滤后: {s['filtered_lines']:,}行 {s['filtered_chars']:,}字符 {s['filtered_words']:,}单词"
                    )
                    print(
                        f"   减少: {s['lines_reduction']:,}行 ({s['lines_reduction_percent']:.1f}%) {s['chars_reduction']:,}字符 ({s['chars_reduction_percent']:.1f}%)"
                    )
                    print(f"   压缩比: {s['compression_ratio']:.3f}")
                # 汇总
                total_original_chars = sum(s["original_chars"] for s in all_stats)
                total_filtered_chars = sum(s["filtered_chars"] for s in all_stats)
                total_chars_reduction = total_original_chars - total_filtered_chars
                total_reduction_percent = (
                    total_chars_reduction / total_original_chars * 100
                    if total_original_chars
                    else 0
                )
                print("\n" + "-" * 60)
                print(f"📈 总计: {len(all_stats)} 个文件")
                print(f"   原始总字符: {total_original_chars:,}")
                print(f"   过滤后总字符: {total_filtered_chars:,}")
                print(
                    f"   总减少: {total_chars_reduction:,} ({total_reduction_percent:.1f}%)"
                )
                print("=" * 60)

        return success_count
    except Exception as e:
        logger.error(f"处理目录失败 {input_dir}: {e}")
        if verbose:
            logger.exception("详细错误信息:")
        return 0


def main() -> None:
    """主函数"""
    parser = argparse.ArgumentParser(
        description="MD_LLM_Filter - 基于LLM的Markdown学术论文过滤器",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
使用示例:
  # 过滤单个文件 (使用pydantic_ai默认模型)
  md-llm-filter -i paper.md
  # 过滤单个文件并指定输出文件
  md-llm-filter -i paper.md -o filtered_paper.md
  # 批量过滤目录中的所有markdown文件 (显示进度条)
  md-llm-filter -m papers/ -o filtered_papers/
  # 显示统计信息
  md-llm-filter -i paper.md --stats

Ollama本地模型示例:
  # 使用Ollama和默认模型 (llama3)
  md-llm-filter -i paper.md --provider ollama
  # 使用指定的Ollama模型
  md-llm-filter -i paper.md --provider ollama --model mistral
  # 使用量化模型 (如 glm-4.7-flash:q8_0)
  md-llm-filter -i paper.md --provider ollama --model glm-4.7-flash:q8_0
  # 使用Ollama批量处理
  md-llm-filter -m papers/ --provider ollama -o filtered/
  # 详细输出 (禁用进度条)
  md-llm-filter -m papers/ --verbose
  # 批量处理并显示统计信息
  md-llm-filter -m papers/ -o filtered_papers/ --stats
        """,
    )
    # 输入参数组
    input_group = parser.add_mutually_exclusive_group(required=True)
    input_group.add_argument(
        "-i", "--input-file", type=Path, help="输入Markdown文件路径"
    )
    input_group.add_argument(
        "-m", "--input-dir", type=Path, help="输入目录路径 (批量处理所有markdown文件)"
    )
    # 输出参数
    parser.add_argument("-o", "--output", type=Path, help="输出文件路径或目录路径")
    # 其他参数
    parser.add_argument("--stats", action="store_true", help="显示过滤统计信息")
    parser.add_argument(
        "--no-llm",
        action="store_true",
        help="仅使用规则预过滤，不调用LLM（节省token）",
    )
    parser.add_argument(
        "--resume",
        action="store_true",
        default=True,
        help="启用续传模式，跳过已存在的输出文件（默认开启）",
    )
    parser.add_argument(
        "--no-resume",
        action="store_true",
        help="禁用续传模式，重新处理所有文件",
    )
    parser.add_argument(
        "--provider",
        choices=["ollama", "openai", "pydantic", "pydantic_ai"],
        default="pydantic_ai",
        help="LLM提供商 (默认: pydantic_ai，自动适配OpenAI兼容端点)",
    )
    parser.add_argument(
        "--model",
        default=None,
        help=("使用的LLM模型（默认: 环境变量OPENAI_MODEL）"),
    )
    parser.add_argument(
        "--ollama-base-url",
        default="http://localhost:11434",
        help="Ollama API地址 (默认: http://localhost:11434, 仅用于--provider ollama)",
    )
    parser.add_argument(
        "--ollama-timeout",
        type=int,
        default=600,
        metavar="SECONDS",
        help="Ollama请求超时时间(秒) (默认: 600, 仅用于--provider ollama, 大文件可能需要更长时间)",
    )
    parser.add_argument(
        "--api-key",
        default=None,
        help="API密钥 (用于--provider openai, 或设置OPENAI_API_KEY环境变量)",
    )
    parser.add_argument(
        "--base-url",
        default=None,
        help="API基础URL (用于--provider openai, 支持OpenAI兼容端点, 或设置OPENAI_BASE_URL环境变量)",
    )
    parser.add_argument("--verbose", "-v", action="store_true", help="详细输出模式")
    args = parser.parse_args()
    # 设置日志级别
    if args.verbose:
        logging.getLogger().setLevel(logging.DEBUG)
    # 验证输入参数
    if args.input_file:
        if not args.input_file.exists():
            logger.error(f"输入文件不存在: {args.input_file}")
            sys.exit(1)
        if not args.input_file.is_file():
            logger.error(f"输入路径不是文件: {args.input_file}")
            sys.exit(1)
    elif args.input_dir:
        if not args.input_dir.exists():
            logger.error(f"输入目录不存在: {args.input_dir}")
            sys.exit(1)
        if not args.input_dir.is_dir():
            logger.error(f"输入路径不是目录: {args.input_dir}")
            sys.exit(1)
    # 验证输出参数
    if args.output:
        if args.input_file and args.output.is_dir():
            logger.error("当输入是文件时，输出不能是目录")
            sys.exit(1)
        if args.input_dir and args.output.is_file():
            logger.error("当输入是目录时，输出不能是文件")
            sys.exit(1)
    try:
        # 准备额外的参数
        llm_kwargs: dict[str, Any] = {}

        if args.provider == "ollama":
            llm_kwargs["timeout"] = args.ollama_timeout
            llm_kwargs["base_url"] = args.ollama_base_url
            logger.info(f"Using Ollama with timeout: {args.ollama_timeout}s")
        elif args.provider == "openai":
            if args.api_key:
                llm_kwargs["api_key"] = args.api_key
            if args.base_url:
                llm_kwargs["base_url"] = args.base_url
            # Log api_key status (without revealing the key)
            api_key_source = "CLI" if args.api_key else "environment variable"
            if not args.api_key and not os.getenv("OPENAI_API_KEY"):
                api_key_source = "not set (using local endpoint?)"
            logger.info(f"Using OpenAI-compatible API, api_key from: {api_key_source}")

        filter_obj = MarkdownFilter(
            model=args.model, provider=args.provider, **llm_kwargs
        )
        # 执行处理
        if args.input_file:
            # 单文件处理
            success, stats = asyncio.run(
                process_single_file(
                    args.input_file,
                    args.output,
                    filter_obj,
                    args.stats,
                    args.verbose,
                    args.no_llm,
                )
            )
            if success:
                output_path = args.output or create_output_path(args.input_file)
                print("\n✅ 处理完成！")
                print(f"📁 输入文件: {args.input_file}")
                print(f"📁 输出文件: {output_path}")
                # 单文件统计信息
                if args.stats and stats:
                    print("\n" + "=" * 60)
                    print(f"📄 {stats['file']}")
                    print(
                        f"   原始: {stats['original_lines']:,}行 {stats['original_chars']:,}字符 {stats['original_words']:,}单词"
                    )
                    print(
                        f"   过滤后: {stats['filtered_lines']:,}行 {stats['filtered_chars']:,}字符 {stats['filtered_words']:,}单词"
                    )
                    print(
                        f"   减少: {stats['lines_reduction']:,}行 ({stats['lines_reduction_percent']:.1f}%) {stats['chars_reduction']:,}字符 ({stats['chars_reduction_percent']:.1f}%)"
                    )
                    print(f"   压缩比: {stats['compression_ratio']:.3f}")
                    print("=" * 60)
            else:
                logger.error("文件处理失败")
                sys.exit(1)
        elif args.input_dir:
            # 目录批量处理
            success_count = asyncio.run(
                process_directory(
                    args.input_dir,
                    args.output,
                    filter_obj,
                    args.stats,
                    args.verbose,
                    args.no_llm,
                )
            )
            print("\n✅ 批量处理完成！")
            print(f"📁 输入目录: {args.input_dir}")
            if args.output:
                print(f"📁 输出目录: {args.output}")
            print(f"📊 成功处理: {success_count} 个文件")
            if success_count == 0:
                logger.error("没有文件被成功处理")
                sys.exit(1)
    except KeyboardInterrupt:
        logger.info("用户中断操作")
        sys.exit(1)
    except Exception as e:
        logger.error(f"处理失败: {e}")
        if args.verbose:
            logger.exception("详细错误信息:")
        sys.exit(1)


if __name__ == "__main__":
    main()
