#!/usr/bin/env python3
"""PGS15 Audit Checklist v2.0 — DOCX Generator"""
from docx import Document
from docx.shared import Pt, Cm, 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

doc = Document()

style = doc.styles['Normal']
style.font.name = 'Calibri'
style.font.size = Pt(9)
style.paragraph_format.space_after = Pt(2)
style.paragraph_format.line_spacing = 1.0

for level in range(1, 4):
    hs = doc.styles[f'Heading {level}']
    hs.font.name = 'Calibri'
    hs.font.color.rgb = RGBColor(0, 51, 102)
    hs.font.size = Pt([14, 11, 10][level-1])
    hs.font.bold = True
    hs.paragraph_format.space_before = Pt([12, 8, 4][level-1])

for section in doc.sections:
    section.top_margin = Cm(1.5)
    section.bottom_margin = Cm(1.5)
    section.left_margin = Cm(1.5)
    section.right_margin = Cm(1.5)

def add_checklist_section(title, items):
    doc.add_heading(title, level=2)
    table = doc.add_table(rows=1+len(items), cols=6)
    table.style = 'Table Grid'
    table.alignment = WD_TABLE_ALIGNMENT.CENTER
    headers = ['Nr.', 'Bron PGS 15:2025', 'Toetsvraag', 'C', 'A', 'Nvt']
    for i, h in enumerate(headers):
        cell = table.rows[0].cells[i]
        cell.text = h
        shading = parse_xml(f'<w:shd {nsdecls("w")} w:fill="003366"/>')
        cell.paragraphs[0].runs[0].font.color.rgb = RGBColor(255, 255, 255)
        cell.paragraphs[0].runs[0].font.bold = True
        cell.paragraphs[0].runs[0].font.size = Pt(8)
        cell._tc.get_or_add_tcPr().append(shading)
    for r_idx, (nr, ref, q) in enumerate(items):
        for c_idx, val in enumerate([nr, ref, q, '', '', '']):
            cell = table.rows[r_idx+1].cells[c_idx]
            cell.text = val
            for p in cell.paragraphs:
                for run in p.runs:
                    run.font.size = Pt(8)
            if r_idx % 2 == 1:
                shading = parse_xml(f'<w:shd {nsdecls("w")} w:fill="F2F2F2"/>')
                cell._tc.get_or_add_tcPr().append(shading)
    # Set column widths
    for row in table.rows:
        row.cells[0].width = Cm(0.8)
        row.cells[1].width = Cm(3.0)
        row.cells[2].width = Cm(10.5)
        row.cells[3].width = Cm(0.8)
        row.cells[4].width = Cm(0.8)
        row.cells[5].width = Cm(0.8)
    doc.add_paragraph()

# ===== TITLE PAGE =====
for _ in range(4):
    doc.add_paragraph()
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run('PGS-15 Audit Checklist')
run.font.size = Pt(26)
run.font.bold = True
run.font.color.rgb = RGBColor(0, 51, 102)
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run('Versie 2.0 — April 2026\nBron: PGS 15:2025 versie 1.0 (januari 2025)')
run.font.size = Pt(12)
doc.add_paragraph()
p = doc.add_paragraph()
p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = p.add_run('C = Conform | A = Afwijking | Nvt = Niet van toepassing\n\nAuditdatum: ____________  Auditor: ____________\nBedrijf: ____________  Locatie: ____________')
run.font.size = Pt(10)
doc.add_page_break()

# ===== SECTIES =====
doc.add_heading('PGS-15 Audit Checklist v2.0', level=1)

