from docx import Document
from docx.shared import Mm, Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH, WD_BREAK
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
from docx.enum.section import WD_SECTION
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from docx.enum.style import WD_STYLE_TYPE
from docx.enum.text import WD_LINE_SPACING
from pathlib import Path

OUT = Path('/home/agent/exports/260820_Gasser_Abmahnung_an_Besteller_CI.docx')
LOGO = Path('/home/agent/projects/GasserWebsite/public/images/gasser-logo.png')
OUT.parent.mkdir(parents=True, exist_ok=True)

BORDEAUX = '7A1F2B'
ANTHRACITE = '1F2326'
STEEL = '596066'
LIGHT = 'E7E3DA'
YELLOW = 'FFF59D'


def set_cell_margins(cell, top=0, start=0, bottom=0, end=0):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    tcMar = tcPr.first_child_found_in('w:tcMar')
    if tcMar is None:
        tcMar = OxmlElement('w:tcMar')
        tcPr.append(tcMar)
    for m, v in [('top', top), ('start', start), ('bottom', bottom), ('end', end)]:
        node = tcMar.find(qn(f'w:{m}'))
        if node is None:
            node = OxmlElement(f'w:{m}')
            tcMar.append(node)
        node.set(qn('w:w'), str(v))
        node.set(qn('w:type'), 'dxa')


def remove_table_borders(table):
    tblPr = table._tbl.tblPr
    borders = tblPr.first_child_found_in('w:tblBorders')
    if borders is None:
        borders = OxmlElement('w:tblBorders')
        tblPr.append(borders)
    for edge in ('top', 'left', 'bottom', 'right', 'insideH', 'insideV'):
        tag = borders.find(qn(f'w:{edge}'))
        if tag is None:
            tag = OxmlElement(f'w:{edge}')
            borders.append(tag)
        tag.set(qn('w:val'), 'nil')


def shade_cell(cell, fill):
    tcPr = cell._tc.get_or_add_tcPr()
    shd = tcPr.find(qn('w:shd'))
    if shd is None:
        shd = OxmlElement('w:shd')
        tcPr.append(shd)
    shd.set(qn('w:fill'), fill)


def add_bottom_border(paragraph, color=BORDEAUX, size='12', space='5'):
    pPr = paragraph._p.get_or_add_pPr()
    pBdr = pPr.find(qn('w:pBdr'))
    if pBdr is None:
        pBdr = OxmlElement('w:pBdr')
        pPr.append(pBdr)
    bottom = OxmlElement('w:bottom')
    bottom.set(qn('w:val'), 'single')
    bottom.set(qn('w:sz'), size)
    bottom.set(qn('w:space'), space)
    bottom.set(qn('w:color'), color)
    pBdr.append(bottom)


def set_repeat_table_header(row):
    trPr = row._tr.get_or_add_trPr()
    tblHeader = OxmlElement('w:tblHeader')
    tblHeader.set(qn('w:val'), 'true')
    trPr.append(tblHeader)


def set_run_font(run, name='Arial', size=10.5, bold=None, color=ANTHRACITE, italic=None):
    run.font.name = name
    run._element.rPr.rFonts.set(qn('w:eastAsia'), name)
    run.font.size = Pt(size)
    run.font.color.rgb = RGBColor.from_string(color)
    if bold is not None:
        run.bold = bold
    if italic is not None:
        run.italic = italic


def set_paragraph_format(p, before=0, after=0, line=1.12, keep_next=False):
    pf = p.paragraph_format
    pf.space_before = Pt(before)
    pf.space_after = Pt(after)
    pf.line_spacing = line
    pf.keep_with_next = keep_next


def add_text(p, text, **kwargs):
    r = p.add_run(text)
    set_run_font(r, **kwargs)
    return r


def highlight_run(run, fill=YELLOW):
    shd = OxmlElement('w:shd')
    shd.set(qn('w:fill'), fill)
    run._r.get_or_add_rPr().append(shd)


def add_field(paragraph, instruction, fallback='1'):
    r = paragraph.add_run()
    fld_begin = OxmlElement('w:fldChar')
    fld_begin.set(qn('w:fldCharType'), 'begin')
    instr = OxmlElement('w:instrText')
    instr.set(qn('xml:space'), 'preserve')
    instr.text = instruction
    fld_sep = OxmlElement('w:fldChar')
    fld_sep.set(qn('w:fldCharType'), 'separate')
    txt = OxmlElement('w:t')
    txt.text = fallback
    fld_end = OxmlElement('w:fldChar')
    fld_end.set(qn('w:fldCharType'), 'end')
    for el in (fld_begin, instr, fld_sep, txt, fld_end):
        r._r.append(el)
    set_run_font(r, size=8, color=STEEL)


