#!/usr/bin/env python3
"""
Generator voor Beleidsdocument_Voorjaarsconventie_2027_v1.0.docx
District 110AN - Voorjaarsconventie April 2027
Type: Beleidsdocument (Policy) conform MASTER_SOP §3.5
Bron: Meta-Dossier v1 (alleen feiten daaruit)
"""

from docx import Document
from docx.shared import Cm, Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import nsdecls
from docx.oxml import parse_xml
import os
from datetime import date

# ── Constants (alleen uit Meta-Dossier) ────────────────────────────────────
DISTRICT = "110AN"
CONVENTIE_TYPE = "Voorjaarsconventie (District Convention)"
CONVENTIE_MAAND = "April 2027"
GOVERNANCE = "Huishoudelijk Reglement MD 110 mei 2021"
STEMRECHT_REGEL = "1 stem per 10 leden per club (min 1, max 5)"
QUORUM_REGEL = ">50% van stemgerechtigden"
THEMA_2026_2027 = "Samen verder bouwen!"
BUDGET_POST = "€ 3.500 (post '1 Districtsconventie', Districtbegroting 25-26)"
LOGO_PATH = "/root/projects/jg/2026-voorjaarsconventie-110an/assets/logo_jvg.png"
LOGO_WIDTH_CM = 2.0
LOGO_HEIGHT_CM = 1.8


def set_cell_shading(cell, color_hex: str):
    shading = parse_xml(f'<w:shd {nsdecls("w")} w:fill="{color_hex}"/>')
    cell._tc.get_or_add_tcPr().append(shading)


def add_heading_styled(doc, text: str, level: int):
    heading = doc.add_heading(text, level=level)
    for run in heading.runs:
        run.font.color.rgb = RGBColor(0x00, 0x33, 0x66)
        run.font.name = 'Calibri'
    return heading


def add_body_text(doc, text: str, bold=False, italic=False, size=Pt(11), alignment=None):
    p = doc.add_paragraph()
    run = p.add_run(text)
    run.font.size = size
    run.font.name = 'Calibri'
    run.bold = bold
    run.italic = italic
    run.font.color.rgb = RGBColor(0x33, 0x33, 0x33)
    if alignment:
        p.alignment = alignment
    return p


def add_bullet(doc, text: str, level=0, bold_prefix=None):
    p = doc.add_paragraph(style='List Bullet')
    p.paragraph_format.left_indent = Cm(1.27 + level * 1.27)
    if bold_prefix:
        run_b = p.add_run(bold_prefix)
        run_b.bold = True
        run_b.font.name = 'Calibri'
        run_b.font.size = Pt(11)
        run_b.font.color.rgb = RGBColor(0x33, 0x33, 0x33)
        run = p.add_run(text)
    else:
        run = p.add_run(text)
    run.font.name = 'Calibri'
    run.font.size = Pt(11)
    run.font.color.rgb = RGBColor(0x33, 0x33, 0x33)
    return p


def add_table_with_style(doc, rows, cols, data, header_row=True, col_widths=None):
    table = doc.add_table(rows=rows, cols=cols, style='Table Grid')
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    if col_widths:
        for i, width in enumerate(col_widths):
            for row in table.rows:
                row.cells[i].width = Cm(width)
    if header_row and table.rows:
        for cell in table.rows[0].cells:
            set_cell_shading(cell, "003366")
            for paragraph in cell.paragraphs:
                paragraph.alignment = WD_ALIGN_PARAGRAPH.CENTER
                for run in paragraph.runs:
                    run.font.color.rgb = RGBColor(0xFF, 0xFF, 0xFF)
                    run.font.bold = True
                    run.font.name = 'Calibri'
                    run.font.size = Pt(10)
    for row_idx, row in enumerate(table.rows):
        if header_row and row_idx == 0:
            continue
        for cell in row.cells:
            for paragraph in cell.paragraphs:
                for run in paragraph.runs:
                    run.font.name = 'Calibri'
                    run.font.size = Pt(10)
                    run.font.color.rgb = RGBColor(0x33, 0x33, 0x33)
    for r, row_data in enumerate(data):
        for c, cell_text in enumerate(row_data):
            if r < len(table.rows) and c < len(table.rows[r].cells):
                table.rows[r].cells[c].text = str(cell_text)
    return table


