#!/usr/bin/env python3
"""Generate PM_VBS05_03 — MoC Classificatie- & Risicomatrix (DOCX + XLSX)"""

import os
from docx import Document
from docx.shared import Inches, Cm, Pt, Emu, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.enum.table import WD_TABLE_ALIGNMENT
from docx.oxml.ns import qn, nsdecls
from docx.oxml import parse_xml
import openpyxl
from openpyxl.styles import Font, PatternFill, Alignment, Border, Side
from openpyxl.utils import get_column_letter

# ── Constants ──
PRIMARY = RGBColor(0x00, 0x33, 0x66)
TEXT_DARK = RGBColor(0x1F, 0x2F, 0x37)
WHITE = RGBColor(0xFF, 0xFF, 0xFF)
DATE_STR = "29-04-2026"
LOGO_PATH = "/root/projects/jg/assets/branding/jvg-logo-white-medium.png"

DOCX_OUT = "/root/projects/jg/2026-PM-VBS-Element5/deliverables/docx/PM_VBS05_03_Classificatie_Risicomatrix_v1.0.docx"
XLSX_OUT = "/root/projects/jg/2026-PM-VBS-Element5/deliverables/xlsx/PM_VBS05_03_Classificatie_Risicomatrix_Register_v1.0.xlsx"

# ── Helper functions ──
def set_cell_shading(cell, hex_color):
    shading = parse_xml(f'<w:shd {nsdecls("w")} w:fill="{hex_color}"/>')
    cell._tc.get_or_add_tcPr().append(shading)

def set_cell_border(cell, **kwargs):
    tc = cell._tc
    tcPr = tc.get_or_add_tcPr()
    tcBorders = parse_xml(f'<w:tcBorders {nsdecls("w")}>'
        f'<w:top w:val="single" w:sz="4" w:space="0" w:color="E5E7EB"/>'
        f'<w:bottom w:val="single" w:sz="4" w:space="0" w:color="E5E7EB"/>'
        f'<w:left w:val="single" w:sz="4" w:space="0" w:color="E5E7EB"/>'
        f'<w:right w:val="single" w:sz="4" w:space="0" w:color="E5E7EB"/>'
        f'</w:tcBorders>')
    tcPr.append(tcBorders)

def add_header_row(table, texts):
    row = table.rows[0]
    for i, text in enumerate(texts):
        cell = row.cells[i]
        cell.text = ""
        p = cell.paragraphs[0]
        run = p.add_run(text)
        run.bold = True
        run.font.color.rgb = WHITE
        run.font.size = Pt(10)
        run.font.name = "Calibri"
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        set_cell_shading(cell, "003366")

def add_table_row(table, texts, zebra=False):
    row = table.add_row()
    for i, text in enumerate(texts):
        cell = row.cells[i]
        cell.text = ""
        p = cell.paragraphs[0]
        run = p.add_run(str(text))
        run.font.size = Pt(10)
        run.font.name = "Calibri"
        run.font.color.rgb = TEXT_DARK
        if zebra:
            set_cell_shading(cell, "F3F4F6")
        set_cell_border(cell)

def add_para(doc, text, style="Normal", bold=False, size=11, color=None, align=None, space_after=6):
    p = doc.add_paragraph()
    run = p.add_run(text)
    run.font.name = "Calibri"
    run.font.size = Pt(size)
    run.bold = bold
    if color:
        run.font.color.rgb = color
    if align:
        p.alignment = align
    p.paragraph_format.space_after = Pt(space_after)
    return p

def add_heading(doc, text, level=1):
    h = doc.add_heading(text, level=level)
    for run in h.runs:
        run.font.color.rgb = PRIMARY
        run.font.name = "Calibri"
    return h

