#!/usr/bin/env python3
"""
Word document (.docx) to Markdown converter for WeChat workflow.
Pure Python standard library + python-docx (pip install python-docx).

Usage:
    python docx_to_md.py input.docx -o output.md
    python docx_to_md.py input.docx --clipboard

Extracts: headings, paragraphs, lists, tables, bold/italic, images (saves to img/ folder).
"""

import argparse
import os
import sys
import zipfile
import re
import xml.etree.ElementTree as ET
from pathlib import Path

# Word XML namespaces
NS = {
    "w": "http://schemas.openxmlformats.org/wordprocessingml/2006/main",
    "r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
    "wp": "http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing",
    "a": "http://schemas.openxmlformats.org/drawingml/2006/main",
}


def extract_docx(docx_path):
    """Extract content from .docx file using ZIP + XML parsing (no python-docx needed)."""
    docx_path = Path(docx_path)
    output_dir = docx_path.parent / docx_path.stem
    
    with zipfile.ZipFile(docx_path, "r") as z:
        # Read document.xml
        doc_xml = z.read("word/document.xml")
        
        # Read relationships (for images)
        rels_xml = z.read("word/_rels/document.xml.rels") if "word/_rels/document.xml.rels" in z.namelist() else None
        
        # Extract images
        image_map = {}
        if rels_xml:
            rels_root = ET.fromstring(rels_xml)
            for rel in rels_root:
                rel_id = rel.get("Id")
                target = rel.get("Target")
                if target and "media/" in target:
                    image_map[rel_id] = target
        
        # Parse document
        root = ET.fromstring(doc_xml)
        body = root.find(f"{{{NS['w']}}}body")
        
        md_lines = []
        img_counter = 0
        img_dir = output_dir / "images"
        img_dir.mkdir(parents=True, exist_ok=True)
        
        for para in body.iter(f"{{{NS['w']}}}p"):
            # Get paragraph style
            ppr = para.find(f"{{{NS['w']}}}pPr")
            style_name = ""
            if ppr is not None:
                pstyle = ppr.find(f"{{{NS['w']}}}pStyle")
                if pstyle is not None:
                    style_name = pstyle.get(f"{{{NS['w']}}}val", "")
            
            # Get text and formatting
            text_parts = []
            for run in para.iter(f"{{{NS['w']}}}r"):
                rpr = run.find(f"{{{NS['w']}}}rPr")
                is_bold = False
                is_italic = False
                if rpr is not None:
                    if rpr.find(f"{{{NS['w']}}}b") is not None:
                        is_bold = True
                    if rpr.find(f"{{{NS['w']}}}i") is not None:
                        is_italic = False
                
                # Get text
                for t in run.iter(f"{{{NS['w']}}}t"):
                    if t.text:
                        prefix = ""
                        suffix = ""
                        if is_bold:
                            prefix += "**"
                            suffix = "**" + suffix
                        if is_italic:
                            prefix += "*"
                            suffix = "*" + suffix
                        text_parts.append(prefix + t.text + suffix)
                
                # Check for images
                for drawing in run.iter(f"{{{NS['wp']}}}inline"):
                    blip = drawing.find(f".//{{{NS['a']}}}blip")
                    if blip is not None:
                        embed = blip.get(f"{{{NS['r']}}}embed")
                        if embed and embed in image_map:
                            img_path = image_map[embed]
                            img_counter += 1
                            # Extract image
                            try:
                                img_data = z.read(f"word/{img_path}")
                                ext = Path(img_path).suffix
                                local_path = img_dir / f"image_{img_counter}{ext}"
                                local_path.write_bytes(img_data)
                                text_parts.append(f"\n![图片{img_counter}]({local_path})\n")
                            except Exception:
                                pass
            
            line = "".join(text_parts).strip()
            
            if not line:
                # Check for images in this paragraph
                has_image = False
                for drawing in para.iter(f"{{{NS['wp']}}}inline"):
                    has_image = True
                if not has_image:
                    md_lines.append("")
                continue
            
            # Determine heading level
            if "Heading1" in style_name or "heading 1" in style_name.lower():
                md_lines.append(f"# {line}")
            elif "Heading2" in style_name or "heading 2" in style_name.lower():
                md_lines.append(f"## {line}")
            elif "Heading3" in style_name or "heading 3" in style_name.lower():
                md_lines.append(f"### {line}")
            elif "Heading4" in style_name or "heading 4" in style_name.lower():
                md_lines.append(f"#### {line}")
            elif "ListParagraph" in style_name:
                # Check if it's numbered or bulleted
                numpr = ppr.find(f"{{{NS['w']}}}numPr") if ppr is not None else None
                if numpr is not None:
                    md_lines.append(f"1. {line}")
                else:
                    md_lines.append(f"- {line}")
            elif line.startswith("·") or line.startswith("•"):
                md_lines.append(f"- {line[1:].strip()}")
            else:
                md_lines.append(line)
        
        return "\n".join(md_lines), output_dir


def main():
    parser = argparse.ArgumentParser(description="Convert .docx to Markdown")
    parser.add_argument("input", help="Input .docx file path")
    parser.add_argument("-o", "--output", help="Output .md file path")
    parser.add_argument("--clipboard", action="store_true", help="Copy to clipboard")
    args = parser.parse_args()
    
    if not os.path.exists(args.input):
        print(f"Error: File not found: {args.input}")
        sys.exit(1)
    
    md_content, output_dir = extract_docx(args.input)
    
    # Determine output path
    if args.output:
        output_path = args.output
    else:
        output_path = str(Path(args.input).with_suffix(".md"))
    
    # Write output
    with open(output_path, "w", encoding="utf-8") as f:
        f.write(md_content)
    
    print(f"Markdown written to: {output_path}")
    print(f"Images saved to: {output_dir}/images/")
    print(f"Content length: ~{len(md_content)} characters")
    
    if args.clipboard:
        try:
            import subprocess
            subprocess.run(["clip"], input=md_content.encode("utf-8"), check=True)
            print("Copied to clipboard!")
        except Exception:
            print("Clipboard copy failed (not on Windows?)")


if __name__ == "__main__":
    main()