def insert_logo(doc, logo_path: str):
    if os.path.exists(logo_path):
        section = doc.sections[0]
        header = section.header
        header.is_linked_to_previous = False
        p = header.paragraphs[0] if header.paragraphs else header.add_paragraph()
        p.alignment = WD_ALIGN_PARAGRAPH.RIGHT
        run = p.add_run()
        run.add_picture(logo_path, width=Cm(LOGO_WIDTH_CM), height=Cm(LOGO_HEIGHT_CM))


def set_document_defaults(doc):
    style = doc.styles['Normal']
    font = style.font
    font.name = 'Calibri'
    font.size = Pt(11)
    font.color.rgb = RGBColor(0x33, 0x33, 0x33)
    for section in doc.sections:
        section.top_margin = Cm(2.54)
        section.bottom_margin = Cm(2.54)
        section.left_margin = Cm(2.54)
        section.right_margin = Cm(2.54)


def build_document():
    doc = Document()
    set_document_defaults(doc)
    insert_logo(doc, LOGO_PATH)

    # ── COVER ─────────────────────────────────────────────────────────────
    for _ in range(4):
        doc.add_paragraph()
    title = doc.add_paragraph(); title.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = title.add_run("BELEIDSDOCUMENT VOORJAARSCONVENTIE 2027")
    r.font.size = Pt(28); r.font.bold = True
    r.font.color.rgb = RGBColor(0x00, 0x33, 0x66); r.font.name = 'Calibri'
    subtitle = doc.add_paragraph(); subtitle.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = subtitle.add_run(f"District {DISTRICT}  —  {CONVENTIE_MAAND}")
    r.font.size = Pt(16); r.font.color.rgb = RGBColor(0x00, 0x66, 0x99); r.font.name = 'Calibri'
    doc.add_paragraph()
    version = doc.add_paragraph(); version.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = version.add_run("Versie 1.0 — Beleidsdocument (MASTER_SOP §3.5)")
    r.font.size = Pt(12); r.font.italic = True
    r.font.color.rgb = RGBColor(0x66, 0x66, 0x66); r.font.name = 'Calibri'
    doc.add_paragraph(); doc.add_paragraph()
    gov = doc.add_paragraph(); gov.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = gov.add_run(f"Governance: LCI Constitution Chapter XVII · {GOVERNANCE}")
    r.font.size = Pt(10); r.font.color.rgb = RGBColor(0x66, 0x66, 0x66); r.font.name = 'Calibri'
    thema = doc.add_paragraph(); thema.alignment = WD_ALIGN_PARAGRAPH.CENTER
    r = thema.add_run(f"Jaarthema 2026–2027: \"{THEMA_2026_2027}\"")
    r.font.size = Pt(11); r.font.italic = True
    r.font.color.rgb = RGBColor(0x00, 0x66, 0x99); r.font.name = 'Calibri'
    doc.add_page_break()

    # ── DOCUMENTBEHEER ────────────────────────────────────────────────────
    add_heading_styled(doc, "Documentbeheer", level=1)
    add_heading_styled(doc, "Versiehistorie", level=2)
    versie_data = [
        ["Versie", "Datum", "Auteur", "Status / Wijziging"],
        ["1.0", date.today().isoformat(), "District 110AN — Conventiecommissie / Cabinet Secretary",
         "Initiële versie ter vaststelling door Gouverneursraad"],
    ]
    add_table_with_style(doc, len(versie_data), 4, versie_data, col_widths=[2, 3, 6.5, 6])
    add_heading_styled(doc, "Goedkeuring", level=2)
    goedkeuring_data = [
        ["Functie", "Naam", "Datum", "Handtekening"],
        ["District Governor 2026–2027 (voorzitter)", "", "", ""],
        ["Voorzitter Gouverneursraad", "", "", ""],
        ["Cabinet Secretary 2026–2027", "", "", ""],
    ]
    add_table_with_style(doc, len(goedkeuring_data), 4, goedkeuring_data, col_widths=[6.5, 4.5, 2.5, 4])
    add_heading_styled(doc, "Status document", level=2)
    add_bullet(doc, "Dit beleidsdocument is bindend voor alle betrokkenen bij de organisatie en uitvoering van de voorjaarsconventie van District 110AN in april 2027.")
    add_bullet(doc, "Geldigheidsduur: tot afronding van de evaluatie na de voorjaarsconventie 2027, en als leidraad voor de eerstvolgende roulatie.")
    add_bullet(doc, "Review: na de evaluatie van de conventie (uiterlijk 8 weken na conventiedatum), vóór overdracht aan de volgende organiserende club conform roulatieschema.")
    add_bullet(doc, "Detailuitwerkingen (processtappen, templates, planning) staan in de Procedure en het Projectplan (PVA); dit beleidsdocument is leidend bij conflicten.")
    doc.add_page_break()

    # ── INHOUDSOPGAVE ─────────────────────────────────────────────────────
    add_heading_styled(doc, "Inhoudsopgave", level=1)
    for item in [
        "1. Doel & Scope", "2. Governance Grondslag", "3. Organisatiestructuur",
        "4. Basisprincipes", "5. Stafverantwoordelijkheden (RACI-samenvatting)",
        "6. Financieel Kader", "7. Communicatie", "8. Kwaliteitsborging",
        "9. Wijzigingsprocedure", "Bijlage A: Controleset Feiten", "Bijlage B: Bronnenregister",
    ]:
        add_body_text(doc, item, bold=True)
    doc.add_page_break()

    # ── 1. DOEL & SCOPE ───────────────────────────────────────────────────
    add_heading_styled(doc, "1. Doel & Scope", level=1)
    add_heading_styled(doc, "1.1 Doel", level=2)
    add_body_text(doc, (
        f"Dit beleidsdocument vestigt het kader voor de organisatie, besluitvorming en uitvoering van de "
        f"officiële LCI District Convention — de voorjaarsconventie — van District {DISTRICT} in {CONVENTIE_MAAND}. "
        "Het beleid waarborgt dat de conventie conform de internationale en districtse regels wordt voorbereid "
        "en gehouden, met rechtsgeldige stemmingen, transparante besluitvorming en een zorgvuldige financiële "
        "verantwoording."
    ))
    add_heading_styled(doc, "1.2 Scope", level=2)
    add_bullet(doc, f"District: {DISTRICT} (Multiple District 110 Nederland)")
    add_bullet(doc, f"Conventietype: {CONVENTIE_TYPE} — officiële jaarvergadering van het district")
    add_bullet(doc, f"Datum: {CONVENTIE_MAAND} (exacte datum conform roulatieschema Gouverneursraad)")
    add_bullet(doc, "Locatie: conform roulatieschema (vastgesteld door de Gouverneursraad op 6 mei 2022)")
    add_bullet(doc, f"Organisator: District {DISTRICT} met Conventiecommissie en Host Club (organiserende club)")
    add_bullet(doc, "Geldigheid: gehele conventiecyclus — voorbereiding, uitvoering, evaluatie en overdracht")
    add_heading_styled(doc, "1.3 Uitsluitingen", level=2)
    add_bullet(doc, "Dit beleidsdocument regelt geen operationele detailuitvoering; zie hiervoor de Procedure en het Projectplan (PVA).")
    add_bullet(doc, "Najaarsconventies (Ledenontmoetings- en inspiratieavonden) vallen buiten de scope van dit beleid.")
    add_bullet(doc, "De Nationale Conventie en het Europaforum vallen onder verantwoordelijkheid van MD 110 respectievelijk de Gouverneursraad.")
    doc.add_page_break()

    # ── 2. GOVERNANCE GRONDSLAG ───────────────────────────────────────────
    add_heading_styled(doc, "2. Governance Grondslag", level=1)
    add_body_text(doc, (
        "De voorjaarsconventie van District 110AN is een officiële District Convention binnen "
        "Lions Clubs International en berust op het onderstaande meerlagenstelsel. Bij conflicten "
        "is het hogere niveau leidend."
    ))
    gronddata = [
        ["Niveau", "Grondslag", "Relevantie voor de conventie"],
        ["Internationaal", "LCI Constitution & By-Laws — Chapter XVII (District Conventions)",
         "Vereisten voor districtconventies: oproeping, stemrechten, verkiezingen DG/VDG, goedkeuring werkplan en begroting"],
        ["Multiple District", "Huishoudelijk Reglement MD 110 Nederland, mei 2021 — Sectie 1 Conventiereglement",
         "Oproeping (≥ 30 dagen vooraf), aanmelding (uiterlijk 48 uur vooraf), aanwijzing stemgerechtigden (uiterlijk 24 uur vooraf), spreekrecht, voorstellen, stembureau en stemmingprocedures"],
        ["District", "Regels en besluiten van District 110AN / Gouverneursraad",
         "Roulatieschema voorjaarsconventies (vastgesteld 6 mei 2022), districtsagenda, districtbegroting met conventie-post"],
        ["Project", "Dit beleidsdocument + Procedure + Projectplan (PVA)",
         "Operationele uitwerking onder verantwoordelijkheid van de District Governor en de Conventiecommissie"],
    ]
    add_table_with_style(doc, len(gronddata), 3, gronddata, col_widths=[3, 6, 9])
    add_heading_styled(doc, "2.1 Kerneisen uit de grondslag", level=2)
    kern = [
        ["Eis", "Regel", "Bron"],
        ["Oproeping", "Ten minste 30 dagen voor de conventie aan alle clubs, clubs in oprichting, Lions en Leoclubs in het MD", "HR art. 2.1"],
        ["Aanmelding stemrecht", "Lid meldt zich uiterlijk 48 uur voor aanvang aan; club wijst stemgerechtigde uiterlijk 24 uur voor aanvang aan", "HR art. 2.1–2.2"],
        ["Stemrecht", STEMRECHT_REGEL, "LCI Statuten / HR"],
        ["Quorum", QUORUM_REGEL + " (voor de meeste artikelen)", "HR"],
        ["Spreekrecht", "Alleen aangewezen stemgerechtigden; leden van MD-organen en MD-stichtingen hebben spreekrecht", "HR art. 2.3–2.4"],
        ["Verkiezingen", "Kandidaatstelling DG/VDG uiterlijk 30 dagen voor de conventie; stemming via stembiljetten", "HR art. 10, 17"],
        ["Conventiestukken", "Digitaal verstrekken uiterlijk 20 dagen voor de conventie", "HR art. 7.1"],
        ["Notulen", "Vaststelling volgende kabinetsvergadering (conform HR)", "HR art. 21.4"],
    ]
    add_table_with_style(doc, len(kern), 3, kern, col_widths=[4, 9.5, 4])
    doc.add_page_break()

    # ── 3. ORGANISATIESTRUCTUUR ───────────────────────────────────────────
    add_heading_styled(doc, "3. Organisatiestructuur", level=1)
    add_body_text(doc, (
        "De organisatie van de voorjaarsconventie kent de volgende structuur. De District Governor "
        "is voorzitter van de conventie en eindverantwoordelijk; besluitvorming over statutaire "
        "zaken ligt uiteindelijk bij de conventie zelf en de Gouverneursraad."
    ))
    orgdata = [
        ["Rol", "Functie", "Verantwoordelijkheid (samengevat)"],
        ["Voorzitter", "District Governor (DG) 2026–2027",
         "Eindverantwoordelijk voor de conventie; leidt de plenaire vergadering; vertegenwoordigt het district naar LCI en internationale gasten"],
        ["Ondersteuning bestuur", "Vice District Governors (1e en 2e VDG)",
         "Ondersteunen de DG; bereiden verkiezingen en werkplanpresentaties voor; deelnemen aan de Conventiecommissie"],
        ["Secretariaat", "Cabinet Secretary 2026–2027",
         "Oproeping, agenda, conventiestukken, registratie van stemgerechtigden, notulen en besluitenlijst; vaste aanspreekpunt samen met de ambtelijk secretaris"],
        ["Financiën", "Cabinet Treasurer 2026–2027",
         "Bewaking conventiebudget conform districtbegroting; financiële verantwoording na de conventie"],
        ["Uitvoering", "Conventiecommissie",
         "Operationele organisatie: locatie, logistiek, programma, registratie, vlaggenceremonie, catering en exploitatie"],
        ["Gastheerschap", "Host Club (organiserende club, conform roulatieschema)",
         "Lokale gastvrijheid, vrijwilligers, logistieke ondersteuning op locatie; overdracht van lessen geleerd aan de volgende organiserende club"],
        ["Toezicht stemproces", "Stembureau (benoemd door Gouverneursraad)",
         "Telling van stemmen, verificatie quorum, vaststelling uitslagen (conform HR)"],
        ["Overlegorgaan", "Gouverneursraad (MD-niveau)",
         "Benoemt stembureau; behandelt escalaties en gevallen niet voorzien in het reglement (HR art. 18)"],
    ]
    add_table_with_style(doc, len(orgdata), 3, orgdata, col_widths=[3.5, 5, 9])
    add_heading_styled(doc, "3.1 Verantwoordingslijnen", level=2)
    add_bullet(doc, "De Conventiecommissie en Host Club rapporteren aan de DG en het kabinet (maandelijkse voortgangsinformatie).")
    add_bullet(doc, "De DG en Cabinet Secretary zijn de 'linking pin' richting de Gouverneursraad en LCI (conform Handboek Lions Conventies).")
    add_bullet(doc, "Het stembureau werkt onafhankelijk en rapporteert uitsluitend aan de conventievoorzitter en de plenaire vergadering.")
    doc.add_page_break()

    # ── 4. BASISPRINCIPES ─────────────────────────────────────────────────
    add_heading_styled(doc, "4. Basisprincipes", level=1)
    add_body_text(doc, "De organisatie en besluitvorming van de voorjaarsconventie zijn gebaseerd op vier basisprincipes:")
    principes = [
        ("Transparantie", (
            "Alle besluitvorming is navolgbaar: oproeping, agenda, voorstellen, begroting en stemuitslagen "
            "worden tijdig en volledig gecommuniceerd aan alle clubs en leden. Conventiestukken worden "
            "uiterlijk 20 dagen voor de conventie digitaal verstrekt (HR art. 7.1). Financiële verantwoording "
            "wordt na de conventie openbaar gemaakt binnen het district.")),
        ("Gelijkwaardigheid", (
            "Elke club heeft stemrecht conform de vaste regel (1 stem per 10 leden, minimaal 1, maximaal 5). "
            "Alle leden hebben onder gelijke voorwaarden toegang tot de conventie; toegang zonder stemrecht "
            "is gewaarborgd voor niet-aangewezen leden (spreekrecht conform HR).")),
        ("Ledenbetrokkenheid", (
            "Clubs en leden kunnen actief bijdragen via voorstellen, amendementen, resoluties en moties "
            "(conform de inzendingstermijnen in het HR). Het programma biedt ruimte voor ontmoeting, "
            "inspiratie en uitwissaling tussen clubs, zones en internationale gasten.")),
        ("Continuïteit", (
            "Kennis en ervaring worden overgedragen aan de volgende organiserende club conform het "
            "roulatieschema: evaluatierapport, overdrachtsmap en archivering (ownCloud / LionsBase) "
            "zijn vaste onderdelen van de afsluiting van elke conventiecyclus.")),
    ]
    for naam, tekst in principes:
        add_heading_styled(doc, naam, level=2)
        add_body_text(doc, tekst)
    doc.add_page_break()

    # ── 5. STAFVERANTWOORDELIJKHEDEN ──────────────────────────────────────
    add_heading_styled(doc, "5. Stafverantwoordelijkheden (RACI-samenvatting)", level=1)
    add_body_text(doc, (
        "Onderstaande RACI-matrix vat de verantwoordelijkheden op beleidsniveau samen. "
        "R = Responsible (uitvoert), A = Accountable (eindverantwoordelijk), C = Consulted, I = Informed. "
        "De gedetailleerde taakverdeling en planning staan in het Projectplan (PVA)."
    ), italic=True)
    raci = [
        ["Beleidsterrein", "DG", "VDG's", "Cab. Secretary", "Cab. Treasurer", "Conventiecommissie", "Host Club", "Stembureau", "GR"],
        ["Datum & locatie (roulatie)", "A", "C", "C", "C", "R", "C", "—", "C"],
        ["Oproeping & conventiestukken", "A", "I", "R", "I", "C", "I", "—", "I"],
        ["Budget & financiën", "A", "I", "C", "R", "C", "C", "—", "I"],
        ["Programma & logistiek", "A", "C", "C", "C", "R", "R", "—", "I"],
        ["Verkiezingen & stemproces", "A", "C", "C", "—", "I", "I", "R", "A*"],
        ["Communicatie (NL/EN)", "A", "C", "R", "I", "R", "C", "—", "I"],
        ["Evaluatie & overdracht", "A", "R", "R", "R", "R", "R", "C", "I"],
    ]
    add_table_with_style(doc, len(raci), len(raci[0]), raci,
                         col_widths=[4.5, 1.3, 1.3, 1.7, 1.7, 2.2, 1.7, 1.6, 1.2])
    add_body_text(doc, "* De Gouverneursraad benoemt het stembureau en is eindverantwoordelijk voor het stemproces conform het Huishoudelijk Reglement.", italic=True, size=Pt(9))
    doc.add_page_break()

    # ── 6. FINANCIEEL KADER ───────────────────────────────────────────────
    add_heading_styled(doc, "6. Financieel Kader", level=1)
    add_heading_styled(doc, "6.1 Begrotingsgrondslag", level=2)
    add_bullet(doc, f"De conventie wordt begroot conform de districtbegroting, met de conventie-post als referentie: {BUDGET_POST}.")
    add_bullet(doc, "Inkomsten: registratiebijdragen deelnemers, eventuele sponsoring en bijdragen vanuit het district.")
    add_bullet(doc, "Het streven is een deugdelijke exploitatie: geen negatief eindresultaat voor het district; overschotten vallen ten goede aan het district.")
    add_heading_styled(doc, "6.2 Beheersregels", level=2)
    add_bullet(doc, "De Cabinet Treasurer bewaakt het budget en rapporteert maandelijks aan DG en kabinet.")
    add_bullet(doc, "Uitgaven boven het door de Gouverneursraad vastgestelde plafond vereisen voorafgaande goedkeuring van de DG.")
    add_bullet(doc, "Alle facturen en betalingen worden gedocumenteerd bewaard in het conventie-archief.")
    add_heading_styled(doc, "6.3 Verantwoording", level=2)
    add_bullet(doc, "Binnen 2 weken na de conventie: financiële afsluiting door de Cabinet Treasurer (budget versus realisatie).")
    add_bullet(doc, "Afwijkingen worden toegelicht en meegenomen in de evaluatie en de overdracht aan de volgende organiserende club.")
    add_bullet(doc, "De eindafrekening wordt gerapporteerd aan de kabinetsvergadering en opgenomen in de jaarverantwoording van het district.")
    doc.add_page_break()

    # ── 7. COMMUNICATIE ───────────────────────────────────────────────────
    add_heading_styled(doc, "7. Communicatie", level=1)
    add_heading_styled(doc, "7.1 Officiële kanalen", level=2)
    comm = [
        ["Kanaal", "Doel", "Beheerder"],
        ["Officiële oproeping (e-mail/post aan clubs)", "Wettelijk vereiste kennisgeving conform HR art. 2.1", "Cabinet Secretary"],
        ["Districtswebsite / LionsBase", "Publicatie agenda, stukken, registratie", "Cabinet Secretary / Conventiecommissie"],
        ["Conventiestukken digitaal (ownCloud / e-mail)", "Distributie voorstellen, amendementen, adviezen (T-20 dagen)", "Cabinet Secretary"],
        ["Districtsnieuwsbrief & clubcirculaires", "Herinneringen, praktische informatie, programma", "Conventiecommissie"],
    ]
    add_table_with_style(doc, len(comm), 3, comm, col_widths=[6, 7.5, 4])
    add_heading_styled(doc, "7.2 Talen", level=2)
    add_bullet(doc, "De officiële voertaal van de conventie is Nederlands.")
    add_bullet(doc, "Kernstukken (oproeping, agenda, programma) worden tevens in het Engels beschikbaar gesteld vanwege internationale gasten en relaties (NL/EN).")
    add_bullet(doc, "Presentaties en toespraken van internationale gasten mogen in het Engels; afstemming over spreektijd en voertaal vindt vooraf plaats.")
    add_heading_styled(doc, "7.3 Timing (beleidskader)", level=2)
    timing = [
        ["Moment", "Communicatie-actie"],
        ["T-6 maanden", "Aankondiging conventie (datum, locatie) in districtskanalen"],
        ["T-30 dagen", "Officiële oproeping met volledige agenda en voorstellen (verplicht conform HR)"],
        ["T-20 dagen", "Conventiestukken digitaal verstrekt (verplicht conform HR art. 7.1)"],
        ["T-14 / T-7 / T-3 dagen", "Herinneringen registratie en aanwijzing stemgerechtigden"],
        ["Tijdens conventie", "Actuele informatie via conventiecommissie / locatie-omroep"],
        ["T+4 weken", "Notulen, besluitenlijst en bedankje aan deelnemers en vrijwilligers"],
    ]
    add_table_with_style(doc, len(timing), 2, timing, col_widths=[4, 13.5])
    doc.add_page_break()

    # ── 8. KWALITEITSBORGING ──────────────────────────────────────────────
    add_heading_styled(doc, "8. Kwaliteitsborging", level=1)
    add_heading_styled(doc, "8.1 QC-gates (Go/No-Go momenten)", level=2)
    add_body_text(doc, (
        "De voorbereiding kent vaste kwaliteitscontroles (QC-gates). Elke gate moet zijn behaald "
        "voordat de volgende fase start; de volledige uitwerking staat in de Procedure."
    ))
    gates = [
        ["Gate", "Moment", "Criterium (samengevat)"],
        ["QC-1 Plan", "T-12/T-9 maanden", "Datum, locatie en organisatiestructuur vastgesteld conform roulatie"],
        ["QC-2 Stukken", "T-30/T-20 dagen", "Oproeping verzonden (≥ 30 dagen); conventiestukken digitaal beschikbaar (20 dagen)"],
        ["QC-3 Registratie", "T-14/T-7 dagen", "Stembureau benoemd en geïnstrueerd; stemgerechtigden per club in beeld (quorum-indicator)"],
        ["QC-4 Uitvoering", "T-3/T-1 dagen", "Logistiek, materiaal en personeel 100% gereed (runbook afgerond)"],
        ["QC-5 Afsluiting", "T+2/T+8 weken", "Financiële afsluiting, evaluatierapport en archivering compleet; overdracht volgende club"],
    ]
    add_table_with_style(doc, len(gates), 3, gates, col_widths=[3, 3.5, 11])
    add_heading_styled(doc, "8.2 Evaluatie na de conventie", level=2)
    add_bullet(doc, "Binnen 3 weken na de conventie vindt een evaluatiebijeenkomst plaats (DG, kabinet, Conventiecommissie, Host Club).")
    add_bullet(doc, "De evaluatie dekt minimaal: proces (termijnen, HR-naleving), programma en tevredenheid deelnemers, financiën en communicatie.")
    add_bullet(doc, "Resultaat: een evaluatierapport met lessen geleerd, dat onderdeel is van de overdrachtsmap voor de volgende roulatie.")
    add_bullet(doc, "De evaluatie wordt gepresenteerd in de eerstvolgende kabinetsvergadering en bewaard in het districtsarchief.")
    doc.add_page_break()

    # ── 9. WIJZIGINGSPROCEDURE ────────────────────────────────────────────
    add_heading_styled(doc, "9. Wijzigingsprocedure", level=1)
    add_heading_styled(doc, "9.1 Afwijkingen van dit beleid", level=2)
    add_body_text(doc, (
        "Afwijkingen van dit beleidsdocument zijn alleen geldig na schriftelijke vastlegging en "
        "goedkeuring via onderstaand escalatiepad. Afwijkingen mogen nooit in strijd zijn met het "
        "Huishoudelijk Reglement of de LCI Constitution; in voorkomende gevallen beslist het hogere niveau."
    ))
    escalatie = [
        ["Niveau", "Bevoegd voor goedkeuring", "Categorie afwijking", "Termijn"],
        ["1", "Cabinet Secretary / Projectleider", "Laag: logistieke details zonder gevolgen voor geldigheid of termijnen", "24 uur"],
        ["2", "District Governor (conventievoorzitter)", "Middelmatig: termijn- of programmawijzigingen zonder statutair effect", "Direct (conventiedag) / 4 uur"],
        ["3", "Gouverneursraad", "Hoog: afwijkingen met risico voor quorum, stemgerechtigheid of geldigheid besluiten", "Binnen 24 uur / eerstvolgende vergadering"],
        ["4", "Plenaire conventievergadering", "Gevallen niet voorzien in het reglement (HR art. 18); ordevoorstellen", "Direct tijdens conventie"],
    ]
    add_table_with_style(doc, len(escalatie), 4, escalatie, col_widths=[1.5, 4.5, 8, 3.5])
    add_heading_styled(doc, "9.2 Versiebeheer van het beleidsdocument", level=2)
    add_bullet(doc, "Wijzigingen van dit beleidsdocument worden vastgelegd in de versiehistorie (hoofdstuk Documentbeheer) met datum, auteur en wijziging.")
    add_bullet(doc, "Structurele wijzigingen worden vastgesteld door de Gouverneursraad, op voorstel van de DG en het kabinet.")
    add_bullet(doc, "Na de evaluatie van de voorjaarsconventie 2027 wordt dit beleid herzien voor de volgende roulatie.")
    add_bullet(doc, "Alle geldige versies worden gearchiveerd in ownCloud / LionsBase; alleen de laatst vastgestelde versie is leidend.")
    doc.add_page_break()

    # ── BIJLAGE A: CONTROLESET FEITEN ─────────────────────────────────────
    add_heading_styled(doc, "Bijlage A: Controleset Feiten (TierVerify §17)", level=1)
    add_body_text(doc, "Deze feiten moeten in alle deliverables consistent terugkomen:", italic=True)
    controleset = [
        ["Feit", "Waarde", "Bron"],
        ["District", DISTRICT, "District 110AN Club Health Assessment (sept 2025)"],
        ["Conventietype", CONVENTIE_TYPE, "Handboek Lions Conventies vs 06 20240729"],
        ["Datum", CONVENTIE_MAAND + " (exacte datum conform roulatie)", "Conventie Roulatieschema"],
        ["Locatie", "Conform roulatieschema (GR, vastgesteld 6 mei 2022)", "Conventie Roulatieschema"],
        ["Governance", GOVERNANCE, "Huishoudelijk Reglement MD 110 mei 2021"],
        ["Stemrechten", STEMRECHT_REGEL, "Huishoudelijk Reglement / LCI Statuten"],
        ["Quorum", QUORUM_REGEL, "Huishoudelijk Reglement"],
        ["Budget referentie", "Districtbegroting 25-26: post '1 Districtsconventie' € 3.500", "Districtbegroting 110 AN 25-26"],
        ["Voorzitter", "District Governor 2026–2027", "Werkplan AN 2026-2027 draft v1"],
        ["Secretaris", "Cabinet Secretary 2026–2027", "Werkplan AN 2026-2027 draft v1"],
        ["Thema FY 2026–2027", THEMA_2026_2027, "Werkplan AN 2026-2027 draft v1"],
        ["Leden district (referentie)", "± 1.200 (Districtbegroting 25-26)", "Districtbegroting 110 AN 25-26"],
    ]
    add_table_with_style(doc, len(controleset), 3, controleset, col_widths=[5, 7, 5.5])
    doc.add_page_break()

    # ── BIJLAGE B: BRONNENREGISTER ────────────────────────────────────────
    add_heading_styled(doc, "Bijlage B: Bronnenregister (Meta-Dossier v1)", level=1)
    bronnen = [
        ["Bron", "Type", "Gebruik in dit beleid"],
        ["Handboek Lions Conventies vs 06 (2024-07-29)", "LCI-handboek", "Conventie-eisen, organisatie-structuur, verkiezingen"],
        ["Huishoudelijk Reglement MD 110 Nederland (mei 2021)", "Reglement", "Oproeping, stemrecht, quorum, stembureau, procedures"],
        ["Conventie Roulatieschema (GR, 6 mei 2022)", "Besluit GR", "Datum- en locatieroulatie voorjaarsconventies"],
        ["Districtbegroting 110 AN 25-26", "Begroting", "Financieel kader en conventie-post (€ 3.500)"],
        ["Werkplan AN 2026-2027 draft v1", "Werkplan", "Thema, kabinet 2026-2027, activiteitenkalender"],
        ["Werkplan AN 2025-2026", "Werkplan", "Context huidig werkjaar"],
        ["District 110AN Club Health Assessment (sept 2025)", "Rapportage", "Ledenaantallen district (basis stemrechtberekening)"],
        ["Verslag Najaarsconventie 110AN (17 oktober 2024)", "Verslag", "Precedenten en lessen geleerd"],
    ]
    add_table_with_style(doc, len(bronnen), 3, bronnen, col_widths=[7, 3, 7.5])

    # ── COLOFON ───────────────────────────────────────────────────────────
    doc.add_page_break()
    add_heading_styled(doc, "Colofon", level=1)
    for item in [
        "Document: Beleidsdocument Voorjaarsconventie 2027 v1.0",
        f"District: {DISTRICT}",
        f"Datum aanmaak: {date.today().isoformat()}",
        f"Governance basis: LCI Constitution Chapter XVII · {GOVERNANCE}",
        "Auteur: District Governor 2026-2027 / Cabinet Secretary / Conventiecommissie",
        "Versiebeheer: v1.0 — initiële versie ter vaststelling Gouverneursraad",
        "Volgende review: na evaluatie voorjaarsconventie 2027 (voor de volgende roulatie)",
    ]:
        add_bullet(doc, item)

    return doc


def main():
    output_path = "/root/projects/jg/2026-voorjaarsconventie-110an/deliverables/docx/Beleidsdocument_Voorjaarsconventie_2027_v1.0.docx"
    os.makedirs(os.path.dirname(output_path), exist_ok=True)
    doc = build_document()
    doc.save(output_path)
    print(f"Document gegenereerd: {output_path}")


if __name__ == "__main__":
    main()