add_checklist_section('Sectie 1: Algemene uitgangspunten & werkingssfeer (§1.2)', [
    ('1.1', '§1.2, Tabel 1', 'Is PGS 15 van toepassing op basis van de aanwezige gevaarlijke stoffen?'),
    ('1.2', '§1.2, Tabel 1', 'Vallen de stoffen binnen het toepassingsgebied (klasse 2, 3, 4.1–4.3, 5.1, 5.2 LQ, 6.1, 6.2, 8, 9 M6/M7, CMR/CLP)?'),
    ('1.3', '§1.2, Tabel 1', 'Zijn stoffen die NIET onder PGS 15 vallen (klasse 1, 7, 5.2 overige, lithium, meststoffen) uitgesloten?'),
    ('1.4', '§1.2, Tabel 1', 'Zijn CLP-stoffen acute toxiciteit cat. 1-3 correct uitgesloten?'),
    ('1.5', '§8, M101', 'Is het gelijkwaardigheidsbeginsel toegepast en schriftelijk onderbouwd/goedgekeurd?'),
    ('1.6', 'M4', 'Bevindt de opslag zich NIET in een vluchtroute?'),
    ('1.7', 'M11', 'Is de opslagvoorziening doelmatig geventileerd?'),
    ('1.8', 'MW1', 'Is good housekeeping en goed vakmanschap zichtbaar (zorgplicht basisveiligheid)?'),
])

add_checklist_section('Sectie 2: Indeling, ondergrenzen & vrijstellingen (§1.2.1, Tabel 2)', [
    ('2.1', '§1.2, Tabel 2', 'Zijn de stoffen geclassificeerd volgens ADR (klasse, VG, UN-nummer)?'),
    ('2.2', '§1.2, Tabel 2', 'Zijn CMR/CLP-stoffen zonder ADR-classificatie apart geïdentificeerd?'),
    ('2.3', '§1.2.1, Tabel 2', 'Worden de ondergrensen uit Tabel 2 overschreden?'),
    ('2.4', '§1.2.1, Tabel 2', 'Is de sommatieformule (Σ qx/Qx ≥ 1) correct toegepast bij verschillende stoffen?'),
    ('2.5', '§1.2.1, Tabel 2', 'Zijn stoffen met bijkomend gevaar beoordeeld op basis van de laagste ondergrens?'),
    ('2.6', '§1.2.1, Tabel 2', 'Zijn LQ/EQ-vrijstellingen correct toegepast (dubbele ondergrens, in transportverpakking)?'),
])

add_checklist_section('Sectie 3: Werkvoorraad (M14)', [
    ('3.1', 'M14', 'Is de werkvoorraad beperkt tot stoffen strikt noodzakelijk voor de bedrijfsvoering?'),
    ('3.2', 'M14', 'Is aangetoond dat de werkvoorraad noodzakelijk is?'),
    ('3.3', 'M14', 'Bevindt de werkvoorraad zich NIET in een rijroute of vluchtroute?'),
    ('3.4', 'M14', 'Zijn er geen meerdere ongeopende eenheden die als "verkapte opslag" kwalificeren?'),
])

add_checklist_section('Sectie 4: Opslagvoorzieningen & Veiligheidsniveaus (M2, M3, M21)', [
    ('4.1', 'M2, M3', 'Zijn alle stoffen opgeslagen in een daarvoor bestemde opslagvoorziening?'),
    ('4.2', 'M3, Tabel 6', 'Is het juiste Veiligheidsniveau bepaald (Basis/A/B/C) via Tabel 6?'),
    ('4.3', 'M3, Tabel 6', 'Is de vereiste WBDBO correct bepaald (60 of 90 min)?'),
    ('4.4', 'M21', 'Voldoet de binnenopslag aan WBDBO 60 min (wanden, vloer, dak)?'),
    ('4.5', 'M21', 'Voldoet de buitenopslag aan WBDBO 60 min, óf is de afstand ≥15 m (60 min reductie)?'),
    ('4.6', 'M21', 'Zijn deuren in brandwerende constructies zelfsluitend uitgevoerd?'),
    ('4.7', 'M21', 'Is bij Veiligheidsniveau A >10 ton de WBDBO 90 min?'),
    ('4.8', 'M90', 'Is de afstandsbenadering correct (≥5 m = 30 min, ≥15 m = 60 min)?'),
    ('4.9', 'M90', 'Is 90 min WBDBO NIET via afstand aangetoond (alleen via berekening NEN 6068)?'),
    ('4.10', 'M21', 'Zijn WBDBO-verlagingen voor bestaande bouw NIET toegepast op PGS 15-opslag?'),
    ('4.11', 'M24', 'Heeft de draagconstructie een brandwerendheid gelijk aan de WBDBO van het brandcompartiment?'),
])

