from pathlib import Path
import re
from docx import Document
from docx.shared import Pt, Cm
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT, WD_CELL_VERTICAL_ALIGNMENT
from docx.oxml import OxmlElement
from docx.oxml.ns import qn
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle, PageBreak
from reportlab.lib.pagesizes import A4
from reportlab.lib import colors
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.enums import TA_LEFT, TA_CENTER
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.lib.units import mm

OUT = Path('/home/agent/outputs/moenchaltorf_jourfix_260623')
RAW = OUT/'transcript_raw.txt'
PROT_DOCX = OUT/'260623_Moenchaltorf_Protokoll_1_Fachplaner_Jourfixe_neu.docx'
PROT_PDF = OUT/'260623_Moenchaltorf_Protokoll_1_Fachplaner_Jourfixe_neu.pdf'
TRANS_DOCX = OUT/'260623_Moenchaltorf_bereinigtes_Transkript_Fachplaner_Jourfixe.docx'

# ---------- shared helpers ----------
def set_cell_shading(cell, fill):
    tcPr = cell._tc.get_or_add_tcPr()
    shd = OxmlElement('w:shd')
    shd.set(qn('w:fill'), fill)
    tcPr.append(shd)

def set_cell_text(cell, text, bold=False):
    cell.text = ''
    p = cell.paragraphs[0]
    r = p.add_run(str(text))
    r.bold = bold
    r.font.size = Pt(8)
    cell.vertical_alignment = WD_CELL_VERTICAL_ALIGNMENT.TOP

def add_docx_table(doc, headers, rows, widths=None):
    table = doc.add_table(rows=1, cols=len(headers))
    table.style = 'Table Grid'
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    hdr = table.rows[0].cells
    for i,h in enumerate(headers):
        set_cell_text(hdr[i], h, bold=True)
        set_cell_shading(hdr[i], 'D9EAF7')
    for row in rows:
        cells = table.add_row().cells
        for i, val in enumerate(row):
            set_cell_text(cells[i], val)
    if widths:
        for row in table.rows:
            for idx, width in enumerate(widths):
                row.cells[idx].width = Cm(width)
    doc.add_paragraph('')
    return table

def add_para(doc, text='', style=None, bold=False):
    p = doc.add_paragraph(style=style) if style else doc.add_paragraph()
    if text:
        r = p.add_run(text)
        r.bold = bold
    return p

# ---------- content ----------
participants = [
    ['BRW', 'Jan Willem Brummelman', 'Gruner AG', 'Werkleitungsplanung / Projektorganisation'],
    ['DOC', 'Cristian Donatiello', 'ERNE AG Holzbau', 'Projektleitung / Sitzungsleitung'],
    ['DÜJ', 'Jonas Dürr', 'ERNE AG Holzbau', 'Holzbauplanung'],
    ['EBD', 'David Ebinger', 'BAKUS Bauphysik & Akustik AG', 'Bauphysik / Akustik'],
    ['GAM', 'Marcel Gasser', 'ERNE AG Holzbau', 'Projektteam'],
    ['GIS', 'Stefan Gilgen', 'HKT Bern', 'Sanitär / Entwässerung'],
    ['KRC', 'Claudia Kresser', 'Hornberger Architekten AG', 'Architektur'],
    ['LJP', 'Ljupko Peric', 'MWV Bauingenieure AG', 'Bauingenieur / Tragwerk'],
    ['NAR', 'Ramon Nachmansohn', 'EBERLE ENGINEERING', 'HLKS / Koordination Gebäudetechnik'],
    ['PEA', 'Angelika Peeters', 'ryffel + ryffel ag', 'Landschaftsarchitektur'],
    ['RIM', 'Mathias Rieser', 'R + B engineering AG', 'Elektroplanung'],
    ['VEL', 'Lukas Velten', 'ERNE AG Holzbau', 'Holzbau / Projektteam'],
]