doc = Document()
sec = doc.sections[0]
sec.page_width = Mm(210)
sec.page_height = Mm(297)
sec.top_margin = Mm(18)
sec.bottom_margin = Mm(20)
sec.left_margin = Mm(28)
sec.right_margin = Mm(24)
sec.header_distance = Mm(8)
sec.footer_distance = Mm(8)

# Base style
normal = doc.styles['Normal']
normal.font.name = 'Arial'
normal._element.rPr.rFonts.set(qn('w:eastAsia'), 'Arial')
normal.font.size = Pt(10.5)
normal.font.color.rgb = RGBColor.from_string(ANTHRACITE)
normal.paragraph_format.space_after = Pt(0)
normal.paragraph_format.line_spacing = 1.12

# Header: full logo + CI rule
header = sec.header
hp = header.paragraphs[0]
hp.alignment = WD_ALIGN_PARAGRAPH.LEFT
hp.paragraph_format.space_after = Pt(5)
r = hp.add_run()
r.add_picture(str(LOGO), width=Mm(66))
add_bottom_border(hp, BORDEAUX, size='16', space='6')

# Top address/contact grid
info = doc.add_table(rows=1, cols=2)
info.alignment = WD_TABLE_ALIGNMENT.CENTER
info.autofit = False
info.columns[0].width = Mm(93)
info.columns[1].width = Mm(65)
remove_table_borders(info)
for c in info.rows[0].cells:
    set_cell_margins(c, top=0, start=0, bottom=0, end=0)
    c.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.TOP

left, right = info.rows[0].cells
p = left.paragraphs[0]
set_paragraph_format(p, after=3)
add_text(p, 'BESTELLER / BAUHERRSCHAFT', size=7.7, bold=True, color=BORDEAUX)
for i, text in enumerate(['[Firma / Name]', '[Kontaktperson]', '[Strasse / Nr.]', '[PLZ Ort]']):
    p = left.add_paragraph() if i or left.paragraphs[0].text else left.paragraphs[0]
    set_paragraph_format(p, after=1.5, line=1.0)
    rr = add_text(p, text, size=10.5)
    highlight_run(rr)

p = right.paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.LEFT
set_paragraph_format(p, after=3)
add_text(p, 'IHRE ANSPRECHPERSON', size=7.7, bold=True, color=BORDEAUX)
for i, (text, size, bold, color) in enumerate([
    ('Andreas Gasser', 10.5, True, ANTHRACITE),
    ('Geschäftsführer', 9.2, False, STEEL),
    ('077 268 25 29', 9.2, False, ANTHRACITE),
    ('info@gassergmbh.ch', 9.2, False, ANTHRACITE),
    ('www.gassergmbh.ch', 9.2, False, ANTHRACITE),
]):
    pp = right.add_paragraph() if i or right.paragraphs[0].text else right.paragraphs[0]
    set_paragraph_format(pp, after=1.2, line=1.0)
    add_text(pp, text, size=size, bold=bold, color=color)

# Date
p = doc.add_paragraph()
set_paragraph_format(p, before=24, after=22)
add_text(p, 'Windisch, 20. August 2026', size=10.5)

# Title and legal subtitle
p = doc.add_paragraph()
set_paragraph_format(p, after=2, keep_next=True)
add_text(p, 'Abmahnung', size=17, bold=True, color=BORDEAUX)
p = doc.add_paragraph()
set_paragraph_format(p, after=12, keep_next=True)
add_text(p, 'gemäss Art. 365/369 OR bzw. Norm SIA 118:2013 Art. 25 usw.', size=9.5, color=STEEL)

# Project facts as clean two-column table
facts = doc.add_table(rows=2, cols=2)
facts.alignment = WD_TABLE_ALIGNMENT.LEFT
facts.autofit = False
facts.columns[0].width = Mm(38)
facts.columns[1].width = Mm(120)
remove_table_borders(facts)
for row in facts.rows:
    for c in row.cells:
        set_cell_margins(c, top=0, start=0, bottom=0, end=0)
labels = ['Projekt:', 'Werkvertrag vom:']
values = ['410.3170.10 – Mönchaltorf – Gemeindehaus mit Wohnbau', '[Datum Werkvertrag]']
for i, (lab, val) in enumerate(zip(labels, values)):
    p = facts.cell(i, 0).paragraphs[0]
    set_paragraph_format(p, after=2)
    add_text(p, lab, size=10.5, bold=True)
    p = facts.cell(i, 1).paragraphs[0]
    set_paragraph_format(p, after=2)
    rr = add_text(p, val, size=10.5)
    if val.startswith('['): highlight_run(rr)