add_checklist_section('Sectie 5: Zonnepanelen (M21)', [
    ('5.1', 'M21', 'Is een PV-installatie op de opslagvoorziening alleen aanwezig NA toestemming bevoegd gezag?'),
    ('5.2', 'M21', 'Is bij een niet-brandwerend dak de PV-installatie NIET aanwezig (uitzondering: noodzakelijke installaties)?'),
    ('5.3', 'M21', 'Zijn aanvullende maatregelen voor PV-installatie vastgelegd (brandwerendheid dak, constructie, integriteit)?'),
])

add_checklist_section('Sectie 6: Productopvang (M35, M36)', [
    ('6.1', 'M35', 'Is bij opslag van vloeistoffen een opvangvoorziening aanwezig?'),
    ('6.2', 'M35', 'Bedraagt de opvangcapaciteit ≥110% grootste verpakking of ≥10% totaal (hoogste bepalend)?'),
    ('6.3', 'M35', 'Zijn lege ongereinigde verpakkingen buiten beschouwing gelaten?'),
    ('6.4', 'M59', 'Is bij acuut toxische stoffen (H300-H331, ADR 3 VG I/II) een monsternamepunt op de lekbak aanwezig?'),
])

add_checklist_section('Sectie 7: Compartimentering (Bijlage E, M42–M44)', [
    ('7.1', 'Bijlage E', 'Zijn onverenigbare combinaties correct gescheiden (V/B/—)?'),
    ('7.2', 'Bijlage E', 'Is klasse 3 (brandbaar) en klasse 5.1 (oxiderend) in aparte vakken (V)?'),
    ('7.3', 'M49', 'Zijn acuut toxische stoffen (ADR 6.1 VG I/II) in apart brandcompartiment of met 5 m vrije zone?'),
    ('7.4', 'M49', 'Zijn acuut toxische stoffen uitsluitend in UN-gekeurde verpakkingen, max. 1,80 m hoog?'),
    ('7.5', 'Bijlage E', 'Gelden de compartimenteringseisen NIET voor LQ/EQ-verpakkingen?'),
    ('7.6', 'M44', 'Zijn verpakkingen bij ontvangst en periodiek gecontroleerd op juiste etikettering (M45)?'),
])

add_checklist_section('Sectie 8: Vakbekwaamheid (M104)', [
    ('8.1', 'M104', 'Is bij >2.500 kg een deskundig persoon aanwezig tijdens werkzaamheden?'),
    ('8.2', 'M104', 'Is de vakbekwaamheid aantoonbaar (certificaten, opleidingen)?'),
    ('8.3', 'M104', 'Is het personeel adequaat geïnstrueerd over gevaren van opgeslagen stoffen?'),
    ('8.4', 'M45', 'Is er een procedure voor controle op juiste en leesbare gevaarsetiketten?'),
])

add_checklist_section('Sectie 9: Informatievoorziening hulpdiensten (M116, M117, M118)', [
    ('9.1', 'M116', 'Is bij >2.500 kg een representatieve informatievoorziening aanwezig (bij aankomst beschikbaar)?'),
    ('9.2', 'M116', 'Bevat de plattegrond alle verplichte elementen conform NEN 1414-1?'),
    ('9.3', 'M116', 'Bevat de stoffenlijst ADR-klasse, maximale hoeveelheid per klasse en totaal?'),
    ('9.4', 'M117', 'Is binnen 30 min na aankomst hulpdienst de uitgebreide stofinformatie beschikbaar?'),
    ('9.5', 'M117', 'Bevat de uitgebreide informatie: vervoersnaam, chemische naam, UN-nummer, GEVI, actuele hoeveelheid, VG, CMR/CLP, MSDS?'),
    ('9.6', 'M117', 'Is een deskundig persoon beschikbaar (telefonisch toegestaan bij niet-continubedrijven)?'),
    ('9.7', 'M118', 'Is de wijze van beschikbaar stellen informatie vastgelegd?'),
    ('9.8', 'M116', 'Is de informatievoorziening NIET van toepassing op brandveiligheidsopslagkasten?'),
])