sections = [
    ('1 Allgemeine Informationen', [
        'Cristian Donatiello begrüsst die Teilnehmenden und führt durch den Fachplaner-Jour-fixe.',
        'Neu im Planungsteam ist Gruner AG für die Werkleitungsplanung. Die Projektorganisation läuft über Jan Willem Brummelman; die konstruktive Bearbeitung erfolgt intern bei Gruner.',
        'asa AG Verkehrsplanung mit Jonas Jost unterstützt bei Erschliessung, Anlieferung und Tiefgarage.',
        'Der Zugriff auf Trimble Connect wurde geprüft. Bei Angelika Peeters und Jan Willem Brummelman war der Zugang zum Sitzungszeitpunkt noch offen bzw. zu aktivieren. Fehlende Zugänge werden durch ERNE organisiert.',
        'Der Terminplan soll digitalisiert und beim nächsten Fachplaner-Jour-fixe gemeinsam geprüft werden.',
    ]),
    ('2 Bauingenieur / Tragwerk', [
        'Ljupko Peric erstellt den Entwurf der Nutzungsvereinbarung bis Ende KW 27 / Anfang Folgewoche.',
        'Nach Vorliegen des konsolidierten Architekturstands prüft der Bauingenieur das bereinigte Vorprojekt nochmals statisch und gibt allfällige Anmerkungen zurück.',
        'Für Rückfragen zu Böschungen und Baustellen-/Geländethemen bleibt Ljupko Peric Ansprechpartner; bei Bedarf wird intern Enrico beigezogen.',
        'Die Tragwerks-/Auflagersituation beim Wohnbau-Treppenhaus, bei Steigzonen und bei Deckeneinbindungen ist zwischen Bauingenieur, Holzbau und Architektur weiter zu koordinieren.',
    ]),
    ('3 Landschaftsarchitektur / Umgebung / Höhen', [
        'Die Aufbauhöhen über der Tiefgaragendecke, das Gefälle der Umgebung und die Anschlusskoten sind kritisch zu koordinieren.',
        'Die Umgebung wird mit ca. 1.5–2.0 % Gefälle vom Gebäude weg betrachtet. Am ungünstigen Punkt verbleiben bei 2 % Gefälle nur ca. 20 cm Aufbau über Rohdecke; für Asphalt werden eher ca. 30 cm benötigt. Plattenbelag würde geringere Aufbauhöhen erlauben, ist gemäss Besprechung aber nicht bevorzugt.',
        'Die Bauphysik weist darauf hin, dass bei ungenügender Überdeckung eine unterseitige Kondensatdämmung der Tiefgaragendecke erforderlich wird; Richtwert ca. 6 cm Mehrschichtdämmplatte.',
        'Zusätzlich ist beim Gebäudeanschluss über der Tiefgaragendecke eine Flankendämmung von ca. 1 m ab Gebäudeaussenkante und ca. 6 cm Stärke zu berücksichtigen; Ausführung z. B. als XPS oberhalb der Abdichtung oder integriert in den Aufbau.',
        'Die Architektur prüft das Absenken des Gebäudes um ca. 5 cm und die Auswirkungen auf Eingänge, Rampen und Anschlüsse. Ein neuer Planstand soll auf der Plattform bereitgestellt werden.',
        'ryffel + ryffel erstellt Querschnitte und Koten-/Aufbaugrundlagen bis Ende KW 27; falls der Plattformzugang nicht rechtzeitig funktioniert, werden die Grundlagen per E-Mail zugestellt.',
    ]),
    ('4 Werkleitungsplanung / Entwässerung / Retention', [
        'Schnittstelle: HKT plant Entwässerung und Leitungen innerhalb des Gebäudes bzw. unter der Bodenplatte inklusive Pumpen; Gruner AG bearbeitet die Werkleitungen ausserhalb des Gebäudes sowie Retention / Regenwasserbewirtschaftung.',
        'Für das Gebäude sind konzeptionell drei Pumpstationen angedacht: Schmutzwasser Technik, Pumpe für Garderoben-/Umgebungsbereich sowie Entwässerung Einstellhalle über Schlammsammler.',
        'Die Übergabepunkte und Anschlussstellen zu Kanalisation und Regenwasserleitungen sind frühzeitig mit der zuständigen Gemeinde-/Ingenieurstelle zu klären.',
        'Cristian Donatiello teilt den Kontakt des zuständigen Ingenieurbüros Bünzli AG in Mönchaltorf mit den Fachplanern.',
        'Eine konzentrierte Versickerung von Meteorwasser auf dem Areal ist gemäss vorliegenden Informationen nicht möglich; das Meteorwasser ist bewilligt einer geeigneten Vorflut, einem Oberflächengewässer oder Meteorwasserkanal zuzuführen. Rückhaltemassnahmen sind nach Möglichkeit vorzusehen.',
        'Die Regenwasserbewirtschaftung / das Formular zur Baueingabe ist gemeinsam durch Sanitär, Landschaftsarchitektur und Werkleitungsplanung zu erarbeiten.',
        'Für den Elektroanschluss werden EKZ- und Swisscom-Werkleitungen in die neue Planung aufgenommen. Die Leistungszusammenstellung erfolgt nach Vorliegen der HLKS-Grundlagen.',
    ]),
    ('5 Bauphysik / Energie / Akustik', [
        'Für den belastbaren Energienachweis benötigt die Bauphysik eine belastbare Plangrundlage mit Fassaden, Fensteröffnungen und konstruktiven Angaben zu Aussenwänden, Dachflächen und Spezialaufbauten.',
        'ERNE Holzbau stellt BAKUS die aktuellen bzw. aus dem Wettbewerb übernommenen Aufbauangaben zur Gebäudehülle zur Verfügung. Danach prüft BAKUS Dämmwerte, U-Werte und Optimierungsbedarf.',
        'Der Dämmperimeter im Untergeschoss ist insbesondere beim Wohnbau nochmals zu prüfen. Der Treppen-/Liftkern im Wohnbau wird als kalter bzw. witterungsexponierter Bereich betrachtet; BAKUS gibt dazu eine Rückmeldung.',
        'Beim Bürobau wird das Untergeschoss voraussichtlich vollständig in den Dämmperimeter einbezogen. Beim Wohnbau wird vorgeschlagen, nur den Treppenhauskern einzubeziehen, Kellerräume auszuklammern und die EG-/UG-Decke unterseitig zu dämmen; die definitive Lösung bleibt zu prüfen.',
        'BAKUS prüft das von der Architektur zugestellte Detail im Bereich Einfahrt/Rampe und gibt Rückmeldung; das Thema ist nicht unmittelbar terminentscheidend, bleibt aber auf dem Radar.',
        'Für umlaufende Elektro-Bodenkanäle entlang der Fassade ist der Schallschutz zu berücksichtigen. Grundsätzlich ist ein durchgehender Kanal möglich, sofern Randdämmstreifen / Trittschalltrennung, unterseitige Trennung sowie raumweise Schottungen gegen Luftschall vorgesehen werden. Details sind durch BAKUS zu prüfen.',
        'Die Bauherrschaft soll im Rahmen der Konzeptvorstellung darauf hingewiesen werden, dass umlaufende Bodenkanäle vorgesehen sind.',
    ]),
    ('6 Architektur / Modellkoordination', [
        'Die Architektur bereinigt Grundrisse und Schnitte, koordiniert die Fachplanerstände und bereitet die Baueingabe weiter vor.',
        'Für den Datenaustausch wurden durch Architektur und HLKS gemeinsame Einfüge-/Referenzpunkte definiert: ein unveränderlicher Ursprungspunkt aus dem Architekturmodell sowie ein Kontrollpunkt im Wohnhaus beim Lift. Höhe: Projekt-Nullniveau. Die Informationen werden allen Fachplanern zugestellt.',
        'Das Projekt wird räumlich 3D koordiniert, ist aber nicht als BIM-Projekt bestellt.',
        'HLKS benötigt für die räumliche Koordination nach Möglichkeit 3D-Grundlagen; Elektro plant grundsätzlich 2D und stellt 3D nur bei Bedarf für Details dar.',
        'Die Architektur integriert die Fachplanerkonzepte und prüft insbesondere UG, Technikflächen, Steigzonen, Raumhöhen, Fluchtwege und Brandschutzdecken.',
    ]),
    ('7 Holzbau / Konstruktion', [
        'Der Treppenhaus-/Liftkern im Wohnbau wird als Massivbau weiterverfolgt. Die Entscheidung wurde am Sitzungsende nochmals bestätigt.',
        'Hintergrund des Entscheids: Befestigung und Auflagerung der Treppe, statische Einfachheit, Schnittstellen und Ausführung werden als geeigneter beurteilt als eine gekapselte Holzbauvariante.',
        'Die Fassade soll aussen mit Wärmedämmung und hinterlüfteter Bekleidung durchlaufen; im Bereich Lift/Steigzone sind Vorsatzschalen bzw. Installationszugänglichkeiten zu berücksichtigen.',
        'Die Auflagersituation der Holzdecken an den Massivbaukern, insbesondere bei Steigzonen und Zimmern, ist zwischen Holzbau, Statik und Architektur weiter zu klären.',
        'ERNE Holzbau lädt den Wettbewerbsstand / Konzeptplan mit Decken, Unterzügen, Holzbau-/Massivbau-Abgrenzungen sowie Aufbauangaben hoch. Abweichungen wie entfallene Kapselung sind zu berücksichtigen.',
        'Die im Wettbewerb vorhandene Variante “Aussenwand Kapselung” kann für den besprochenen Treppenhausbereich gestrichen bzw. ignoriert werden; Typ “Aussenwand Laubengang” bleibt separat relevant.',
        'Innenwandtypen und WC-Wandkonzept werden durch ERNE Holzbau nochmals bereitgestellt bzw. in Trimble hochgeladen.',
    ]),
    ('8 Elektro', [
        'R + B engineering hat einen Entwurf als Gesprächsgrundlage für Platzbedarf, Technikräume, Steigzonen und Erschliessung vorgestellt.',
        'Im Wohnbau sollen Elektroverteilungen und Bodenheizverteiler möglichst in Garderoben-/Schrankbereichen integriert werden. Eine Variante mit ca. 30 cm Zusatzelement neben der Garderobe wurde diskutiert; genaue Abmessungen sind bilateral mit Architektur und HLKS zu prüfen.',
        'HLKS bevorzugt eine möglichst getrennte Führung von Heizung/Bodenheizverteiler und Elektro, da gemeinsame Steigzonen in der Ausführung problematisch sein können. Die Detailabstimmung erfolgt bilateral.',
        'Im Gemeindehaus wird ein eigenes Elektro-Steigzonenkonzept angestrebt, möglichst unabhängig von HLKS. Technikraum, Putzraum, WC-Lagen und Steigzonen sollen übereinander bzw. logisch angeordnet werden.',
        'Die Architektur prüft, ob der Elektro-/Technikraum im UG eher in Richtung Lagerraum verbreitert werden kann, damit der Archivraum nicht zusätzlich verkleinert wird.',
        'Brandschutz- und Fluchtwegsituation sind bei Trassenführungen, abgehängten Decken und Leitungsführungen im Gemeindehaus zu berücksichtigen. Das Brandschutzkonzept ist auf Trimble abgelegt.',
        'Mathias Rieser ist am nächsten Fachplaner-Jour-fixe abwesend; Sandro Weder nimmt stellvertretend teil und wird intern instruiert.',
    ]),
    ('9 HLKS / Gebäudetechnik', [
        'EBERLE hat einen Übersichtsplan mit Installationsbereichen, Installationshöhen und Steigzonen vorgestellt und lädt die Unterlagen auf Trimble hoch.',
        'Für die Tiefgarage wird die natürliche bzw. teilnatürliche Belüftung als schwierig eingeschätzt. Eine Simulation ist erforderlich, sofern nicht vollständig mechanisch be- und entlüftet wird.',
        'EBERLE hat eine Offerte für die Simulation der natürlichen / teilnatürlichen Tiefgaragenlüftung eingeholt und stellt diese ERNE zur Prüfung und Freigabe zu.',
        'Als Worst-Case bzw. teilmechanisches Szenario wurden Lüftungskanäle entlang der Tiefgarage, Schächte und eine Führung über Dach skizziert. Die Kanäle benötigen ca. 30 cm Höhe und beeinflussen Türhöhen, Durchgänge, Fluchtwege und Raumhöhen.',
        'Die technische Integration im UG ist kritisch. Architektur und HLKS prüfen gemeinsam, wie Monoblock, Lüftung, Heizung, Elektro, Technikflächen, Putzräume und Steigzonen räumlich zusammengebracht werden können.',
        'Für öffentliche Nutzungen und hindernisfreies Bauen sind Raumhöhen und spätere Umnutzbarkeit zu beachten; zu knappe Höhen können spätere Nutzungsänderungen einschränken.',
        'Die Putzräume bzw. Steigzonen sollen, soweit möglich, übereinandergelegt oder logisch geführt werden. Abweichungen sind wegen Nutzung, Fluchtwegen und Grundrissanforderungen zu begründen und zu koordinieren.',
    ]),
    ('10 Nächste Termine / weiteres Vorgehen', [
        'Nächster Fachplaner-Jour-fixe: 07.07.2026, gleiche Zeit, Microsoft Teams.',
        'Beim nächsten Termin sollen der digitalisierte Terminplan, die integrierten Fachplanerkonzepte, die kritischen UG-/Technikthemen sowie die Pendenzen vertieft geprüft werden.',
        'Zusätzliche Fachkoordinationstermine zwischen Architektur, HLKS, Elektro, Holzbau und weiteren Planern sind bei Bedarf geführt und protokolliert durchzuführen; rein bilaterale Abstimmungen reichen für die Konsolidierung nicht aus.',
    ]),
]

