#!/usr/bin/env python3
"""
文档转 PDF 工具
使用 LibreOffice 将各种文档格式转换为 PDF

支持格式：
- Word: .doc, .docx, .rtf
- Excel: .xls, .xlsx
- PowerPoint: .ppt, .pptx
- OpenDocument: .odt, .ods, .odp
- 文本: .txt, .csv
- 图片: .jpg, .png (会嵌入PDF)
"""

import subprocess
import os
import sys
from pathlib import Path
from typing import Optional, List
import concurrent.futures


class Doc2PDF:
    """文档转 PDF 转换器"""
    
    SUPPORTED_FORMATS = {
        # 文档
        '.doc', '.docx', '.rtf', '.odt', '.txt',
        # 表格
        '.xls', '.xlsx', '.ods', '.csv',
        # 演示文稿
        '.ppt', '.pptx', '.odp',
        # 图片
        '.jpg', '.jpeg', '.png', '.bmp', '.gif',
        # 其他
        '.html', '.htm', '.xml'
    }
    
    def __init__(self, libreoffice_path: str = 'libreoffice'):
        self.libreoffice_path = libreoffice_path
        self._verify_libreoffice()
    
    def _verify_libreoffice(self):
        """验证 LibreOffice 是否可用"""
        try:
            result = subprocess.run(
                [self.libreoffice_path, '--version'],
                capture_output=True, text=True
            )
            if result.returncode != 0:
                raise RuntimeError("LibreOffice 不可用")
            print(f"✓ 检测到 {result.stdout.strip().split(chr(10))[0]}")
        except FileNotFoundError:
            raise RuntimeError("未找到 LibreOffice，请先安装")
    
    def convert(
        self, 
        input_file: str, 
        output_dir: Optional[str] = None,
        timeout: int = 120
    ) -> str:
        """
        转换单个文件为 PDF
        
        Args:
            input_file: 输入文件路径
            output_dir: 输出目录（默认为输入文件所在目录）
            timeout: 超时时间（秒）
        
        Returns:
            生成的 PDF 文件路径
        """
        input_path = Path(input_file).resolve()
        
        if not input_path.exists():
            raise FileNotFoundError(f"文件不存在: {input_file}")
        
        suffix = input_path.suffix.lower()
        if suffix not in self.SUPPORTED_FORMATS:
            raise ValueError(f"不支持的格式: {suffix}")
        
        if output_dir is None:
            output_dir = str(input_path.parent)
        else:
            output_dir = str(Path(output_dir).resolve())
            os.makedirs(output_dir, exist_ok=True)
        
        cmd = [
            self.libreoffice_path,
            '--headless',
            '--invisible',
            '--nologo',
            '--nofirststartwizard',
            '--convert-to', 'pdf',
            '--outdir', output_dir,
            str(input_path)
        ]
        
        print(f"  转换中: {input_path.name} ...", end=' ', flush=True)
        
        try:
            result = subprocess.run(
                cmd, 
                capture_output=True, 
                text=True, 
                timeout=timeout
            )
            
            if result.returncode != 0:
                print("✗")
                raise RuntimeError(f"转换失败: {result.stderr}")
            
            output_file = os.path.join(output_dir, f"{input_path.stem}.pdf")
            
            if os.path.exists(output_file):
                print("✓")
                return output_file
            else:
                print("✗")
                raise RuntimeError("转换完成但未找到输出文件")
                
        except subprocess.TimeoutExpired:
            print("✗ (超时)")
            raise RuntimeError(f"转换超时 ({timeout}秒)")
    
    def convert_batch(
        self, 
        input_files: List[str], 
        output_dir: Optional[str] = None,
        max_workers: int = 1  # LibreOffice 并发可能有问题，建议设为1
    ) -> List[dict]:
        """
        批量转换文件
        
        Args:
            input_files: 输入文件列表
            output_dir: 输出目录
            max_workers: 并发数
        
        Returns:
            转换结果列表
        """
        results = []
        
        print(f"\n开始批量转换 {len(input_files)} 个文件...\n")
        
        for i, input_file in enumerate(input_files, 1):
            print(f"[{i}/{len(input_files)}]", end='')
            try:
                output_file = self.convert(input_file, output_dir)
                results.append({
                    'input': input_file,
                    'output': output_file,
                    'success': True,
                    'error': None
                })
            except Exception as e:
                results.append({
                    'input': input_file,
                    'output': None,
                    'success': False,
                    'error': str(e)
                })
        
        # 打印统计
        success_count = sum(1 for r in results if r['success'])
        print(f"\n转换完成: {success_count}/{len(results)} 成功")
        
        return results
    
    def convert_directory(
        self, 
        input_dir: str, 
        output_dir: Optional[str] = None,
        recursive: bool = False
    ) -> List[dict]:
        """
        转换目录中的所有支持文件
        
        Args:
            input_dir: 输入目录
            output_dir: 输出目录
            recursive: 是否递归处理子目录
        """
        input_path = Path(input_dir)
        
        if not input_path.is_dir():
            raise NotADirectoryError(f"不是目录: {input_dir}")
        
        # 收集所有支持的文件
        files = []
        pattern = '**/*' if recursive else '*'
        
        for file_path in input_path.glob(pattern):
            if file_path.is_file() and file_path.suffix.lower() in self.SUPPORTED_FORMATS:
                files.append(str(file_path))
        
        if not files:
            print("未找到支持的文件")
            return []
        
        return self.convert_batch(files, output_dir)


def main():
    """命令行入口"""
    import argparse
    
    parser = argparse.ArgumentParser(
        description='使用 LibreOffice 将文档转换为 PDF',
        formatter_class=argparse.RawDescriptionHelpFormatter,
        epilog='''
示例:
  python doc2pdf.py document.docx
  python doc2pdf.py document.xlsx -o ./output
  python doc2pdf.py ./documents/ -o ./pdfs -r
        '''
    )
    
    parser.add_argument('input', help='输入文件或目录')
    parser.add_argument('-o', '--output', help='输出目录')
    parser.add_argument('-r', '--recursive', action='store_true', 
                        help='递归处理子目录')
    parser.add_argument('-t', '--timeout', type=int, default=120,
                        help='单个文件转换超时时间（秒）')
    
    args = parser.parse_args()
    
    converter = Doc2PDF()
    
    input_path = Path(args.input)
    
    if input_path.is_file():
        # 单文件转换
        try:
            output = converter.convert(args.input, args.output, args.timeout)
            print(f"\n输出文件: {output}")
        except Exception as e:
            print(f"\n错误: {e}")
            sys.exit(1)
    
    elif input_path.is_dir():
        # 目录转换
        results = converter.convert_directory(
            args.input, 
            args.output, 
            args.recursive
        )
        
        # 显示失败的文件
        failures = [r for r in results if not r['success']]
        if failures:
            print("\n失败的文件:")
            for f in failures:
                print(f"  - {f['input']}: {f['error']}")
    
    else:
        print(f"错误: 路径不存在 - {args.input}")
        sys.exit(1)


if __name__ == '__main__':
    main()