add_checklist_section('Sectie 10: Noodplan (M113)', [
    ('10.1', 'M113', 'Is een intern noodplan aanwezig en actueel?'),
    ('10.2', 'M113', 'Beschrijft het noodplan organisatorische en technische maatregelen?'),
    ('10.3', 'M113', 'Bevat het noodplan een actuele telefoonnummerslijst?'),
    ('10.4', 'M113', 'Is het noodplan ten minste eenmaal per drie jaar geëvalueerd en beproefd?'),
])

add_checklist_section('Sectie 11: Veiligheidssignalering (M135)', [
    ('11.1', 'M135', 'Zijn gevaarsidentificatiesymbolen conform ADR art. 5.2 aan de buitenzijde aangebracht?'),
    ('11.2', 'M48', 'Zijn alle opgeslagen stoffen inclusief kleinverpakkingen correct geëtiketteerd?'),
    ('11.3', 'M135', 'Zijn pictogrammen op geschikte plaatsen aangebracht en goed zichtbaar?'),
])

add_checklist_section('Sectie 12: Brandbeveiliging & VBB (M96, M97, M99)', [
    ('12.1', 'M96', 'Is bij Veiligheidsniveau A een VBB-systeem aanwezig?'),
    ('12.2', 'M99', 'Is een UPD opgesteld en goedgekeurd door bevoegd gezag (Veiligheidsniveau A)?'),
    ('12.3', 'M99', 'Wordt het VBB-systeem jaarlijks geïnspecteerd door geaccrediteerde instelling?'),
    ('12.4', 'M99', 'Is het UPD elke 5 jaar op actualiteit beoordeeld?'),
    ('12.5', 'M97', 'Is een bedrijfsbrandweer ingericht (indien van toepassing op basis van M98)?'),
    ('12.6', 'M103', 'Is de bluswatervoorziening adequaat en bereikbaar?'),
    ('12.7', 'M89', 'Zijn failsafe-functies of behoud van functie geborgd bij relevante installaties?'),
])

add_checklist_section('Sectie 13: Gasflessen (M7, M8, M54–M56, M91, M92)', [
    ('13.1', 'M7', 'Is binnenopslag ≤250 l in brandveiligheidsopslagkast (≥60 min BW)?'),
    ('13.2', 'M8', 'Is binnenopslag >250 l zoveel mogelijk voorkomen en dedicated opgeslagen?'),
    ('13.3', 'M8', 'Is opslag van gasflessen in een kelder VERBODEN?'),
    ('13.4', 'M8', 'Zijn maximaal 2 brandveiligheidsopslagkasten gasflessen per brandcompartiment op verdieping?'),
    ('13.5', 'M54', 'Zijn gasflessen staand opgeslagen en niet gestapeld (tenzij voorziening)?'),
    ('13.6', 'M56', 'Zijn gasflessen tegen omvallen beveiligd?'),
    ('13.7', 'M91', 'Bedraagt de afstand van buitenopgeslagen gasflessen tot gebouwen/brandbaar object minimaal 15 m?'),
    ('13.8', 'M91', 'Voldoet de gevel bij gasflessenopslag aan Wbdbo ≥60 min tot 4 m hoogte?'),
    ('13.9', 'M92', 'Bedraagt de afstand van brandbare gassen zwaarder dan lucht tot kelderopeningen/putten minimaal 5 m?'),
    ('13.10', 'M18', 'Is de totale waterinhoud van een cilinderpakket maximaal 1.000 l (giftig) of 3.000 l (overig)?'),
])