decisions = [
    ['Treppenhaus / Liftkern Wohnbau', 'Der Treppenhaus-/Liftkern im Wohnbau wird in Massivbau weiterverfolgt; gekapselte Holzbauvariante wird nicht weiterverfolgt.', 'Architektur / Holzbau / Statik arbeiten auf dieser Basis weiter.'],
    ['Gebäudehöhe / Umgebung', 'Das Absenken des Gebäudes um ca. 5 cm wird als Lösungsansatz weiter geprüft.', 'Architektur prüft Auswirkungen auf Eingänge, Rampen, Koten und Planunterlagen.'],
    ['Modellkoordination', 'Einfüge-/Referenzpunkte für den Datenaustausch werden durch Architektur vorgegeben und von den Fachplanern übernommen.', 'Architektur kommuniziert die Punkte an alle Fachplaner.'],
    ['Tiefgaragenlüftung', 'Natürliche / teilnatürliche Belüftung wird weiter geprüft; hierfür ist eine Simulation bzw. externe Offerte erforderlich.', 'EBERLE stellt Offerte ERNE zur Freigabe zu.'],
]

open_questions = [
    ['Belag / Aufbau über Tiefgarage', 'Reicht die verfügbare Aufbauhöhe bei Asphalt; ist ein alternativer Belag erforderlich oder möglich?', 'Architektur / Landschaft / Bauphysik', 'Ende KW 27 / nächster JF'],
    ['Dämmperimeter Wohnbau UG', 'Wie verläuft der definitive Dämmperimeter bei kaltem Treppen-/Liftkern und Kellerräumen?', 'BAKUS / Architektur', 'Rückmeldung nach Prüfung'],
    ['Auflager Holzdecken am Massivkern', 'Wie werden Decken, Steigzonen und Auflager an den Massivbaukern konstruktiv gelöst?', 'Holzbau / Statik / Architektur', 'nächste Koordination'],
    ['Werkleitungsanschlüsse / Meteorwasser', 'Welche Einleitmengen und Anschlussbedingungen gelten gemäss Gemeinde / Ingenieurbüro Bünzli AG?', 'Gruner / HKT / ERNE', 'frühzeitig vor Baueingabe'],
    ['Elektro- und HLKS-Steigzonen', 'Können Putzräume, WC, Elektro und HLKS-Steigzonen logisch übereinander bzw. getrennt geführt werden?', 'Architektur / Elektro / HLKS', 'nächster JF'],
    ['Tiefgaragenlüftung', 'Ist natürliche oder teilnatürliche Lüftung bewilligungsfähig, oder ist eine mechanische Lösung notwendig?', 'EBERLE / externer Spezialist / ERNE', 'nach Offerte / Simulation'],
]