# Body
p = doc.add_paragraph()
set_paragraph_format(p, before=13, after=11)
add_text(p, 'Sehr geehrte Damen und Herren', size=10.5)

p = doc.add_paragraph()
set_paragraph_format(p, after=11, line=1.18)
add_text(p, 'Wir mahnen in Erfüllung unserer vertraglichen und gesetzlichen Sorgfaltspflichten die nachfolgend angeführten Verhältnisse ab und lehnen die Haftung für alle Mängel und anderen nachteiligen Folgen, die sich aus der Nichtbeachtung dieser Abmahnung einstellen, vollumfänglich ab.', size=10.5)

p = doc.add_paragraph()
set_paragraph_format(p, after=3, keep_next=True)
add_text(p, 'Abgemahnte Verhältnisse:', size=10.5, bold=True)
p = doc.add_paragraph()
set_paragraph_format(p, after=11)
rr = add_text(p, '[Abgemahnte Verhältnisse präzise beschreiben]', size=10.5)
highlight_run(rr)

p = doc.add_paragraph()
set_paragraph_format(p, after=3, keep_next=True)
add_text(p, 'Sachverhalt bereits mündlich mitgeteilt am ', size=10.5)
rr = add_text(p, '[Datum]', size=10.5)
highlight_run(rr)

p = doc.add_paragraph()
set_paragraph_format(p, after=11)
add_text(p, 'an ', size=10.5)
rr = add_text(p, '[Name / Funktion]', size=10.5)
highlight_run(rr)

p = doc.add_paragraph()
set_paragraph_format(p, after=14)
rr = add_text(p, '[Ort / Datum]', size=10.5)
highlight_run(rr)

p = doc.add_paragraph()
set_paragraph_format(p, after=22)
add_text(p, 'Freundliche Grüsse', size=10.5)

p = doc.add_paragraph()
set_paragraph_format(p, after=1)
add_text(p, 'Andreas Gasser', size=10.5, bold=True)
p = doc.add_paragraph()
set_paragraph_format(p)
add_text(p, 'Geschäftsführer', size=9.5, color=STEEL)

# Footer with CI line and company/contact data
footer = sec.footer
fp = footer.paragraphs[0]
fp.paragraph_format.space_after = Pt(5)
add_bottom_border(fp, BORDEAUX, size='12', space='5')
ft = footer.add_table(rows=1, cols=3, width=Mm(158))
ft.autofit = False
ft.columns[0].width = Mm(62)
ft.columns[1].width = Mm(58)
ft.columns[2].width = Mm(38)
remove_table_borders(ft)
for c in ft.rows[0].cells:
    set_cell_margins(c, top=0, start=0, bottom=0, end=0)

p = ft.cell(0,0).paragraphs[0]
set_paragraph_format(p, line=1.0)
add_text(p, 'Gasser Bauunternehmen GmbH', size=8, bold=True, color=BORDEAUX)
p = ft.cell(0,0).add_paragraph(); set_paragraph_format(p, line=1.0)
add_text(p, 'Grünrain 3 · 5210 Windisch', size=8, color=STEEL)

p = ft.cell(0,1).paragraphs[0]; set_paragraph_format(p, line=1.0)
add_text(p, '077 268 25 29', size=8, color=STEEL)
p = ft.cell(0,1).add_paragraph(); set_paragraph_format(p, line=1.0)
add_text(p, 'info@gassergmbh.ch · www.gassergmbh.ch', size=8, color=STEEL)

p = ft.cell(0,2).paragraphs[0]
p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
set_paragraph_format(p, line=1.0)
add_text(p, 'Seite ', size=8, color=STEEL)
add_field(p, 'PAGE', '1')
add_text(p, ' von ', size=8, color=STEEL)
add_field(p, 'NUMPAGES', '1')

# Document properties
props = doc.core_properties
props.title = 'Abmahnung an Besteller'
props.subject = 'Vorlage im Firmenlayout der Gasser Bauunternehmen GmbH'
props.author = 'Gasser Bauunternehmen GmbH'
props.keywords = 'Abmahnung, Besteller, SIA 118, Gasser Bauunternehmen GmbH'

# Keep tables on page and set language to German (Switzerland)
for p in doc.paragraphs:
    for run in p.runs:
        rPr = run._r.get_or_add_rPr()
        lang = rPr.find(qn('w:lang'))
        if lang is None:
            lang = OxmlElement('w:lang')
            rPr.append(lang)
        lang.set(qn('w:val'), 'de-CH')

# Update fields when opened
settings = doc.settings._element
update = settings.find(qn('w:updateFields'))
if update is None:
    update = OxmlElement('w:updateFields')
    settings.append(update)
update.set(qn('w:val'), 'true')

doc.save(OUT)
print(OUT)
