#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Word 文档目录更新工具（Linux 环境）
通过修改 XML 标记让 Word 打开时自动更新目录
"""

import zipfile
import os
import tempfile
import shutil
from lxml import etree
from io import BytesIO


class WordTocUpdater:
    """Word 文档目录更新工具类"""
    
    # XML 命名空间
    W_NS = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
    W = f'{{{W_NS}}}'
    
    def __init__(self, docx_path):
        """
        初始化 Word TOC 更新器
        
        Args:
            docx_path: Word 文档路径
        """
        self.docx_path = docx_path
        if not os.path.exists(docx_path):
            raise FileNotFoundError(f"文件不存在: {docx_path}")
    
    def mark_for_toc_update(self, output_path=None):
        """
        标记文档需要更新目录（用户打开时 Word 会提示）
        
        通过修改 settings.xml 添加 updateFields 设置
        
        Args:
            output_path: 输出路径，默认覆盖原文件
            
        Returns:
            bool: 是否成功
        """
        if output_path is None:
            output_path = self.docx_path
        
        try:
            # 创建临时目录
            temp_dir = tempfile.mkdtemp()
            temp_docx = os.path.join(temp_dir, 'document.docx')
            
            # 解压 docx 文件
            with zipfile.ZipFile(self.docx_path, 'r') as zip_ref:
                zip_ref.extractall(temp_dir)
            
            # 修改 settings.xml
            settings_path = os.path.join(temp_dir, 'word', 'settings.xml')
            self._update_settings_xml(settings_path)
            
            # 重新打包为 docx
            self._create_docx(temp_dir, output_path)
            
            # 清理临时目录
            shutil.rmtree(temp_dir)
            return True
            
        except Exception as e:
            print(f"❌ 标记失败: {str(e)}")
            # 确保清理临时目录
            if 'temp_dir' in locals():
                shutil.rmtree(temp_dir)
            return False
    
    def _update_settings_xml(self, settings_path):
        """
        修改 settings.xml，添加 updateFields 设置
        
        Args:
            settings_path: settings.xml 文件路径
        """
        # 解析 settings.xml
        tree = etree.parse(settings_path)
        root = tree.getroot()
        
        # 检查是否已存在 updateFields 元素
        existing = root.find(f'.//{self.W}updateFields')
        if existing is not None:
            # 已存在，只需确保值为 true
            existing.set(f'{self.W}val', 'true')
        else:
            # 创建新的 updateFields 元素
            update_fields = etree.Element(f'{self.W}updateFields')
            update_fields.set(f'{self.W}val', 'true')
            
            # 插入到根元素下（通常插入到前面）
            root.insert(0, update_fields)
        
        # 保存修改后的 settings.xml
        tree.write(
            settings_path,
            xml_declaration=True,
            encoding='UTF-8',
            standalone=True
        )
    
    def _create_docx(self, source_dir, output_path):
        """
        将目录重新打包为 docx 文件
        
        Args:
            source_dir: 源目录
            output_path: 输出文件路径
        """
        # 如果输出文件已存在，先删除
        if os.path.exists(output_path):
            os.remove(output_path)
        
        # 创建新的 zip 文件
        with zipfile.ZipFile(output_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
            for root, dirs, files in os.walk(source_dir):
                for file in files:
                    file_path = os.path.join(root, file)
                    # 计算相对路径
                    arcname = os.path.relpath(file_path, source_dir)
                    zipf.write(file_path, arcname)
    
    def update_toc_directly(self):
        """
        尝试直接更新目录内容
        注意：这只对简单的目录有效，复杂目录需要 Word 引擎
        """
        try:
            temp_dir = tempfile.mkdtemp()
            
            # 解压 docx
            with zipfile.ZipFile(self.docx_path, 'r') as zip_ref:
                zip_ref.extractall(temp_dir)
            
            # 修改 document.xml
            document_path = os.path.join(temp_dir, 'word', 'document.xml')
            
            # 读取并解析 document.xml
            tree = etree.parse(document_path)
            root = tree.getroot()
            
            # 查找并更新目录域
            toc_updated = self._update_toc_fields(root)
            
            if toc_updated:
                # 保存修改
                tree.write(
                    document_path,
                    xml_declaration=True,
                    encoding='UTF-8',
                    standalone=True
                )
                
                # 重新打包
                output_path = self.docx_path.replace('.docx', '_updated.docx')
                self._create_docx(temp_dir, output_path)

                return True
            else:
                print("ℹ️ 未找到可更新的目录内容")
                shutil.rmtree(temp_dir)
                return False
                
        except Exception as e:
            print(f"❌ 直接更新失败: {str(e)}")
            if 'temp_dir' in locals():
                shutil.rmtree(temp_dir)
            return False
    
    def _update_toc_fields(self, root):
        """
        更新文档中的目录域
        
        Args:
            root: document.xml 的根元素
            
        Returns:
            bool: 是否找到并更新了目录
        """
        # 查找所有 fldSimple 域（目录常用结构）
        fld_simple_list = root.findall(f'.//{self.W}fldSimple')
        
        # 查找所有 instr 域内容
        instr_list = root.findall(f'.//{self.W}instrText')
        
        updated = False
        
        for instr in instr_list:
            text = instr.text or ''
            if 'TOC' in text:
                updated = True
                # 注意：这里只是标记，实际页面编号需要 Word 计算
        
        return updated


def mark_toc_for_update(docx_path, output_path=None):
    """
    标记 Word 文档需要更新目录（简单接口）
    
    Args:
        docx_path: Word 文档路径
        output_path: 输出路径，默认覆盖原文件
        
    Returns:
        bool: 是否成功
    """
    updater = WordTocUpdater(docx_path)
    return updater.mark_for_toc_update(output_path)


def update_toc_in_document(docx_path):
    """
    尝试直接更新文档中的目录（高级功能）
    
    Args:
        docx_path: Word 文档路径
        
    Returns:
        bool: 是否成功
    """
    updater = WordTocUpdater(docx_path)
    return updater.update_toc_directly()


# 使用示例和测试
if __name__ == "__main__":
    import sys
    
    if len(sys.argv) > 1:
        docx_file = sys.argv[1]
        print(f"处理文件: {docx_file}")
        
        # 方法1：标记文档（推荐）
        print("\n=== 方法1：标记文档自动更新 ===")
        mark_toc_for_update(docx_file)
        
        # 方法2：尝试直接更新
        print("\n=== 方法2：尝试直接更新目录 ===")
        update_toc_in_document(docx_file)
        
    else:
        print("使用方法:")
        print("  python word_toc_updater.py <docx文件路径>")
        print("\n示例:")
        print("  python word_toc_updater.py report.docx")