risks = [
    ['Aufbauhöhen Umgebung / Tiefgaragendecke', 'Risiko', 'Zu geringe Aufbauhöhe für Gefälle, Belag, Entwässerung, Dämmung und Abdichtung.', 'Höhenkonflikte, Plananpassungen, Kosten-/Terminfolgen.', 'Querschnitte, Koten und Belag klären; Gebäudeabsenkung prüfen.'],
    ['Dämmperimeter / Kondensat', 'Risiko', 'Unklare Dämmführung im UG und ungenügende Überdeckung können Kondensatprobleme verursachen.', 'Bauschäden / Komfort- und Qualitätsrisiken.', 'BAKUS prüft Perimeter und Kondensatdämmung.'],
    ['Tiefgaragenlüftung', 'Risiko', 'Natürliche Lüftung wahrscheinlich schwierig; Kanäle und Schächte haben grosse räumliche Auswirkungen.', 'Raumhöhen, Türen, Fluchtwege, Schächte und Kosten betroffen.', 'Simulation / Offerte prüfen und Konzept integrieren.'],
    ['Steigzonen / Technikflächen', 'Risiko', 'Versetzte Putzräume/WC/Steigzonen erschweren HLKS- und Elektroführung.', 'Mehr Abhängedecken, Durchdringungen, Koordinationsaufwand.', 'Grundrisse gegen Fachplanerkonzepte prüfen.'],
    ['Schallschutz Bodenkanäle', 'Risiko', 'Durchgehende Bodenkanäle können Luft- und Trittschall übertragen.', 'Nichterfüllung Schallschutzanforderungen.', 'Schottungen und Detailaufbau durch BAKUS prüfen.'],
    ['Werkleitungen / Meteorwasser', 'Risiko', 'Versickerung nicht möglich; Einleitung und Retention müssen bewilligungsfähig gelöst werden.', 'Baueingabe- und Terminrisiko.', 'Gemeinde/Bünzli frühzeitig einbeziehen.'],
]

