from pathlib import Path
import re, shutil, zipfile
from docx import Document
from docx.shared import Cm, Pt
from docx.enum.text import WD_BREAK
from docx.oxml import OxmlElement
from docx.oxml.ns import qn

SRC = Path('/home/agent/.hermes/cache/documents/doc_6fda631bdff2_260623_Moenchaltorf_Protokoll_1_Fachplaner_Jourfixe_ERNE_CD_optimiert.docx')
OUT = Path('/home/agent/outputs/moenchaltorf_jourfix_260623/260623_Moenchaltorf_Protokoll_1_Fachplaner_Jourfixe_ERNE_CD_final_v2.docx')

TRACT_RE = re.compile(r'^(?:[1-9]|1[0-3])\s+.+')
MAJOR_HEADINGS = {
    'TEILNEHMENDE','MANAGEMENT SUMMARY','AMPELSTATUS','TRAKTANDEN / PROTOKOLL',
    'ENTSCHEIDE','OFFENE FRAGEN','RISIKEN UND CHANCEN','PENDENZENLISTE','PUNKTE FÜR NÄCHSTE SITZUNG'
}
REMOVE_HEADINGS = {'KURZVERSION ZUM WEITERLEITEN'}


def set_keep(paragraph, together=True, with_next=False, page_break_before=False):
    pf = paragraph.paragraph_format
    pf.keep_together = together
    pf.keep_with_next = with_next
    pf.page_break_before = page_break_before


def set_spacing(paragraph, before=None, after=None, line=1.0):
    pf = paragraph.paragraph_format
    if before is not None: pf.space_before = Pt(before)
    if after is not None: pf.space_after = Pt(after)
    pf.line_spacing = line


def add_bottom_border(paragraph, color='365294', size='8'):
    pPr = paragraph._p.get_or_add_pPr()
    # remove existing pBdr to avoid duplicates
    for old in pPr.findall(qn('w:pBdr')):
        pPr.remove(old)
    pBdr = OxmlElement('w:pBdr')
    bottom = OxmlElement('w:bottom')
    bottom.set(qn('w:val'), 'single')
    bottom.set(qn('w:sz'), size)
    bottom.set(qn('w:space'), '4')
    bottom.set(qn('w:color'), color)
    pBdr.append(bottom); pPr.append(pBdr)


def remove_paragraph(paragraph):
    el = paragraph._element
    el.getparent().remove(el)


def set_cant_split(row):
    trPr = row._tr.get_or_add_trPr()
    if trPr.find(qn('w:cantSplit')) is None:
        trPr.append(OxmlElement('w:cantSplit'))


def set_run_font(paragraph, size_pt, bold=None):
    for run in paragraph.runs:
        run.font.name = 'Arial'
        run._element.rPr.rFonts.set(qn('w:eastAsia'), 'Arial')
        run.font.size = Pt(size_pt)
        if bold is not None:
            run.font.bold = bold


def remove_table_column(table, idx):
    # Remove grid column where present.
    tblGrid = table._tbl.tblGrid
    if tblGrid is not None and idx < len(tblGrid.gridCol_lst):
        tblGrid.remove(tblGrid.gridCol_lst[idx])
    for row in table.rows:
        if idx < len(row.cells):
            tc = row.cells[idx]._tc
            tc.getparent().remove(tc)


def set_table_full_width(table):
    tblPr = table._tbl.tblPr
    tblW = tblPr.find(qn('w:tblW'))
    if tblW is None:
        tblW = OxmlElement('w:tblW')
        tblPr.append(tblW)
    tblW.set(qn('w:type'), 'pct')
    tblW.set(qn('w:w'), '5000')
    table.autofit = True