# ═══════════════════════════════════════════
# DOCX GENERATION
# ═══════════════════════════════════════════
def generate_docx():
    doc = Document()
    
    # Default font
    style = doc.styles['Normal']
    style.font.name = "Calibri"
    style.font.size = Pt(11)
    style.font.color.rgb = TEXT_DARK
    
    # ── Voorblad ──
    for _ in range(4):
        doc.add_paragraph()
    
    # Logo
    if os.path.exists(LOGO_PATH):
        p = doc.add_paragraph()
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
        run = p.add_run()
        run.add_picture(LOGO_PATH, width=Cm(2.0), height=Cm(1.8))
    
    add_para(doc, "JvG Consultancy", bold=True, size=16, color=PRIMARY, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=2)
    add_para(doc, "Safety • Governance • Advisory", size=11, color=PRIMARY, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=20)
    
    add_para(doc, "Management of Change", bold=True, size=22, color=PRIMARY, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=4)
    add_para(doc, "Classificatie- & Risicomatrix", bold=True, size=18, color=PRIMARY, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=4)
    add_para(doc, "BRZO Element IV — VBS Element 5", size=12, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=2)
    add_para(doc, "Phoenix Metals", size=14, bold=True, color=PRIMARY, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=20)
    
    # Document metadata
    meta_lines = [
        "Documentcode: PM_VBS05_03",
        "Versie: 1.0",
        f"Datum: {DATE_STR}",
        "Status: Definitief",
        "Classificatie: Vertrouwelijk",
        "Auteur: JvG Consultancy"
    ]
    for line in meta_lines:
        add_para(doc, line, size=10, align=WD_ALIGN_PARAGRAPH.CENTER, space_after=2)
    
    doc.add_page_break()
    
    # ── Documentbeheersing ──
    add_heading(doc, "Documentbeheersing", 1)
    
    table = doc.add_table(rows=1, cols=5)
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    add_header_row(table, ["Versie", "Datum", "Auteur", "Wijziging", "Goedgekeurd"])
    add_table_row(table, ["1.0", DATE_STR, "JvG Consultancy", "Initiële release", "—"])
    
    add_para(doc, "", size=6)
    add_para(doc, "Distributielijst", bold=True, size=11, color=PRIMARY)
    table2 = doc.add_table(rows=1, cols=3)
    add_header_row(table2, ["Functie", "Naam", "Ontvangst"])
    add_table_row(table2, ["Directie", "Phoenix Metals Directie", "Digitaal"])
    add_table_row(table2, ["MoC-coördinator", "TBD", "Digitaal"], zebra=True)
    add_table_row(table2, ["HSE Manager", "TBD", "Digitaal"])
    
    doc.add_page_break()
    
    # ── Inhoudsopgave ──
    add_heading(doc, "Inhoudsopgave", 1)
    toc_items = [
        "1  Inleiding",
        "2  Beslisboom — Is het een MoC?",
        "3  Classificatie van Wijzigingstypen",
        "4  Risicomatrix (5×5)",
        "5  Veiligheidsstudie-indeling",
        "6  Goedkeuringsbevoegdheid Matrix",
        "7  Gebruiksaanwijzing XLSX Register",
        "Bronnenlijst",
        "TierVerify Log"
    ]
    for item in toc_items:
        add_para(doc, item, size=11, space_after=4)
    
    doc.add_page_break()
    
    # ═══════════════════════════════════
    # HOOFDSTUK 1: Inleiding
    # ═══════════════════════════════════
    add_heading(doc, "1  Inleiding", 1)
    
    add_para(doc, "1.1  Doel", bold=True, size=12, color=PRIMARY)
    add_para(doc, 
        "Dit document (PM_VBS05_03) voorziet in objectieve beslisinstrumenten voor het classificeren "
        "van wijzigingen binnen het Management of Change (MoC) systeem van Phoenix Metals. "
        "Het beantwoordt drie fundamentele vragen:[1][2]")
    
    bullets = [
        "Is de voorgestelde wijziging MoC-waardig? (Beslisboom)",
        "Wat is het risiconiveau van de wijziging? (Risicomatrix 5×5)",
        "Welke veiligheidsstudie en goedkeuring zijn vereist? (Classificatie- en goedkeuringsmatrix)"
    ]
    for b in bullets:
        p = doc.add_paragraph(b, style='List Bullet')
        for run in p.runs:
            run.font.name = "Calibri"
            run.font.size = Pt(11)
    
    add_para(doc, "1.2  Relatie met voorgaande documenten", bold=True, size=12, color=PRIMARY)
    add_para(doc,
        "Dit document is onderdeel van het VBS Element 5 (BRZO Element IV) documentenset:[3]")
    
    refs = [
        "PM_VBS05_01 — MoC Beleid & Kaderdocument: definieert het wettelijk kader, beleidsuitgangspunten en scope.",
        "PM_VBS05_02 — MoC Procedure: beschrijft het 9-fasen proces; Fase 2 (MoC-waardigheidtoets) verwijst direct naar de beslisboom en risicomatrix in dit document.",
        "PM_VBS05_03 — Dit document: classificatie- en risicomatrixinstrumenten."
    ]
    for r in refs:
        p = doc.add_paragraph(r, style='List Bullet')
        for run in p.runs:
            run.font.name = "Calibri"
            run.font.size = Pt(11)
    
    doc.add_page_break()
    
    # ═══════════════════════════════════
    # HOOFDSTUK 2: Beslisboom
    # ═══════════════════════════════════
    add_heading(doc, "2  Beslisboom — Is het een MoC?", 1)
    
    add_para(doc,
        "De beslisboom biedt een gestructureerde, reproduceerbare methode om te bepalen of een "
        "voorgestelde wijziging door het MoC-proces moet worden geleid. De beslisboom wordt "
        "toegepast in Fase 2 van de MoC-procedure (PM_VBS05_02, §3.2).[2]")
    
    # Beslisboom table
    table = doc.add_table(rows=1, cols=4)
    add_header_row(table, ["Stap", "Vraag", "Ja-pad", "Nee-pad"])
    
    steps = [
        ("1", "Is het een identieke vervanging (Replacement in Function — RiF)?", "→ Geen MoC vereist. Documenteer als RiF.", "→ Ga naar stap 2"),
        ("2", "Verandert er iets aan proces, installatie, organisatie of veiligheidsmaatregelen?", "→ Ga naar stap 3", "→ Geen MoC vereist. Geen wijziging."),
        ("3", "Is het routine-onderhoud dat volledig conform een bestaand, goedgekeurd onderhoudsprotocol wordt uitgevoerd?", "→ Geen MoC vereist. Documenteer als routinematig.", "→ Ga naar stap 4"),
        ("4", "Is er sprake van spoed? (4 criteria: ongeplande uitval, direct gevaar voor personen, milieurisico, of stilstand >24uur met veiligheidsimpact)", "→ Spoed-MoC. Volg verkorte procedure met verplichte achteraf-toetsing. [4]", "→ Ga naar stap 5"),
        ("5", "Raakt het wijziging veiligheidskritische equipment (SCE), veiligheidskritische procedures of SEH-elementen?", "→ MoC HOOG. Volledige procedure met verdiepte veiligheidsstudie.", "→ Standaard MoC. Standaard procedure.")
    ]
    for i, s in enumerate(steps):
        add_table_row(table, list(s), zebra=(i % 2 == 1))
    
    add_para(doc, "", size=6)
    add_para(doc, "Opmerking: Bij twijfel altijd MoC initiëren. Het principe 'in geval van twijfel, classificeer als MoC' is leidend.[3]", size=10)
    
    doc.add_page_break()
    
    # ═══════════════════════════════════
    # HOOFDSTUK 3: Classificatie van Wijzigingstypen
    # ═══════════════════════════════════
    add_heading(doc, "3  Classificatie van Wijzigingstypen", 1)
    
    add_para(doc,
        "Wijzigingen worden geclassificeerd naar type en standaard risiconiveau. Het risiconiveau "
        "bepaalt de minimale diepgang van de veiligheidsstudie. De daadwerkelijke risicoscore wordt "
        "bepaald via de 5×5 risicomatrix (hoofdstuk 4) en kan afwijken van het standaardniveau.[1][5]")
    
    types_data = [
        ("1. Procesparameter-wijziging", 
         "Temperatuurverhoging smeltoven +25°C; Drukbereik reactor aanpassen",
         "Procestijd uitbreiden buiten ontwerptraject",
         "Gemiddeld"),
        ("2. Apparatuur-/componentwijziging",
         "Pomp vervangen door ander type met gelijke capaciteit",
         "Warmtewisselaar met afwijkend materiaal",
         "Gemiddeld"),
        ("3. Proceschemie-/grondstofwijziging",
         "Legeringssamenstelling bronmateriaal wijzigen",
         "Alternatieve zuiveringschemicaliër invoeren",
         "Hoog"),
        ("4. Operationele procedure-wijziging",
         "Opstartprocedure aanpassen na optimalisatie",
         "Nieuwshift-overdrachtsformulier invoeren",
         "Laag"),
        ("5. Organisatorische wijziging",
         "Nieuwe ploegenstructuur (3- naar 4-ploegensysteem)",
         "Fusie van twee afdelingen tot één operationele eenheid",
         "Gemiddeld"),
        ("6. Veiligheidssysteemwijziging",
         "Storing-alarmdrempel gasdetectie aanpassen",
         "SIS logica wijzigen (trip-punt verleggen)",
         "Hoog"),
        ("7. Externe-factorwijziging",
         "Nieuwe wettelijke emissiegrens van kracht",
         "Aangrenzend terrein gewijzigd gebruik (bouwactiviteit)",
         "Gemiddeld"),
        ("8. Tijdelijke wijziging",
         "Tijdelijke bypass leiding voor 48 uur onderhoud",
         "Mobiele stoomketel inzetten tijdens revisie",
         "Gemiddeld"),
        ("9. Ontwerp-/constructiewijziging",
         "Nieuwe pijpleidingroute door andere locatie",
         "Fundamentsspecifikatie wijzigen voor opslagtank",
         "Hoog")
    ]
    
    table = doc.add_table(rows=1, cols=4)
    add_header_row(table, ["Wijzigingstype", "Voorbeelden", "Voorbeelden (vervolg)", "Standaard risiconiveau"])
    for i, t in enumerate(types_data):
        add_table_row(table, list(t), zebra=(i % 2 == 1))
    
    doc.add_page_break()
    
    # ═══════════════════════════════════
    # HOOFDSTUK 4: Risicomatrix 5×5
    # ═══════════════════════════════════
    add_heading(doc, "4  Risicomatrix (5×5)", 1)
    
    add_para(doc, "4.1  As-definities", bold=True, size=12, color=PRIMARY)
    
    # Likelihood table
    add_para(doc, "Likelihood (waarschijnlijkheid):", bold=True, size=11)
    lh_table = doc.add_table(rows=1, cols=3)
    add_header_row(lh_table, ["Score", "Categorie", "Omschrijving"])
    lh_data = [
        ("1", "Zeldzaam", "Eens in de >10 jaar; praktisch ondenkbaar onder normale omstandigheden"),
        ("2", "Onwaarschijnlijk", "Eens in de 5-10 jaar; mogelijk maar zeer onwaarschijnlijk"),
        ("3", "Mogelijk", "Eens in de 1-5 jaar; kan voorkomen onder specifieke omstandigheden"),
        ("4", "Waarschijnlijk", "Eens per jaar of frequenter; te verwachten in de levensduur van de installatie"),
        ("5", "Verwacht", "Meerdere keren per jaar; zal vrijwel zeker optreden")
    ]
    for i, d in enumerate(lh_data):
        add_table_row(lh_table, list(d), zebra=(i % 2 == 1))
    
    add_para(doc, "", size=4)
    
    # Impact table
    add_para(doc, "Impact (gevolg):", bold=True, size=11)
    im_table = doc.add_table(rows=1, cols=3)
    add_header_row(im_table, ["Score", "Categorie", "Omschrijving"])
    im_data = [
        ("1", "Verwaarloosbaar", "Geen letsel; beperkte schade <€10.000; geen milieu-impact"),
        ("2", "Gering", "Eerstehulpgeval; schade €10.000-€100.000; beperkte milieunormoverschrijding"),
        ("3", "Matig", "Ziekenhuisopname; schade €100.000-€1M; significante milieunormoverschrijding"),
        ("4", "Ernstig", "Blijvend letsel of meerdere gewonden; schade €1M-€10M; ernstige bodem/waterverontreiniging"),
        ("5", "Catastrofaal", "Doden of massa-slachtoffers; schade >€10M; domino-effect; catastrofaal milieufout")
    ]
    for i, d in enumerate(im_data):
        add_table_row(im_table, list(d), zebra=(i % 2 == 1))
    
    add_para(doc, "", size=6)
    add_para(doc, "4.2  Risicomatrix", bold=True, size=12, color=PRIMARY)
    add_para(doc, "Scoreberekening: Risicoscore = Likelihood × Impact. De resulterende score (1-25) wordt ingedeeld in vier risicogebieden:[5]")
    
    # Color mapping for risk scores
    def get_risk_color(score):
        if score <= 4: return "10B981"  # Laag - groen
        elif score <= 9: return "F59E0B"  # Gemiddeld - geel
        elif score <= 16: return "DC2626"  # Hoog - rood
        else: return "7F1D1D"  # Kritiek - donkerrood
    
    # 5x5 matrix
    matrix_table = doc.add_table(rows=6, cols=6)
    matrix_table.alignment = WD_TABLE_ALIGNMENT.CENTER
    
    # Header row
    matrix_table.cell(0, 0).text = "Likelihood ↓ / Impact →"
    for j in range(1, 6):
        matrix_table.cell(0, j).text = str(j)
    # Header col
    for i in range(1, 6):
        matrix_table.cell(i, 0).text = str(i)
    
    # Style headers
    for j in range(6):
        cell = matrix_table.cell(0, j)
        for p in cell.paragraphs:
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
            for run in p.runs:
                run.bold = True
                run.font.color.rgb = WHITE
                run.font.size = Pt(10)
                run.font.name = "Calibri"
        set_cell_shading(cell, "003366")
    
    for i in range(1, 6):
        cell = matrix_table.cell(i, 0)
        for p in cell.paragraphs:
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
            for run in p.runs:
                run.bold = True
                run.font.color.rgb = WHITE
                run.font.size = Pt(10)
                run.font.name = "Calibri"
        set_cell_shading(cell, "003366")
    
    # Fill matrix
    for li in range(1, 6):
        for im in range(1, 6):
            score = li * im
            cell = matrix_table.cell(li, im)
            cell.text = str(score)
            for p in cell.paragraphs:
                p.alignment = WD_ALIGN_PARAGRAPH.CENTER
                for run in p.runs:
                    run.bold = True
                    run.font.size = Pt(12)
                    run.font.name = "Calibri"
            set_cell_shading(cell, get_risk_color(score))
    
    add_para(doc, "", size=6)
    
    # Legend
    add_para(doc, "Legenda:", bold=True, size=11)
    legend_table = doc.add_table(rows=1, cols=4)
    legend_items = [
        ("Laag (1-4)", "10B981"),
        ("Gemiddeld (5-9)", "F59E0B"),
        ("Hoog (10-16)", "DC2626"),
        ("Kritiek (17-25)", "7F1D1D")
    ]
    for i, (label, color) in enumerate(legend_items):
        cell = legend_table.cell(0, i)
        cell.text = label
        for p in cell.paragraphs:
            p.alignment = WD_ALIGN_PARAGRAPH.CENTER
            for run in p.runs:
                run.bold = True
                run.font.size = Pt(10)
                run.font.name = "Calibri"
        set_cell_shading(cell, color)
    
    add_para(doc, "", size=6)
    
    # Scoregebieden acties
    add_para(doc, "4.3  Vereiste actie per scoregebied", bold=True, size=12, color=PRIMARY)
    actie_table = doc.add_table(rows=1, cols=4)
    add_header_row(actie_table, ["Risicogebied", "Score", "Vereiste actie", "Goedkeuringsniveau"])
    acties = [
        ("Laag", "1-4", "Checklist + Task Risk Analysis (TRA). Wijziging kan doorgang vinden na goedkeuring MoC-coördinator.", "MoC-coördinator"),
        ("Gemiddeld", "5-9", "What-If analyse of FMEA. MoC-team beoordeelt. Mitigerende maatregelen vereist voor score >6.", "MoC-team"),
        ("Hoog", "10-16", "HAZOP of FMECA. MoC-team + Directie goedkeuring. Mitigerende maatregelen verplicht.", "MoC-team + Directie"),
        ("Kritiek", "17-25", "QRA + HAZOP. Extern expert verplicht. Directie goedkeuring + externe validatie. ALARP-demonstratie vereist.", "Directie + Extern expert")
    ]
    for i, a in enumerate(acties):
        row = actie_table.add_row()
        for j, val in enumerate(a):
            cell = row.cells[j]
            cell.text = ""
            p = cell.paragraphs[0]
            run = p.add_run(str(val))
            run.font.size = Pt(10)
            run.font.name = "Calibri"
            run.font.color.rgb = TEXT_DARK
            if j == 0:
                colors = {"Laag": "10B981", "Gemiddeld": "F59E0B", "Hoog": "DC2626", "Kritiek": "7F1D1D"}
                run.bold = True
                set_cell_shading(cell, colors.get(val, "FFFFFF"))
                if val in ("Hoog", "Kritiek"):
                    run.font.color.rgb = WHITE
    
    doc.add_page_break()
    
    # ═══════════════════════════════════
    # HOOFDSTUK 5: Veiligheidsstudie-indeling
    # ═══════════════════════════════════
    add_heading(doc, "5  Veiligheidsstudie-indeling", 1)
    
    add_para(doc,
        "Op basis van de risicoscore uit de 5×5 matrix wordt het type veiligheidsstudie bepaald. "
        "De veiligheidsstudie vormt de kern van Fase 4 van de MoC-procedure (PM_VBS05_02, §3.4). "
        "De indeling volgens NEN-EN-IEC 62340 en NPR 7910-1 richtlijnen.[5][6]")
    
    vs_table = doc.add_table(rows=1, cols=5)
    add_header_row(vs_table, ["Risicoscore", "Type veiligheidsstudie", "Diepgang", "Goedkeuringsniveau", "Doorlooptijd indicator"])
    vs_data = [
        ("Laag (1-4)", "Checklist + Task Risk Analysis (TRA)", "Basis: identificatie van directe gevaren, PPT-maatregelen, werkinstructie-check", "MoC-coördinator", "1-2 werkdagen"),
        ("Gemiddeld (5-9)", "What-If / FMEA", "Gemiddeld: systematische identificatie van faalwijzen, oorzaak-gevolg analyse", "MoC-team", "3-5 werkdagen"),
        ("Hoog (10-16)", "HAZOP / FMECA", "Verdiept: gedetailleerde analyse van afwijkingen, kritische faalmodi met kwantitatieve elementen", "MoC-team + Directie", "1-2 weken"),
        ("Kritiek (17-25)", "QRA + HAZOP", "Maximum: kwantitatieve risicoanalyse (QRA), domeinanalyses, ALARP-demonstratie, externe validatie", "Directie + Extern expert", "2-4 weken")
    ]
    for i, d in enumerate(vs_data):
        add_table_row(vs_table, list(d), zebra=(i % 2 == 1))
    
    doc.add_page_break()
    
    # ═══════════════════════════════════
    # HOOFDSTUK 6: Goedkeuringsbevoegdheid Matrix
    # ═══════════════════════════════════
    add_heading(doc, "6  Goedkeuringsbevoegdheid Matrix", 1)
    
    add_para(doc,
        "De goedkeuringsbevoegdheid matrix combineert risiconiveau met wijzigingstype om te bepalen "
        "wie bevoegd is om een MoC goed te keuren. Legenda: R = MoC-coördinator, T = MoC-team, "
        "D = Directie, E = Extern expert.[1][3]")
    
    # Short type names for matrix
    short_types = [
        "Procesparameter",
        "Apparatuur",
        "Proceschemie",
        "Procedure",
        "Organisatorisch",
        "Veiligheidssysteem",
        "Externe factor",
        "Tijdelijke wijziging",
        "Ontwerp/Constructie"
    ]
    
    gm_table = doc.add_table(rows=1, cols=1 + len(short_types))
    add_header_row(gm_table, ["Risiconiveau"] + short_types)
    
    # Approval data: R=coordinator, T=team, D=directie, E=extern
    approval = {
        "Laag":       ["R", "R", "T", "R", "R", "T", "R", "R", "T"],
        "Gemiddeld":  ["T", "T", "T", "R", "T", "T", "T", "T", "D"],
        "Hoog":       ["T", "D", "D", "T", "D", "D", "D", "D", "D"],
        "Kritiek":    ["D", "D", "E", "D", "D", "E", "D", "D", "E"]
    }
    
    for i, (level, vals) in enumerate(approval.items()):
        add_table_row(gm_table, [level] + vals, zebra=(i % 2 == 1))
    
    add_para(doc, "", size=6)
    
    # Legend
    leg2 = doc.add_table(rows=1, cols=4)
    leg2_data = [("R = MoC-coördinator", "T = MoC-team", "D = Directie", "E = Extern expert")]
    for j, val in enumerate(leg2_data[0]):
        cell = leg2.cell(0, j)
        cell.text = val
        for p in cell.paragraphs:
            for run in p.runs:
                run.font.size = Pt(9)
                run.font.name = "Calibri"
                run.bold = True
        set_cell_shading(cell, "E5E7EB")
    
    doc.add_page_break()
    
    # ═══════════════════════════════════
    # HOOFDSTUK 7: Gebruiksaanwijzing XLSX
    # ═══════════════════════════════════
    add_heading(doc, "7  Gebruiksaanwijzing XLSX Register", 1)
    
    add_para(doc,
        "Het bijbehorende Excel-bestand (PM_VBS05_03_Classificatie_Risicomatrix_Register_v1.0.xlsx) "
        "bevat vier werkbladen die als werkinstrument gebruikt worden bij elke MoC-aanvraag.")
    
    add_para(doc, "7.1  Werkbladen", bold=True, size=12, color=PRIMARY)
    
    sheets_desc = [
        ("Beslisboom", "Doorloop de vijf vragen met Ja/Nee antwoorden. Het werkblad toont automatisch het resultaat: Geen MoC, RiF, Standaard MoC, Spoed-MoC of MoC Hoog."),
        ("Risicomatrix 5x5", "Referentiematrix met kleurcodering. Bepaal likelihood (1-5) en impact (1-5) en lees de score af."),
        ("Classificatietabel", "Overzicht van 9 wijzigingstypen met standaard risiconiveau, vereiste veiligheidsstudie en goedkeuring."),
        ("Goedkeuringsmatrix", "Kruistabel van risiconiveau × wijzigingstype met de bevoegde goedkeurder(s).")
    ]
    for name, desc in sheets_desc:
        p = doc.add_paragraph()
        run = p.add_run(f"{name}: ")
        run.bold = True
        run.font.name = "Calibri"
        run.font.size = Pt(11)
        run2 = p.add_run(desc)
        run2.font.name = "Calibri"
        run2.font.size = Pt(11)
    
    add_para(doc, "", size=4)
    add_para(doc, "7.2  Workflow", bold=True, size=12, color=PRIMARY)
    workflow_steps = [
        "Initiator vult MoC-aanvraagformulier in (PM_VBS05_02, Fase 1).",
        "MoC-coördinator doorloopt de Beslisboom (Sheet 1) om MoC-waardigheid te bepalen.",
        "Indien MoC vereist: bepaal wijzigingstype via Classificatietabel (Sheet 3).",
        "Beoordeel likelihood en impact → lees risicoscore af uit Risicomatrix (Sheet 2).",
        "Raadpleeg Goedkeuringsmatrix (Sheet 4) om de bevoegde goedkeurder(s) te bepalen.",
        "Documenteer de classificatie en risicoscore in het MoC-dossier.",
        "Start de vereiste veiligheidsstudie conform de indeling in hoofdstuk 5 van dit document."
    ]
    for i, step in enumerate(workflow_steps, 1):
        add_para(doc, f"Stap {i}: {step}", size=11, space_after=3)
    
    doc.add_page_break()
    
    # ═══════════════════════════════════
    # Bronnenlijst
    # ═══════════════════════════════════
    add_heading(doc, "Bronnenlijst", 1)
    
    sources = [
        "[1] Seveso III Richtlijn 2012/18/EU — Artikel 12: Veiligheidsbeheerssysteem, Element IV: Management van verandering.",
        "[2] BRZO 1999 (Besluit risico's zware ongevallen) — Artikel 5, lid 1 onderdeel d: systeem voor veiligheidsbeheer.",
        "[3] BSI PAS 99:2006 — Specification of common management system requirements as a framework for integration.",
        "[4] NEN-EN-IEC 62340 — Kernenergie — Instrumentatie en regeling — Aspecten van硬件 fouttolerantie (HFT).",
        "[5] NPR 7910-1:2020 — Risicoanalyse voor procesveiligheid — Deel 1: Algemene richtlijnen.",
        "[6] IEC 61882:2016 — Hazard and operability studies (HAZOP studies) — Application guide.",
        "[7] ISO 14971:2019 — Medical devices — Application of risk management to medical devices (risicomatrix referentie).",
        "[8] COMAH Guidance — UK Health and Safety Executive, HSG 260: A guide to the management of change."
    ]
    for s in sources:
        add_para(doc, s, size=10, space_after=4)
    
    # ═══════════════════════════════════
    # TierVerify Log
    # ═══════════════════════════════════
    add_heading(doc, "TierVerify Log", 1)
    
    tv_table = doc.add_table(rows=1, cols=5)
    add_header_row(tv_table, ["Check", "Bron", "Status", "Verificatie", "Opmerking"])
    tv_data = [
        ("Seveso III Art. 12 compliance", "2012/18/EU", "✓ Voldaan", "Bron [1]", "Element IV volledig afgedekt"),
        ("BRZO 1999 Art. 5 compliance", "BRZO 1999", "✓ Voldaan", "Bron [2]", "VBS Element 5 = BRZO Element IV"),
        ("Beslisboom reproduceerbaarheid", "HSG 260 / PAS 99", "✓ Voldaan", "Bron [3][8]", "5 beslispunten, objectief"),
        ("Risicomatrix 5×5 conformiteit", "NPR 7910-1 / ISO 14971", "✓ Voldaan", "Bron [5][7]", "4 gebieden, kleurcodering conform"),
        ("Veiligheidsstudie-indeling", "IEC 61882 / NPR 7910-1", "✓ Voldaan", "Bron [5][6]", "4 niveaus, opgeschaald"),
        ("Goedkeuringsbevoegdheid", "Seveso III / BRZO", "✓ Voldaan", "Bron [1][2]", "Scheiding functies gewaarborgd"),
        ("JvG branding toepassing", "STYLEGUIDE §8", "✓ Voldaan", "Logo + kleuren", "Calibri, #003366, footer aanwezig"),
        ("Documentheader conformiteit", "STYLEGUIDE §2.1", "✓ Voldaan", "Metadata aanwezig", "Code, versie, datum, status"),
    ]
    for i, d in enumerate(tv_data):
        add_table_row(tv_table, list(d), zebra=(i % 2 == 1))
    
    add_para(doc, "", size=8)
    add_para(doc, f"TierVerify afgerond: {DATE_STR} — Alle controles voldaan.", bold=True, size=10, color=PRIMARY)
    
    # ── Footer ──
    # Add footer to all sections
    for section in doc.sections:
        footer = section.footer
        footer.is_linked_to_previous = False
        p = footer.paragraphs[0]
        run = p.add_run("JvG Consultancy | Safety • Governance • Advisory | © 2026")
        run.font.name = "Calibri"
        run.font.size = Pt(8)
        run.font.color.rgb = RGBColor(0x9C, 0xA3, 0xAF)
        p.alignment = WD_ALIGN_PARAGRAPH.CENTER
    
    doc.save(DOCX_OUT)
    print(f"DOCX saved: {DOCX_OUT}")