pendenzen = [
    ['P01', 'Trimble-Zugänge für Angelika Peeters und Jan Willem Brummelman prüfen/organisieren.', 'DOC / ERNE', 'sofort', 'hoch', 'Termin / Koordination', 'offen'],
    ['P02', 'Entwurf Nutzungsvereinbarung erstellen.', 'LJP', 'Ende KW 27 / Anfang Folgewoche', 'hoch', 'Planungsgrundlage', 'offen'],
    ['P03', 'Bereinigtes Vorprojekt nach Abschluss Koordination statisch prüfen.', 'LJP', 'nach neuem Planstand', 'hoch', 'Qualität / Statik', 'offen'],
    ['P04', 'Gebäudeabsenkung um ca. 5 cm inkl. Eingänge/Rampen/Koten prüfen und neuen Architekturstand bereitstellen.', 'KRC', 'bis 24.06.2026 / vor Querschnitten', 'hoch', 'Termin / Qualität', 'offen'],
    ['P05', 'Querschnitte, Gefälle und Aufbauhöhen Umgebung/Tiefgaragendecke erstellen.', 'PEA', 'Ende KW 27', 'hoch', 'Termin / Qualität', 'offen'],
    ['P06', 'Dämmperimeter Wohnbau UG, Flankendämmung und Kondensatdämmung prüfen und Rückmeldung geben.', 'EBD', 'nächste Koordination', 'hoch', 'Qualität / Bauphysik', 'offen'],
    ['P07', 'Aktuelle Aufbauangaben Hülle, Innenwände, WC-Wände und Konzeptplan Holzbau/Decken/Unterzüge hochladen.', 'DÜJ / VEL', 'zeitnah', 'hoch', 'Koordination / Energie', 'offen'],
    ['P08', 'Detail Einfahrt/Rampe bauphysikalisch prüfen und Rückmeldung an Architektur geben.', 'EBD', 'nächste Woche möglich', 'mittel', 'Qualität', 'offen'],
    ['P09', 'Kontakt Ingenieurbüro Bünzli AG / Gemeinde für Kanalisation und Meteorwasser an Gruner/HKT verteilen.', 'DOC', 'sofort', 'hoch', 'Baueingabe / Werkleitungen', 'offen'],
    ['P10', 'Werkleitungs-/Regenwasser-/Retentionsschnittstellen und Einleitbedingungen mit Gemeinde/Bünzli klären.', 'BRW / GIS / PEA', 'frühzeitig vor Baueingabe', 'hoch', 'Baueingabe / Risiko', 'offen'],
    ['P11', 'Leistungszusammenstellung Elektro nach Vorliegen HLKS-Grundlagen erarbeiten.', 'RIM', 'nach HLKS-Grundlagen', 'mittel', 'Koordination', 'offen'],
    ['P12', 'Einfüge-/Referenzpunkte für Modell-/Planaustausch an alle Fachplaner kommunizieren.', 'KRC', 'zeitnah', 'hoch', 'Koordination', 'offen'],
    ['P13', 'Bodenkanal-Detail hinsichtlich Schall- und Trittschalltrennung prüfen.', 'EBD / RIM', 'vor Detailplanung', 'mittel', 'Schallschutz', 'offen'],
    ['P14', 'Elektro-/HLKS-Verteiler, Garderoben-/Schranklösung und Steigzonen im Wohnbau bilateral vertiefen.', 'RIM / NAR / KRC', 'vor nächstem JF', 'hoch', 'Koordination', 'offen'],
    ['P15', 'Elektro-Technikraum UG prüfen: Verbreiterung Richtung Lager statt Reduktion Archiv.', 'KRC / RIM', 'nächster JF', 'mittel', 'Nutzung / Koordination', 'offen'],
    ['P16', 'HLKS-Installationspläne, Installationshöhen und Steigzonen auf Trimble hochladen.', 'NAR', 'sofort', 'hoch', 'Koordination', 'offen'],
    ['P17', 'Offerte für Simulation natürliche/teilnatürliche Tiefgaragenlüftung ERNE zustellen.', 'NAR', '24.06.2026 / zeitnah', 'hoch', 'Kosten / Entscheid', 'offen'],
    ['P18', 'Digitalisierten Terminplan erstellen und beim nächsten Fachplaner-Jour-fixe besprechen.', 'DOC / ERNE', '07.07.2026', 'hoch', 'Termin', 'offen'],
    ['P19', 'Sandro Weder für nächsten Fachplaner-Jour-fixe instruieren und teilnehmen lassen.', 'RIM', '07.07.2026', 'mittel', 'Koordination', 'offen'],
]

