"""
Standalone function to add comments to a .docx file using zipfile + lxml.
Use AFTER ContractEditor.save() since ContractEditor rewrites document.xml.

Usage:
    from add_comments_to_docx import add_comments_to_docx
    # comments = [(anchor_text, comment_text), ...]
    placed = add_comments_to_docx('/tmp/【修】contract.docx', comments)
"""
import zipfile, io
from datetime import datetime
from lxml import etree

W = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
R_NS = 'http://schemas.openxmlformats.org/officeDocument/2006/relationships'

def qn(tag):
    return f'{{{W}}}{tag}'

def add_comments_to_docx(filepath, comments, author='WB'):
    """Add comments to a docx file.
    
    Args:
        filepath: path to docx (modified in place)
        comments: list of (anchor_text, comment_text) tuples
        author: comment author name (default 'WB')
    
    Returns:
        int: number of comments successfully placed
    """
    with open(filepath, 'rb') as f:
        data = f.read()
    zin = zipfile.ZipFile(io.BytesIO(data))
    buf = io.BytesIO()
    zout = zipfile.ZipFile(buf, 'w', zipfile.ZIP_DEFLATED)
    doc_xml = zin.read('word/document.xml')
    doc_tree = etree.fromstring(doc_xml)
    body = doc_tree.find(qn('body'))
    
    nsmap = {'w': W, 'r': R_NS}
    comments_xml = etree.Element(qn('comments'), nsmap=nsmap)
    
    comment_id = 200
    placed = 0
    
    for anchor_text, comment_text in comments:
        cid = str(comment_id)
        comment_id += 1
        
        # Create comment element
        comment_el = etree.SubElement(comments_xml, qn('comment'))
        comment_el.set(qn('id'), cid)
        comment_el.set(qn('author'), author)
        comment_el.set(qn('date'), datetime.now().strftime('%Y-%m-%dT%H:%M:%SZ'))
        cp = etree.SubElement(comment_el, qn('p'))
        cr = etree.SubElement(cp, qn('r'))
        ct = etree.SubElement(cr, qn('t'))
        ct.set('{http://www.w3.org/XML/1998/namespace}space', 'preserve')
        ct.text = comment_text
        
        # Find anchor in document
        for p in body.iter(qn('p')):
            runs = p.findall(f'.//{qn("r")}')
            full = ''.join(''.join(t.text or '' for t in r.findall(qn('t'))) for r in runs)
            if anchor_text in full:
                # Insert commentRangeStart at beginning of paragraph
                crs = etree.Element(qn('commentRangeStart'))
                crs.set(qn('id'), cid)
                p.insert(0, crs)
                
                # Append commentRangeEnd + reference
                cre = etree.Element(qn('commentRangeEnd'))
                cre.set(qn('id'), cid)
                p.append(cre)
                
                ref_run = etree.SubElement(p, qn('r'))
                ref_rpr = etree.SubElement(ref_run, qn('rPr'))
                ref_style = etree.SubElement(ref_rpr, qn('rStyle'))
                ref_style.set(qn('val'), 'CommentReference')
                ref_cr = etree.SubElement(ref_run, qn('commentReference'))
                ref_cr.set(qn('id'), cid)
                
                placed += 1
                break
    
    # Rewrite zip
    for item in zin.namelist():
        if item == 'word/document.xml':
            zout.writestr(item, etree.tostring(doc_tree, xml_declaration=True, encoding='UTF-8', standalone=True))
        elif item == '[Content_Types].xml':
            ct_xml = zin.read(item)
            ct_tree = etree.fromstring(ct_xml)
            if not any('comments.xml' in (el.get('PartName') or '') for el in ct_tree):
                override = etree.SubElement(ct_tree, 'Override')
                override.set('PartName', '/word/comments.xml')
                override.set('ContentType', 'application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml')
            zout.writestr(item, etree.tostring(ct_tree, xml_declaration=True, encoding='UTF-8', standalone=True))
        elif item == 'word/_rels/document.xml.rels':
            rels_xml = zin.read(item)
            rels_tree = etree.fromstring(rels_xml)
            if not any('comments.xml' in (el.get('Target') or '') for el in rels_tree):
                rel = etree.SubElement(rels_tree, 'Relationship')
                rel.set('Id', f'rId{len(rels_tree) + 10}')
                rel.set('Type', 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments')
                rel.set('Target', 'comments.xml')
            zout.writestr(item, etree.tostring(rels_tree, xml_declaration=True, encoding='UTF-8', standalone=True))
        else:
            zout.writestr(item, zin.read(item))
    
    zout.writestr('word/comments.xml', etree.tostring(comments_xml, xml_declaration=True, encoding='UTF-8', standalone=True))
    zout.close()
    zin.close()
    
    with open(filepath, 'wb') as f:
        f.write(buf.getvalue())
    return placed
