#!/usr/bin/env python3
"""
批量处理图片/PDF文件并生成符合评测要求的预测结果（PP-StructureV3版本）

根据 OmniDocBench 评测要求：
- 输入：支持 PDF 和各种图片格式（统一使用 --input 参数）
- 输出：每个文件对应的 .md、.json 文件，所有图片保存为单独的图片文件
- 调用方式：通过 PaddleX Pipeline 处理

使用方法：
    python main.py --input document.pdf --output_dir ./output
    python main.py --input ./images/ --output_dir ./output
    python main.py --input file_list.txt --output_dir ./output
    python main.py --input results.csv --output_dir ./output --dry_run
"""

import os
import sys
import json
import time
import traceback
from pathlib import Path
from typing import List, Dict, Any
from tqdm import tqdm
import argparse

from loguru import logger

# 导入 ocr_utils
ocr_platform_root = Path(__file__).parents[2]
if str(ocr_platform_root) not in sys.path:
    sys.path.insert(0, str(ocr_platform_root))

from ocr_utils import (
    get_input_files,
    collect_pid_files,
    setup_logging
)

# 导入共享处理器
tools_root = Path(__file__).parents[1] 
if str(tools_root) not in sys.path:
    sys.path.insert(0, str(tools_root))

try:
    from paddle_common.processor import PaddleXProcessor
except ImportError:
    raise ImportError(f"Failed to import PaddleXProcessor from [{tools_root}]/paddle_common.processor")


def process_images_single_process(
    image_paths: List[str],
    processor: PaddleXProcessor,
    batch_size: int = 1,
    output_dir: str = "./output"
) -> List[Dict[str, Any]]:
    """
    单进程版本的图像处理函数
    
    Args:
        image_paths: 图像文件路径列表
        processor: PaddleX 处理器实例
        batch_size: 批次大小（PaddleX 通常单张处理，此参数保留用于兼容）
        output_dir: 输出目录
        
    Returns:
        处理结果列表
    """
    # 创建输出目录
    output_path = Path(output_dir)
    output_path.mkdir(parents=True, exist_ok=True)
    
    all_results = []
    total_images = len(image_paths)
    
    logger.info(f"Processing {total_images} images")
    
    with tqdm(total=total_images, desc="Processing images", unit="img", 
              bar_format='{l_bar}{bar}| {n_fmt}/{total_fmt} [{elapsed}<{remaining}, {rate_fmt}]') as pbar:
        
        for img_path in image_paths:
            try:
                result = processor.process_single_image(img_path, output_dir)
                all_results.append(result)
                
                # 更新进度条
                success_count = sum(1 for r in all_results if r.get('success', False))
                skipped_count = sum(1 for r in all_results if r.get('skipped', False))
                
                pbar.update(1)
                pbar.set_postfix({
                    'time': f"{result.get('processing_time', 0):.2f}s",
                    'success': f"{success_count}/{len(all_results)}",
                    'skipped': f"{skipped_count}",
                    'rate': f"{success_count/len(all_results)*100:.1f}%" if len(all_results) > 0 else "0%"
                })
                
            except Exception as e:
                logger.error(f"Error processing {img_path}: {e}")
                all_results.append({
                    "image_path": img_path,
                    "processing_time": 0,
                    "success": False,
                    "device": processor.device,
                    "error": str(e)
                })
                pbar.update(1)
    
    return all_results