# ---------- protocol docx ----------
def build_protocol_docx():
    doc = Document()
    sec = doc.sections[0]
    sec.top_margin = Cm(1.6); sec.bottom_margin = Cm(1.4); sec.left_margin = Cm(1.5); sec.right_margin = Cm(1.5)
    styles = doc.styles
    styles['Normal'].font.name = 'Arial'; styles['Normal'].font.size = Pt(9)
    styles['Heading 1'].font.name = 'Arial'; styles['Heading 1'].font.size = Pt(14); styles['Heading 1'].font.bold = True
    styles['Heading 2'].font.name = 'Arial'; styles['Heading 2'].font.size = Pt(11); styles['Heading 2'].font.bold = True
    p = doc.add_paragraph()
    p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = p.add_run('Projekt: 410.3170.10 – Mönchaltorf – Gemeindehaus mit Wohnbau')
    r.bold = True; r.font.size = Pt(12)
    p = doc.add_paragraph(); p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = p.add_run('Protokoll 1. Fachplaner-Jour-fixe – bereinigt und ergänzt')
    r.bold = True; r.font.size = Pt(16)
    add_docx_table(doc, ['Feld','Inhalt'], [
        ['Datum / Zeit', '23.06.2026, 13:15–ca. 15:09 Uhr'],
        ['Ort / Medium', 'Microsoft Teams'],
        ['Protokollbasis', 'Teams-Transkript, geführter Fachplaner-Jour-fixe und handgeführtes Vorlageprotokoll'],
        ['Sitzungsleitung', 'Cristian Donatiello, ERNE AG Holzbau'],
        ['Erstellung', '30.06.2026, bereinigt nach tatsächlich besprochenen Inhalten'],
        ['Legende', 'I = Information, B = Beschluss, P = Pendenz'],
    ], widths=[4,13])
    doc.add_heading('Teilnehmende', level=1)
    add_docx_table(doc, ['Kürzel','Name','Firma','Rolle / Fachbereich'], participants, widths=[1.5,4.5,5,6])
    doc.add_heading('Management Summary', level=1)
    summary = [
        'Hauptthemen waren die Höhenkoordination über der Tiefgarage, die Werkleitungs-/Meteorwasserplanung, Dämmperimeter und Bauphysik, die Ausführung des Wohnbau-Treppenhauses, Elektro-/HLKS-Steigzonen sowie die Tiefgaragenlüftung.',
        'Die Höhen über der Tiefgaragendecke sind kritisch: Gefälle, Belag, Abdichtung, Dämmung und Entwässerung müssen anhand von Querschnitten und Koten konsolidiert werden.',
        'Das Wohnbau-Treppenhaus inkl. Liftkern wird als Massivbau weiterverfolgt; dadurch sind Auflager, Dämmperimeter und Schnittstellen weiter zu koordinieren.',
        'Die natürliche bzw. teilnatürliche Tiefgaragenlüftung ist unsicher und benötigt eine Simulation; eine mechanische / teilmechanische Lösung hätte relevante Auswirkungen auf Raumhöhen, Kanäle und Schächte.',
        'Werkleitungen, Retention und Meteorwasser sind wegen nicht möglicher konzentrierter Versickerung früh mit Gemeinde/Bünzli AG abzustimmen.',
        'Elektro und HLKS benötigen weitere Koordination zu Technikflächen, Putzräumen, Steigzonen, Bodenkanälen und Brandschutz-/Schallschutzdetails.',
        'Der Terminplan wird digitalisiert und beim nächsten Fachplaner-Jour-fixe vom 07.07.2026 geprüft.',
    ]
    for s in summary:
        add_para(doc, s, style='List Bullet')
    doc.add_heading('Ampelstatus', level=1)
    add_docx_table(doc, ['Bereich','Status','Begründung / Handlungsbedarf'], [
        ['Gesamtstatus','Gelb','Viele Schnittstellen sind erkannt, aber mehrere koordinationskritische Punkte sind noch offen.'],
        ['Termine','Gelb','Baueingabe-relevante Themen Werkleitungen, Höhen, Technik und Lüftung müssen kurzfristig geklärt werden.'],
        ['Kosten','Gelb','Simulation Tiefgaragenlüftung, mögliche Kanal-/Schachtlösungen und Höhen-/Belagsvarianten können Kosten auslösen.'],
        ['Qualität / PQM','Gelb','Dämmperimeter, Kondensat, Schallschutz und Raumhöhen benötigen technische Absicherung.'],
        ['Koordination','Gelb','3D-/2D-Schnittstellen, Einfügepunkte, Fachmodelle und Steigzonen sind zu konsolidieren.'],
        ['Entscheide / Eskalation','Gelb','Massivbau Treppenhaus entschieden; Lüftung, Meteorwasser und Technikflächen offen.'],
    ], widths=[4,2,11])
    doc.add_heading('Traktanden / Protokoll', level=1)
    for title, bullets in sections:
        doc.add_heading(title, level=2)
        for b in bullets:
            add_para(doc, b, style='List Bullet')
    doc.add_heading('Entscheide', level=1)
    add_docx_table(doc, ['Entscheid','Kontext','Folge / Verantwortlich'], decisions, widths=[4,8,6])
    doc.add_heading('Offene Fragen', level=1)
    add_docx_table(doc, ['Frage','Kontext','Klärung durch','Termin'], open_questions, widths=[4,7,4,3])
    doc.add_heading('Risiken und Chancen', level=1)
    add_docx_table(doc, ['Thema','Art','Bewertung','Auswirkung','Nächster Schritt'], risks, widths=[4,2,5,4,4])
    doc.add_heading('Pendenzenliste', level=1)
    add_docx_table(doc, ['Nr.','Pendenz','Verantwortlich','Termin','Priorität','Relevanz','Status'], pendenzen, widths=[1.2,7,2.5,2.5,1.7,2.5,1.5])
    doc.add_heading('Punkte für nächste Sitzung', level=1)
    for item in [
        'Digitalisierter Terminplan und Sommerferien-/Koordinationstermine.',
        'Konsolidierter Architekturstand mit Fachplanerinputs und Höhen-/Kotenprüfung.',
        'Werkleitungs-/Meteorwasserabstimmung mit Gemeinde/Bünzli AG.',
        'Entscheidungsgrundlage Tiefgaragenlüftung inkl. Offerte Simulation.',
        'Steigzonen, Technikflächen, Putzräume und Elektro-/HLKS-Koordination.',
        'Holzbau-/Massivbau-Abgrenzung, Deckenauflager und Dämmperimeter Wohnbau.',
    ]:
        add_para(doc, item, style='List Bullet')
    doc.add_heading('Unklare oder nicht abschliessend geklärte Punkte', level=1)
    for item in [
        'Definitiver Belag und Aufbau über der Tiefgaragendecke.',
        'Definitive Lösung natürliche / teilnatürliche / mechanische Tiefgaragenlüftung.',
        'Definitiver Dämmperimeter im Wohnbau-Untergeschoss.',
        'Definitive Lage und Dimensionierung einzelner Elektro-/HLKS-Steigzonen und Technikflächen.',
        'Konkrete Einleitmengen und Anschlussbedingungen für Meteorwasser.',
    ]:
        add_para(doc, item, style='List Bullet')
    doc.add_heading('Kurzversion zum Weiterleiten', level=1)
    add_para(doc, 'Der 1. Fachplaner-Jour-fixe zeigte mehrere koordinationskritische Punkte: Höhen/Aufbau über Tiefgarage, Werkleitungen/Meteorwasser, Dämmperimeter, Steigzonen/Technikflächen und Tiefgaragenlüftung. Das Treppenhaus/Liftkern Wohnbau wird als Massivbau weiterverfolgt. Für die Umgebung werden Querschnitte/Koten bis Ende KW 27 erstellt; Architektur prüft das Absenken des Gebäudes um ca. 5 cm. Die natürliche/teilnatürliche Tiefgaragenlüftung benötigt eine Simulation; EBERLE stellt ERNE die Offerte zu. Nächster Fachplaner-Jour-fixe: 07.07.2026, Microsoft Teams.')
    doc.save(PROT_DOCX)

# ---------- protocol PDF ----------
def register_font():
    for f in ['/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', '/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf']:
        if Path(f).exists():
            pdfmetrics.registerFont(TTFont('BodyFont', f))
            return 'BodyFont'
    return 'Helvetica'

def pdf_para(text, style):
    text = str(text).replace('&','&amp;').replace('<','&lt;').replace('>','&gt;').replace('\n','<br/>')
    return Paragraph(text, style)