def main():
    doc = Document(str(SRC))
    sec = doc.sections[0]
    # More breathing room from header/footer and page edges.
    sec.top_margin = Cm(2.35)
    sec.bottom_margin = Cm(1.85)
    sec.left_margin = Cm(1.55)
    sec.right_margin = Cm(1.35)
    sec.header_distance = Cm(0.85)
    sec.footer_distance = Cm(0.85)

    # Remove optional forwarding-summary section if present.
    paras = list(doc.paragraphs)
    remove = False
    for p in paras:
        txt = p.text.strip()
        if txt in REMOVE_HEADINGS:
            remove = True
            remove_paragraph(p)
            continue
        if remove and (txt in MAJOR_HEADINGS or TRACT_RE.match(txt)):
            remove = False
        if remove:
            remove_paragraph(p)

    paras = list(doc.paragraphs)

    # Normalise excessive empty paragraphs: keep at most one between blocks.
    consecutive_empty = 0
    for p in list(doc.paragraphs):
        if not p.text.strip():
            consecutive_empty += 1
            if consecutive_empty > 1:
                remove_paragraph(p)
        else:
            consecutive_empty = 0

    paras = list(doc.paragraphs)
    tract_indices = [i for i,p in enumerate(paras) if TRACT_RE.match(p.text.strip())]
    major_indices = [i for i,p in enumerate(paras) if p.text.strip() in MAJOR_HEADINGS]

    # Global font/paragraph spacing polish.
    for i,p in enumerate(paras):
        txt = p.text.strip()
        if not txt:
            set_spacing(p, before=0, after=4)
            continue
        if txt == 'PROTOKOLL':
            set_keep(p, together=True, with_next=True)
            set_spacing(p, before=8, after=2, line=1.0)
            set_run_font(p, 11, bold=True)
        elif txt == '1. FACHPLANER-JOUR-FIXE':
            set_keep(p, together=True, with_next=True)
            set_spacing(p, before=0, after=5, line=1.0)
            set_run_font(p, 16, bold=False)
        elif txt in MAJOR_HEADINGS:
            set_keep(p, together=True, with_next=True, page_break_before=(txt == 'RISIKEN UND CHANCEN'))
            set_spacing(p, before=16, after=7, line=1.0)
            set_run_font(p, 13, bold=True)
            add_bottom_border(p)
        elif TRACT_RE.match(txt):
            # Traktandum heading: stronger separation and keep with following content.
            set_keep(p, together=True, with_next=True)
            set_spacing(p, before=15, after=7, line=1.0)
            set_run_font(p, 13, bold=True)
        elif txt.startswith('•'):
            set_keep(p, together=True, with_next=False)
            set_spacing(p, before=0, after=3.2, line=1.08)
            set_run_font(p, 8.5)
        else:
            set_keep(p, together=True, with_next=False)
            set_spacing(p, before=0, after=3, line=1.05)
            set_run_font(p, 8.5)

    # Keep each Traktandum together as far as Word/LibreOffice can honour it.
    paras = list(doc.paragraphs)
    starts = [i for i,p in enumerate(paras) if TRACT_RE.match(p.text.strip())]
    for n,start in enumerate(starts):
        end = starts[n+1] if n+1 < len(starts) else len(paras)
        # stop at the next major section after the Traktanden block
        for j in range(start+1, end):
            if paras[j].text.strip() in MAJOR_HEADINGS:
                end = j
                break
        # keep all section paragraphs with next, except final paragraph
        for j in range(start, max(start, end-1)):
            set_keep(paras[j], together=True, with_next=True)
        if end-1 >= start:
            set_keep(paras[end-1], together=True, with_next=False)
            set_spacing(paras[end-1], after=11)

    # Remove less relevant Pendenzen column per user request: keep Relevanz, remove only Prio.
    for table in doc.tables:
        if not table.rows:
            continue
        headers = [c.text.strip() for c in table.rows[0].cells]
        if 'Prio' in headers or 'Priorität' in headers:
            idx = headers.index('Prio') if 'Prio' in headers else headers.index('Priorität')
            remove_table_column(table, idx)

    # Keep tables readable and harmonise font sizes: table titles 10 pt, all other
    # text 8.5 pt. Keep rows from splitting; move Risiko table to a new page via
    # heading setting above to avoid a page break inside it.
    for table in doc.tables:
        set_table_full_width(table)
        header_text = ' | '.join(c.text.strip() for c in table.rows[0].cells) if table.rows else ''
        is_pendenzen = 'Pendenz' in header_text and 'Status' in header_text and 'Termin' in header_text
        is_info_band = len(table.rows) == 1 and len(table.rows[0].cells) == 2
        for r_idx, row in enumerate(table.rows):
            set_cant_split(row)
            for c_idx, cell in enumerate(row.cells):
                # Moderate cell padding: enough air, but enough width for line wraps.
                tcPr = cell._tc.get_or_add_tcPr()
                tcMar = tcPr.find(qn('w:tcMar'))
                if tcMar is None:
                    tcMar = OxmlElement('w:tcMar'); tcPr.append(tcMar)
                for side in ['top','bottom','left','right']:
                    el = tcMar.find(qn(f'w:{side}'))
                    if el is None:
                        el = OxmlElement(f'w:{side}'); tcMar.append(el)
                    el.set(qn('w:w'), '70' if is_pendenzen else '85')
                    el.set(qn('w:type'), 'dxa')
                for p in cell.paragraphs:
                    p.paragraph_format.keep_together = True
                    p.paragraph_format.space_before = Pt(0)
                    p.paragraph_format.space_after = Pt(1 if r_idx == 0 else 0.5)
                    p.paragraph_format.line_spacing = 1.0
                    if is_info_band:
                        # Metadata bands: label/title 10 pt, value normal 8.5 pt.
                        is_label = c_idx == 0
                        set_run_font(p, 10 if is_label else 8.5, bold=True if is_label else None)
                    else:
                        set_run_font(p, 10 if r_idx == 0 else 8.5, bold=True if r_idx == 0 else None)

    doc.save(str(OUT))
    print(OUT)

if __name__ == '__main__':
    main()