add_checklist_section('Sectie 14: Verdiepingsopslag (M8)', [
    ('14.1', 'M8', 'Bevat de eerste verdieping maximaal 1 opslagruimte met max 2.500 kg/l?'),
    ('14.2', 'M8', 'Bedraagt de opslag in kelder en hogere verdiepingen maximaal 500 kg/l?'),
    ('14.3', 'M8', 'Is de uitzondering voor onbrandbare stoffen (ADR 8 VG II/III, ADR 9) correct toegepast?'),
    ('14.4', 'M8', 'Bedraagt de opslag in kasten op verdieping maximaal 2.500 kg/l per brandcompartiment?'),
    ('14.5', 'M8', 'Zijn gasflessen NIET in de kelder opgeslagen?'),
])

add_checklist_section('Sectie 15: Tijdelijke opslag (§7.6.2, M109–M115)', [
    ('15.1', '§7.6.2', 'Is de juiste categorie tijdelijke opslag bepaald (10.000 kg / 10.000 kg deskundig / 30.000 kg deskundig)?'),
    ('15.2', 'M109', 'Zijn alleen de basisveiligheidsniveau-maatregelen van toepassing?'),
    ('15.3', '§7.6.2', 'Zijn uitgesloten stoffen (VG I, klasse 1, 2.3, 5.2 overige, 7, gasflessen) NIET aanwezig?'),
    ('15.4', '§7.6.2', 'Is het brandcompartiment minimaal 60 min brandwerend?'),
    ('15.5', '§7.6.2', 'Is het brandcompartiment bereikbaar voor hulpdiensten?'),
])

add_checklist_section('Sectie 16: Specifieke stofgroepen', [
    ('16.1', 'M55', 'Is de stapelhoogte spuitbussen maximaal 3,60 m zonder stellingen?'),
    ('16.2', 'M55', 'Is bij Veiligheidsniveau A de stapelhoogte spuitbussen vastgelegd in het UPD?'),
    ('16.3', 'M19', 'Bedraagt de opslag organische peroxiden maximaal 1.000 kg in LQ-verpakkingen?'),
    ('16.4', 'M49', 'Zijn acuut toxische stoffen (H300-H331) in UN-gekeurde verpakkingen, max. 1,80 m hoog?'),
    ('16.5', 'M13', 'Zijn ADR 6.2 cat. I1/I2 stoffen correct opgeslagen?'),
    ('16.6', 'M47', 'Zijn (tank)containers bij ontvangst visueel geïnspecteerd?'),
    ('16.7', 'M52', 'Zijn buiten opgeslagen stoffen in weerbestendige verpakking?'),
])

add_checklist_section('Sectie 17: Onderhoud, keuring & training (M45, M46, M104)', [
    ('17.1', 'M45', 'Is een procedure aanwezig voor controle op etiketten, beschadiging en lekkage?'),
    ('17.2', '§7.4.1', 'Is periodiek onderhoud uitgevoerd en gedocumenteerd?'),
    ('17.3', '§7.4.2', 'Zijn keuringen en inspecties uitgevoerd?'),
    ('17.4', '§7.4.3', 'Is het personeel opgeleid en getraind?'),
    ('17.5', 'M76', 'Is de manoeuvreerruimte tussen opslagvakken voldoende?'),
    ('17.6', 'M75', 'Is aanrijdbeveiliging van stellingen aanwezig?'),
    ('17.7', 'M31', 'Is het terrein correct afgeschot voor bluswater?'),
])

# ===== COLOFON =====
doc.add_paragraph()
p = doc.add_paragraph()
run = p.add_run('Colofon: PGS 15:2025 versie 1.0 (januari 2025), vastgesteld BOb 16 januari 2025. Versie 2.0, april 2026. Dit document vervangt niet de originele PGS 15:2025-publicatie.')
run.font.size = Pt(8)
run.font.italic = True

output_path = '/root/projects/jg/2026-pgs15-audit-toolkit/deliverables/docx/PGS15_Audit_Checklist_v2.0.docx'
doc.save(output_path)
print(f"DOCX saved to: {output_path}")