def build_protocol_pdf():
    font = register_font()
    styles = getSampleStyleSheet()
    styles.add(ParagraphStyle(name='BodyX', fontName=font, fontSize=7.2, leading=9, spaceAfter=2))
    styles.add(ParagraphStyle(name='H1X', fontName=font, fontSize=13, leading=15, spaceBefore=7, spaceAfter=5, textColor=colors.HexColor('#003B5C')))
    styles.add(ParagraphStyle(name='H2X', fontName=font, fontSize=9.5, leading=11, spaceBefore=5, spaceAfter=3, textColor=colors.HexColor('#003B5C')))
    styles.add(ParagraphStyle(name='TitleX', fontName=font, fontSize=15, leading=18, alignment=TA_CENTER, spaceAfter=8))
    doc = SimpleDocTemplate(str(PROT_PDF), pagesize=A4, rightMargin=12*mm, leftMargin=12*mm, topMargin=12*mm, bottomMargin=12*mm)
    story = []
    story.append(pdf_para('Projekt: 410.3170.10 – Mönchaltorf – Gemeindehaus mit Wohnbau', styles['BodyX']))
    story.append(pdf_para('Protokoll 1. Fachplaner-Jour-fixe – bereinigt und ergänzt', styles['TitleX']))
    def rtable(headers, rows, widths=None, font_size=6.4):
        data = [[pdf_para(h, styles['BodyX']) for h in headers]] + [[pdf_para(c, styles['BodyX']) for c in row] for row in rows]
        tbl = Table(data, colWidths=widths, repeatRows=1)
        tbl.setStyle(TableStyle([
            ('BACKGROUND',(0,0),(-1,0),colors.HexColor('#D9EAF7')), ('TEXTCOLOR',(0,0),(-1,0),colors.black),
            ('GRID',(0,0),(-1,-1),0.25,colors.grey), ('VALIGN',(0,0),(-1,-1),'TOP'),
            ('FONTNAME',(0,0),(-1,-1),font), ('FONTSIZE',(0,0),(-1,-1),font_size),
            ('LEFTPADDING',(0,0),(-1,-1),3), ('RIGHTPADDING',(0,0),(-1,-1),3), ('TOPPADDING',(0,0),(-1,-1),2), ('BOTTOMPADDING',(0,0),(-1,-1),2),
        ]))
        story.append(tbl); story.append(Spacer(1,4))
    rtable(['Feld','Inhalt'], [
        ['Datum / Zeit','23.06.2026, 13:15–ca. 15:09 Uhr'], ['Ort / Medium','Microsoft Teams'],
        ['Protokollbasis','Teams-Transkript, geführter Fachplaner-Jour-fixe und handgeführtes Vorlageprotokoll'],
        ['Sitzungsleitung','Cristian Donatiello, ERNE AG Holzbau'], ['Legende','I = Information, B = Beschluss, P = Pendenz']], [35*mm, 135*mm])
    story.append(pdf_para('Teilnehmende', styles['H1X'])); rtable(['Kürzel','Name','Firma','Rolle'], participants, [14*mm,36*mm,49*mm,71*mm])
    story.append(pdf_para('Management Summary', styles['H1X']))
    for s in ['Hauptthemen: Höhenkoordination über Tiefgarage, Werkleitungen/Meteorwasser, Dämmperimeter/Bauphysik, Wohnbau-Treppenhaus, Elektro-/HLKS-Steigzonen und Tiefgaragenlüftung.', 'Höhen über der Tiefgaragendecke sind kritisch und anhand Querschnitten/Koten zu konsolidieren.', 'Wohnbau-Treppenhaus inkl. Liftkern wird als Massivbau weiterverfolgt.', 'Natürliche/teilnatürliche Tiefgaragenlüftung benötigt Simulation; Kanal-/Schachtlösungen beeinflussen Raumhöhen und Fluchtwege.', 'Werkleitungen, Retention und Meteorwasser sind früh mit Gemeinde/Bünzli AG abzustimmen.', 'Nächster Fachplaner-Jour-fixe: 07.07.2026, Microsoft Teams.']:
        story.append(pdf_para('• '+s, styles['BodyX']))
    story.append(pdf_para('Ampelstatus', styles['H1X']))
    rtable(['Bereich','Status','Begründung / Handlungsbedarf'], [['Gesamtstatus','Gelb','Viele Schnittstellen erkannt; mehrere koordinationskritische Punkte offen.'], ['Termine','Gelb','Baueingabe-relevante Themen kurzfristig klären.'], ['Kosten','Gelb','Simulation, Kanäle/Schächte und Höhen-/Belagsvarianten können Kosten auslösen.'], ['Qualität','Gelb','Dämmperimeter, Kondensat, Schallschutz und Raumhöhen absichern.'], ['Koordination','Gelb','3D-/2D-Schnittstellen, Einfügepunkte und Steigzonen konsolidieren.']], [35*mm,18*mm,117*mm])
    story.append(pdf_para('Traktanden / Protokoll', styles['H1X']))
    for title, bullets in sections:
        story.append(pdf_para(title, styles['H2X']))
        for b in bullets:
            story.append(pdf_para('• '+b, styles['BodyX']))
    story.append(PageBreak())
    story.append(pdf_para('Entscheide', styles['H1X'])); rtable(['Entscheid','Kontext','Folge / Verantwortlich'], decisions, [35*mm,80*mm,55*mm])
    story.append(pdf_para('Offene Fragen', styles['H1X'])); rtable(['Frage','Kontext','Klärung durch','Termin'], open_questions, [34*mm,68*mm,34*mm,34*mm])
    story.append(pdf_para('Risiken und Chancen', styles['H1X'])); rtable(['Thema','Art','Bewertung','Auswirkung','Nächster Schritt'], risks, [33*mm,15*mm,50*mm,35*mm,37*mm])
    story.append(pdf_para('Pendenzenliste', styles['H1X'])); rtable(['Nr.','Pendenz','Verantwortlich','Termin','Prio','Relevanz','Status'], pendenzen, [10*mm,68*mm,24*mm,25*mm,13*mm,22*mm,12*mm], font_size=5.8)
    story.append(pdf_para('Kurzversion zum Weiterleiten', styles['H1X']))
    story.append(pdf_para('Der 1. Fachplaner-Jour-fixe zeigte mehrere koordinationskritische Punkte: Höhen/Aufbau über Tiefgarage, Werkleitungen/Meteorwasser, Dämmperimeter, Steigzonen/Technikflächen und Tiefgaragenlüftung. Das Treppenhaus/Liftkern Wohnbau wird als Massivbau weiterverfolgt. Für die Umgebung werden Querschnitte/Koten bis Ende KW 27 erstellt; Architektur prüft das Absenken des Gebäudes um ca. 5 cm. Die natürliche/teilnatürliche Tiefgaragenlüftung benötigt eine Simulation; EBERLE stellt ERNE die Offerte zu. Nächster Fachplaner-Jour-fixe: 07.07.2026, Microsoft Teams.', styles['BodyX']))
    doc.build(story)