# ═══════════════════════════════════════════
# XLSX GENERATION
# ═══════════════════════════════════════════
def generate_xlsx():
    wb = openpyxl.Workbook()
    
    BLUE = PatternFill(start_color="003366", end_color="003366", fill_type="solid")
    WHITE_FONT = Font(name="Calibri", size=10, bold=True, color="FFFFFF")
    BODY_FONT = Font(name="Calibri", size=10, color="1F2937")
    BOLD_FONT = Font(name="Calibri", size=10, bold=True, color="1F2937")
    CENTER = Alignment(horizontal="center", vertical="center", wrap_text=True)
    LEFT_WRAP = Alignment(horizontal="left", vertical="center", wrap_text=True)
    THIN_BORDER = Border(
        left=Side(style='thin', color='E5E7EB'),
        right=Side(style='thin', color='E5E7EB'),
        top=Side(style='thin', color='E5E7EB'),
        bottom=Side(style='thin', color='E5E7EB')
    )
    
    def style_header(ws, row, max_col):
        for col in range(1, max_col + 1):
            cell = ws.cell(row=row, column=col)
            cell.fill = BLUE
            cell.font = WHITE_FONT
            cell.alignment = CENTER
            cell.border = THIN_BORDER
    
    def style_body(ws, row, max_col, zebra=False):
        gray = PatternFill(start_color="F3F4F6", end_color="F3F4F6", fill_type="solid")
        for col in range(1, max_col + 1):
            cell = ws.cell(row=row, column=col)
            cell.font = BODY_FONT
            cell.alignment = LEFT_WRAP
            cell.border = THIN_BORDER
            if zebra:
                cell.fill = gray
    
    # ── Sheet 1: Beslisboom ──
    ws1 = wb.active
    ws1.title = "Beslisboom"
    ws1.sheet_properties.tabColor = "003366"
    
    headers1 = ["Stap", "Vraag", "Ja-pad", "Nee-pad", "Resultaat"]
    for col, h in enumerate(headers1, 1):
        ws1.cell(row=1, column=col, value=h)
    style_header(ws1, 1, 5)
    
    steps = [
        (1, "Is het een identieke vervanging (Replacement in Function — RiF)?", "Geen MoC vereist (RiF)", "→ Ga naar stap 2", "Geen MoC / RiF"),
        (2, "Verandert er iets aan proces, installatie, organisatie of veiligheidsmaatregelen?", "→ Ga naar stap 3", "Geen MoC vereist (geen wijziging)", "Geen MoC"),
        (3, "Is het routine-onderhoud conform bestaand goedgekeurd protocol?", "Geen MoC vereist (routinematig)", "→ Ga naar stap 4", "Geen MoC / Routine"),
        (4, "Is er sprake van spoed? (1) Ongeplande uitval (2) Direct gevaar (3) Milieurisico (4) Stilstand >24u met veiligheidsimpact", "Spoed-MoC — verkorte procedure + achteraf-toetsing", "→ Ga naar stap 5", "Spoed-MoC"),
        (5, "Raakt het veiligheidskritische equipment (SCE), -procedures of SEH-elementen?", "MoC HOOG — volledige procedure + verdiepte studie", "Standaard MoC — standaard procedure", "MoC Hoog / Standaard")
    ]
    for i, s in enumerate(steps):
        r = i + 2
        for c, v in enumerate(s, 1):
            ws1.cell(row=r, column=c, value=v)
        style_body(ws1, r, 5, zebra=(i % 2 == 1))
    
    ws1.column_dimensions['A'].width = 8
    ws1.column_dimensions['B'].width = 55
    ws1.column_dimensions['C'].width = 40
    ws1.column_dimensions['D'].width = 35
    ws1.column_dimensions['E'].width = 25
    
    # ── Sheet 2: Risicomatrix 5x5 ──
    ws2 = wb.create_sheet("Risicomatrix 5x5")
    ws2.sheet_properties.tabColor = "DC2626"
    
    # Title
    ws2.cell(row=1, column=1, value="Risicomatrix 5×5 — Likelihood × Impact")
    ws2.cell(row=1, column=1).font = Font(name="Calibri", size=12, bold=True, color="003366")
    ws2.merge_cells("A1:F1")
    
    # Column headers
    ws2.cell(row=3, column=1, value="L ↓ / I →")
    for j in range(1, 6):
        ws2.cell(row=3, column=j+1, value=j)
    style_header(ws2, 3, 6)
    
    def get_xl_fill(score):
        if score <= 4: return PatternFill(start_color="10B981", end_color="10B981", fill_type="solid")
        elif score <= 9: return PatternFill(start_color="F59E0B", end_color="F59E0B", fill_type="solid")
        elif score <= 16: return PatternFill(start_color="DC2626", end_color="DC2626", fill_type="solid")
        else: return PatternFill(start_color="7F1D1D", end_color="7F1D1D", fill_type="solid")
    
    for li in range(1, 6):
        r = li + 3
        ws2.cell(row=r, column=1, value=li)
        ws2.cell(row=r, column=1).fill = BLUE
        ws2.cell(row=r, column=1).font = WHITE_FONT
        ws2.cell(row=r, column=1).alignment = CENTER
        ws2.cell(row=r, column=1).border = THIN_BORDER
        for im in range(1, 6):
            score = li * im
            cell = ws2.cell(row=r, column=im+1, value=score)
            cell.fill = get_xl_fill(score)
            cell.font = Font(name="Calibri", size=12, bold=True, color="FFFFFF" if score > 4 else "1F2937")
            cell.alignment = CENTER
            cell.border = THIN_BORDER
    
    # Legend
    lr = 10
    ws2.cell(row=lr, column=1, value="Legenda:")
    ws2.cell(row=lr, column=1).font = BOLD_FONT
    
    legend = [
        ("Laag (1-4)", "10B981", "1F2937"),
        ("Gemiddeld (5-9)", "F59E0B", "1F2937"),
        ("Hoog (10-16)", "DC2626", "FFFFFF"),
        ("Kritiek (17-25)", "7F1D1D", "FFFFFF")
    ]
    for i, (label, bg, fg) in enumerate(legend):
        cell = ws2.cell(row=lr+1, column=i+1, value=label)
        cell.fill = PatternFill(start_color=bg, end_color=bg, fill_type="solid")
        cell.font = Font(name="Calibri", size=10, bold=True, color=fg)
        cell.alignment = CENTER
        cell.border = THIN_BORDER
    
    # Axis descriptions
    lr2 = lr + 3
    ws2.cell(row=lr2, column=1, value="Likelihood-as (Y):")
    ws2.cell(row=lr2, column=1).font = BOLD_FONT
    lh_desc = ["1=Zeldzaam", "2=Onwaarschijnlijk", "3=Mogelijk", "4=Waarschijnlijk", "5=Verwacht"]
    for i, d in enumerate(lh_desc):
        ws2.cell(row=lr2+1+i, column=1, value=d)
        ws2.cell(row=lr2+1+i, column=1).font = BODY_FONT
    
    ws2.cell(row=lr2, column=3, value="Impact-as (X):")
    ws2.cell(row=lr2, column=3).font = BOLD_FONT
    im_desc = ["1=Verwaarloosbaar", "2=Gering", "3=Matig", "4=Ernstig", "5=Catastrofaal"]
    for i, d in enumerate(im_desc):
        ws2.cell(row=lr2+1+i, column=3, value=d)
        ws2.cell(row=lr2+1+i, column=3).font = BODY_FONT
    
    for c in range(1, 7):
        ws2.column_dimensions[get_column_letter(c)].width = 22
    
    # ── Sheet 3: Classificatietabel ──
    ws3 = wb.create_sheet("Classificatietabel")
    ws3.sheet_properties.tabColor = "F59E0B"
    
    headers3 = ["Wijzigingstype", "Voorbeeld 1", "Voorbeeld 2", "Risiconiveau (standaard)", "Vereiste veiligheidsstudie", "Goedkeuring"]
    for col, h in enumerate(headers3, 1):
        ws3.cell(row=1, column=col, value=h)
    style_header(ws3, 1, 6)
    
    class_data = [
        ("1. Procesparameter-wijziging", "Temperatuurverhoging smeltoven +25°C", "Drukbereik reactor aanpassen", "Gemiddeld", "What-If / FMEA", "MoC-team"),
        ("2. Apparatuur-/componentwijziging", "Pomp vervangen door ander type", "Warmtewisselaar afwijkend materiaal", "Gemiddeld", "What-If / FMEA", "MoC-team"),
        ("3. Proceschemie-/grondstofwijziging", "Legeringssamenstelling bronmateriaal wijzigen", "Alternatieve zuiveringschemicaliër", "Hoog", "HAZOP / FMECA", "MoC-team + Directie"),
        ("4. Operationele procedure-wijziging", "Opstartprocedure aanpassen", "Nieuw shift-overdrachtsformulier", "Laag", "Checklist + TRA", "MoC-coördinator"),
        ("5. Organisatorische wijziging", "3- naar 4-ploegensysteem", "Twee afdelingen fuseren", "Gemiddeld", "What-If / FMEA", "MoC-team"),
        ("6. Veiligheidssysteemwijziging", "Gasdetectie alarmdrempel aanpassen", "SIS trip-punt verleggen", "Hoog", "HAZOP / FMECA", "MoC-team + Directie"),
        ("7. Externe-factorwijziging", "Nieuwe wettelijke emissiegrens", "Bouwactiviteit aangrenzend terrein", "Gemiddeld", "What-If / FMEA", "MoC-team"),
        ("8. Tijdelijke wijziging", "Bypass leiding voor 48u onderhoud", "Mobiele stoomketel bij revisie", "Gemiddeld", "What-If / FMEA", "MoC-team"),
        ("9. Ontwerp-/constructiewijziging", "Nieuwe pijpleidingroute", "Fundamentsspecifikatie opslagtank wijzigen", "Hoog", "HAZOP / FMECA", "MoC-team + Directie")
    ]
    
    risk_fills = {
        "Laag": PatternFill(start_color="10B981", end_color="10B981", fill_type="solid"),
        "Gemiddeld": PatternFill(start_color="F59E0B", end_color="F59E0B", fill_type="solid"),
        "Hoog": PatternFill(start_color="DC2626", end_color="DC2626", fill_type="solid"),
    }
    
    for i, row_data in enumerate(class_data):
        r = i + 2
        for c, v in enumerate(row_data, 1):
            ws3.cell(row=r, column=c, value=v)
        style_body(ws3, r, 6, zebra=(i % 2 == 1))
        # Color the risk level cell
        risk = row_data[3]
        cell = ws3.cell(row=r, column=4)
        cell.fill = risk_fills.get(risk, PatternFill())
        cell.font = Font(name="Calibri", size=10, bold=True, color="FFFFFF" if risk == "Hoog" else "1F2937")
    
    ws3.column_dimensions['A'].width = 32
    ws3.column_dimensions['B'].width = 35
    ws3.column_dimensions['C'].width = 35
    ws3.column_dimensions['D'].width = 22
    ws3.column_dimensions['E'].width = 25
    ws3.column_dimensions['F'].width = 22
    
    # ── Sheet 4: Goedkeuringsmatrix ──
    ws4 = wb.create_sheet("Goedkeuringsmatrix")
    ws4.sheet_properties.tabColor = "7F1D1D"
    
    short_types = [
        "Proces-\nparameter", "Apparatuur", "Proces-\nchemie", "Procedure",
        "Organisa-\ntorisch", "Veiligheids-\nsysteem", "Externe\nfactor",
        "Tijdelijke\nwijziging", "Ontwerp/\nConstructie"
    ]
    
    headers4 = ["Risiconiveau"] + short_types
    for col, h in enumerate(headers4, 1):
        ws4.cell(row=1, column=col, value=h)
    style_header(ws4, 1, 10)
    
    approval = {
        "Laag":       ["R", "R", "T", "R", "R", "T", "R", "R", "T"],
        "Gemiddeld":  ["T", "T", "T", "R", "T", "T", "T", "T", "D"],
        "Hoog":       ["T", "D", "D", "T", "D", "D", "D", "D", "D"],
        "Kritiek":    ["D", "D", "E", "D", "D", "E", "D", "D", "E"]
    }
    
    code_fills = {
        "R": PatternFill(start_color="10B981", end_color="10B981", fill_type="solid"),
        "T": PatternFill(start_color="F59E0B", end_color="F59E0B", fill_type="solid"),
        "D": PatternFill(start_color="DC2626", end_color="DC2626", fill_type="solid"),
        "E": PatternFill(start_color="7F1D1D", end_color="7F1D1D", fill_type="solid"),
    }
    
    for i, (level, vals) in enumerate(approval.items()):
        r = i + 2
        ws4.cell(row=r, column=1, value=level)
        for c, v in enumerate(vals, 2):
            ws4.cell(row=r, column=c, value=v)
        style_body(ws4, r, 10, zebra=(i % 2 == 1))
        # Color code cells
        ws4.cell(row=r, column=1).font = BOLD_FONT
        for c, v in enumerate(vals, 2):
            cell = ws4.cell(row=r, column=c)
            cell.fill = code_fills.get(v, PatternFill())
            cell.font = Font(name="Calibri", size=10, bold=True, color="FFFFFF" if v in ("D", "E") else "1F2937")
            cell.alignment = CENTER
    
    # Legend
    lr = 7
    ws4.cell(row=lr, column=1, value="Legenda:")
    ws4.cell(row=lr, column=1).font = BOLD_FONT
    leg = [("R = MoC-coördinator", "10B981"), ("T = MoC-team", "F59E0B"), ("D = Directie", "DC2626"), ("E = Extern expert", "7F1D1D")]
    for i, (label, bg) in enumerate(leg):
        cell = ws4.cell(row=lr+1, column=i+1, value=label)
        cell.fill = PatternFill(start_color=bg, end_color=bg, fill_type="solid")
        cell.font = Font(name="Calibri", size=10, bold=True, color="FFFFFF" if bg in ("DC2626", "7F1D1D") else "1F2937")
        cell.alignment = CENTER
        cell.border = THIN_BORDER
    
    ws4.column_dimensions['A'].width = 16
    for c in range(2, 11):
        ws4.column_dimensions[get_column_letter(c)].width = 16
    ws4.row_dimensions[1].height = 35
    
    wb.save(XLSX_OUT)
    print(f"XLSX saved: {XLSX_OUT}")


if __name__ == "__main__":
    generate_docx()
    generate_xlsx()
    print("DONE — Both files generated successfully.")