def main():
    """主函数"""
    parser = argparse.ArgumentParser(
        description="PP-StructureV3 Batch Processing",
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog="""
示例:
  # 处理单个PDF文件
  python main.py --input document.pdf --output_dir ./output
  
  # 处理图片目录
  python main.py --input ./images/ --output_dir ./output
  
  # 处理文件列表
  python main.py --input file_list.txt --output_dir ./output
  
  # 处理CSV文件（失败的文件）
  python main.py --input results.csv --output_dir ./output
  
  # 指定页面范围（仅PDF）
  python main.py --input document.pdf --output_dir ./output --pages "1-5,7"
  
  # 仅验证配置（dry run）
  python main.py --input document.pdf --output_dir ./output --dry_run
  
  # 使用 DEBUG 日志级别获取详细错误信息
  python main.py --input document.pdf --output_dir ./output --log_level DEBUG
        """
    )
    
    # 输入参数（统一使用 --input）
    parser.add_argument(
        "--input", "-i",
        required=True,
        type=str,
        help="输入路径（支持PDF文件、图片文件、图片目录、文件列表.txt、CSV文件）"
    )
    
    # 输出参数
    parser.add_argument(
        "--output_dir", "-o",
        type=str,
        required=True,
        help="输出目录"
    )
    
    # PaddleX Pipeline 参数
    parser.add_argument(
        "--pipeline",
        type=str,
        default="PP-StructureV3",
        help="Pipeline 名称或配置文件路径（默认: PP-StructureV3）"
    )
    parser.add_argument(
        "--device",
        type=str,
        default="gpu:0",
        help="设备字符串（如 'gpu:0', 'cpu'，默认: gpu:0）"
    )
    parser.add_argument(
        "--pdf_dpi",
        type=int,
        default=200,
        help="PDF 转图片的 DPI（默认: 200）"
    )
    parser.add_argument(
        '--no-normalize',
        action='store_true',
        help='禁用数字标准化'
    )
    parser.add_argument(
        '--no-adapter',
        action='store_true',
        help='禁用增强适配器'
    )
    
    # 处理参数
    parser.add_argument(
        "--batch_size",
        type=int,
        default=1,
        help="Batch size（PaddleX 通常单张处理，此参数保留用于兼容）"
    )
    parser.add_argument(
        "--pages", "-p",
        type=str,
        help="页面范围（PDF和图片目录有效），如: '1-5,7,9-12', '1-', '-10'"
    )
    parser.add_argument(
        "--collect_results",
        type=str,
        help="收集处理结果到指定CSV文件"
    )
    
    # 日志参数
    parser.add_argument(
        "--log_level",
        default="INFO",
        choices=["DEBUG", "INFO", "WARNING", "ERROR"],
        help="日志级别（默认: INFO）"
    )
    parser.add_argument(
        "--log_file",
        type=str,
        help="日志文件路径"
    )
    
    # Dry run 参数
    parser.add_argument(
        "--dry_run",
        action="store_true",
        help="仅验证配置和输入，不执行实际处理"
    )
    
    args = parser.parse_args()
    
    # 设置日志
    setup_logging(args.log_level, args.log_file)
    
    try:
        # 创建参数对象（用于 get_input_files）
        class Args:
            def __init__(self, input_path, output_dir, pdf_dpi):
                self.input = input_path
                self.output_dir = output_dir
                self.pdf_dpi = pdf_dpi
        
        args_obj = Args(args.input, args.output_dir, args.pdf_dpi)
        
        # 获取并预处理输入文件（页面范围过滤已在 get_input_files 中处理）
        logger.info("🔄 Preprocessing input files...")
        if args.pages:
            logger.info(f"📄 页面范围: {args.pages}")
        image_files = get_input_files(args_obj, page_range=args.pages)
        
        if not image_files:
            logger.error("❌ No input files found or processed")
            return 1
        
        output_dir = Path(args.output_dir).resolve()
        logger.info(f"📁 Output dir: {output_dir}")
        logger.info(f"📊 Found {len(image_files)} image files to process")
        
        # Dry run 模式
        if args.dry_run:
            logger.info("🔍 Dry run mode: 仅验证配置，不执行处理")
            logger.info(f"📋 配置信息:")
            logger.info(f"  - 输入: {args.input}")
            logger.info(f"  - 输出目录: {output_dir}")
            logger.info(f"  - Pipeline: {args.pipeline}")
            logger.info(f"  - 设备: {args.device}")
            logger.info(f"  - 批次大小: {args.batch_size}")
            logger.info(f"  - PDF DPI: {args.pdf_dpi}")
            logger.info(f"  - 数字标准化: {not args.no_normalize}")
            logger.info(f"  - 增强适配器: {not args.no_adapter}")
            logger.info(f"  - 日志级别: {args.log_level}")
            if args.pages:
                logger.info(f"  - 页面范围: {args.pages}")
            logger.info(f"📋 将要处理的文件 ({len(image_files)} 个):")
            for i, img_file in enumerate(image_files[:20], 1):  # 只显示前20个
                logger.info(f"  {i}. {img_file}")
            if len(image_files) > 20:
                logger.info(f"  ... 还有 {len(image_files) - 20} 个文件")
            logger.info("✅ Dry run 完成：配置验证通过")
            return 0
        
        logger.info(f"🔧 Using pipeline: {args.pipeline}")
        logger.info(f"🖥️  Using device: {args.device}")
        logger.info(f"📦 Batch size: {args.batch_size}")
        
        # 创建处理器
        processor = PaddleXProcessor(
            pipeline_name=args.pipeline,
            device=args.device,
            normalize_numbers=not args.no_normalize,
            use_enhanced_adapter=not args.no_adapter,
            log_level=args.log_level
        )
        
        # 开始处理
        start_time = time.time()
        results = process_images_single_process(
            image_files,
            processor,
            args.batch_size,
            str(output_dir)
        )
        
        total_time = time.time() - start_time
        
        # 统计结果
        success_count = sum(1 for r in results if r.get('success', False))
        skipped_count = sum(1 for r in results if r.get('skipped', False))
        error_count = len(results) - success_count
        pdf_page_count = sum(1 for r in results if r.get('is_pdf_page', False))
        
        # 统计标准化信息
        total_changes = sum(r.get('processing_info', {}).get('character_changes_count', 0) for r in results if 'processing_info' in r)
        
        print(f"\n" + "="*60)
        print(f"✅ Processing completed!")
        print(f"📊 Statistics:")
        print(f"  Total files processed: {len(image_files)}")
        print(f"  PDF pages processed: {pdf_page_count}")
        print(f"  Regular images processed: {len(image_files) - pdf_page_count}")
        print(f"  Successful: {success_count}")
        print(f"  Skipped: {skipped_count}")
        print(f"  Failed: {error_count}")
        if len(image_files) > 0:
            print(f"  Success rate: {success_count / len(image_files) * 100:.2f}%")
        if not args.no_normalize and total_changes > 0:
            print(f"  总标准化字符数: {total_changes}")
        
        print(f"⏱️ Performance:")
        print(f"  Total time: {total_time:.2f} seconds")
        if total_time > 0:
            print(f"  Throughput: {len(image_files) / total_time:.2f} images/second")
            print(f"  Avg time per image: {total_time / len(image_files):.2f} seconds")
        
        print(f"\n📁 Output Structure:")
        print(f"  output_dir/")
        print(f"  ├── filename.md              # Markdown content")
        print(f"  ├── filename.json            # Content list JSON")
        print(f"  └── filename_*.jpg           # Output images")

        # 保存结果统计
        stats = {
            "total_files": len(image_files),
            "pdf_pages": pdf_page_count,
            "regular_images": len(image_files) - pdf_page_count,
            "success_count": success_count,
            "skipped_count": skipped_count,
            "error_count": error_count,
            "success_rate": success_count / len(image_files) if len(image_files) > 0 else 0,
            "total_time": total_time,
            "throughput": len(image_files) / total_time if total_time > 0 else 0,
            "avg_time_per_image": total_time / len(image_files) if len(image_files) > 0 else 0,
            "batch_size": args.batch_size,
            "device": args.device,
            "pipeline": args.pipeline,
            "pdf_dpi": args.pdf_dpi,
            "normalization_enabled": not args.no_normalize,
            "adapter_enabled": not args.no_adapter,
            "total_character_changes": total_changes,
            "timestamp": time.strftime("%Y-%m-%d %H:%M:%S")
        }
        
        # 保存最终结果
        output_file_name = Path(output_dir).name
        output_file = output_dir / f"{output_file_name}_results.json"
        final_results = {
            "stats": stats,
            "results": results
        }
        
        with open(output_file, 'w', encoding='utf-8') as f:
            json.dump(final_results, f, ensure_ascii=False, indent=2)
        
        logger.info(f"💾 Results saved to: {output_file}")

        # 收集处理结果
        if not args.collect_results:
            output_file_processed = output_dir / f"processed_files_{time.strftime('%Y%m%d_%H%M%S')}.csv"
        else:
            output_file_processed = Path(args.collect_results).resolve()
            
        processed_files = collect_pid_files(str(output_file))
        with open(output_file_processed, 'w', encoding='utf-8') as f:
            f.write("image_path,status\n")
            for file_path, status in processed_files:
                f.write(f"{file_path},{status}\n")
        logger.info(f"💾 Processed files saved to: {output_file_processed}")

        return 0
        
    except Exception as e:
        logger.error(f"Processing failed: {e}")
        traceback.print_exc()
        return 1


if __name__ == "__main__":
    logger.info(f"🚀 启动PP-StructureV3统一PDF/图像处理程序...")
    logger.info(f"🔧 CUDA_VISIBLE_DEVICES: {os.environ.get('CUDA_VISIBLE_DEVICES', 'Not set')}")
    
    if len(sys.argv) == 1:
        # 如果没有命令行参数，使用默认配置运行
        logger.info("ℹ️  No command line arguments provided. Running with default configuration...")
        
        # 默认配置（PP-StructureV3）
        default_config = {
            "input": "/Users/zhch158/workspace/data/流水分析/马公账流水_工商银行.pdf",
            "output_dir": "./output",
            "pipeline": "../paddle_common/config/PP-StructureV3-zhch.yaml",  # 默认使用 PP-StructureV3
            "device": "cpu",
            "pdf_dpi": "200",
            "pages": "-1",
            "log_level": "DEBUG",
        }
        
        # 构造参数
        sys.argv = [sys.argv[0]]
        for key, value in default_config.items():
            sys.argv.extend([f"--{key}", str(value)])
    
    sys.exit(main())