# ---------- cleaned transcript ----------
def parse_transcript():
    lines = RAW.read_text(encoding='utf-8').splitlines()
    speaker_re = re.compile(r'^(.*?)\s+(\d+:\d{2}(?::\d{2})?)$')
    turns=[]; cur=None
    for line in lines:
        m=speaker_re.match(line.strip())
        if m:
            if cur and cur['text']:
                turns.append(cur)
            cur={'speaker':m.group(1).strip(), 'time':m.group(2), 'text':[]}
        else:
            if cur:
                t=line.strip()
                if t:
                    cur['text'].append(t)
    if cur and cur['text']:
        turns.append(cur)
    return turns

skip_phrases = set(['hallo miteinander','hallo','hi','hoi david','hoi','ok','o.k.','mhm','yeah','ja','nein','yep','good','ciao','tschüss','super','danke','thank you','i','no','sure','but'])
noise_words = re.compile(r'^(mhm|ähm|äh|ok|o\.k\.|ja|nein|yeah|yep|good|no|but|thanks?|ciao|tschüss|super|aha|uh huh|hm)[\s.,!?-]*$', re.I)

def clean_text_fragments(frags):
    # Join fragments, remove obvious ASR-only one-word/filler lines, keep content otherwise.
    kept=[]
    for f in frags:
        s=f.strip()
        low=s.lower().strip(' .,!?:;')
        if low in skip_phrases or noise_words.match(low):
            continue
        if len(low) <= 3 and not any(ch.isdigit() for ch in low):
            continue
        # Remove repeated filler words lightly, without rewriting technical content.
        s=re.sub(r'\b(ähm|äh|mhm|okay|ok)\b[,. ]*', '', s, flags=re.I).strip()
        if s:
            kept.append(s)
    text=' '.join(kept)
    # Remove opening smalltalk from the first substantive chair turn.
    text=re.sub(r'^.*?Gut, dann starten wir doch\.\s*', '', text, flags=re.I)
    text=re.sub(r'^Jo, sonst.*?Gut, dann starten wir doch\.\s*', '', text, flags=re.I)
    text=re.sub(r'\s+', ' ', text).strip()
    return text


def time_to_seconds(t):
    parts=[int(x) for x in t.split(':')]
    if len(parts)==2:
        return parts[0]*60+parts[1]
    return parts[0]*3600+parts[1]*60+parts[2]

def build_transcript_docx():
    turns = parse_transcript()
    doc = Document()
    sec = doc.sections[0]
    sec.top_margin=Cm(1.5); sec.bottom_margin=Cm(1.5); sec.left_margin=Cm(1.5); sec.right_margin=Cm(1.5)
    doc.styles['Normal'].font.name='Arial'; doc.styles['Normal'].font.size=Pt(9)
    p=doc.add_paragraph(); p.alignment=WD_ALIGN_PARAGRAPH.CENTER
    r=p.add_run('Mönchaltorf – Gemeindehaus mit Wohnbau\nBereinigtes Transkript Fachplaner-Jour-fixe')
    r.bold=True; r.font.size=Pt(15)
    add_docx_table(doc, ['Feld','Inhalt'], [['Datum','23.06.2026'], ['Basis','Teams-Transkript / Meeting Recording'], ['Bereinigung','Smalltalk, reine Begrüssungen, Füllwörter und offensichtlich irrelevante Kurzreaktionen entfernt; fachliche Aussagen nicht inhaltlich erweitert.']], widths=[4,13])
    tbl=doc.add_table(rows=1, cols=3); tbl.style='Table Grid'; tbl.alignment=WD_TABLE_ALIGNMENT.CENTER
    for i,h in enumerate(['Zeit','Sprecher','Bereinigte Aussage']):
        set_cell_text(tbl.rows[0].cells[i], h, bold=True); set_cell_shading(tbl.rows[0].cells[i], 'D9EAF7')
    for turn in turns:
        # Skip the joining/technical-check phase before the meeting content starts.
        if time_to_seconds(turn['time']) < 4*60+57:
            continue
        text=clean_text_fragments(turn['text'])
        if not text:
            continue
        # Skip short nontechnical confirmations and residual smalltalk.
        lower=text.lower()
        if any(p in lower for p in ['hörst du uns', 'warte mal', 'kommt auch dazu', 'der einzige, wo noch fehlt', 'tschüss zusammen']):
            continue
        if len(text) < 28 and not any(k in lower for k in ['kw','termin','zugang','trimble','konzept','plan','dämm','lüft','elektro','holz','bau']):
            continue
        cells=tbl.add_row().cells
        set_cell_text(cells[0], turn['time'])
        set_cell_text(cells[1], turn['speaker'])
        set_cell_text(cells[2], text)
    doc.save(TRANS_DOCX)

if __name__ == '__main__':
    build_protocol_docx()
    build_protocol_pdf()
    build_transcript_docx()
    print(PROT_DOCX)
    print(PROT_PDF)
    print(TRANS_DOCX)
