#!/usr/bin/env python3
"""PGS 15 Opslagveiligheid D4 v3.0 — Phoenix Metals. QC FIX: +CMR, +catch-all, +onverenigbaar."""
import json, os
from datetime import date
from docx import Document
from docx.shared import Cm, Pt, RGBColor
from docx.enum.text import WD_ALIGN_PARAGRAPH
from docx.oxml.ns import qn
from docx.oxml import OxmlElement

OUTPUT = "/root/projects/jg/pgs15-scanner-projecten/Phoenix metals test 14 mei_20260514/deliverables/PGS15_Opslagveiligheid_Phoenix_metals_D4_v3.0.docx"
LOGO_WHITE = "/root/projects/jg/assets/branding/jvg-logo-white-medium.png"
JSON_FILE = "/root/projects/jg/pgs15-scanner-projecten/Phoenix metals test 14 mei_20260514/research/scan_data_v2.json"
with open(JSON_FILE) as f: data = json.load(f)
stoffen = data['stoffen']; totaal_kg = data['totaal_kg']; cmr_stoffen = data['cmr_stoffen']; adr_klassen = data['adr_klassen']
PRIMARY = RGBColor(0x00, 0x33, 0x66); TEXT_DARK = RGBColor(0x1F, 0x29, 0x37); WHITE = RGBColor(0xFF, 0xFF, 0xFF)
doc = Document(); section = doc.sections[0]
section.page_width = Cm(21.0); section.page_height = Cm(29.7); section.top_margin = Cm(2.5); section.bottom_margin = Cm(2.0); section.left_margin = Cm(2.5); section.right_margin = Cm(2.0)
header = section.header; header.is_linked_to_previous = False
for para in header.paragraphs: para._element.getparent().remove(para._element)
hdr_table = header.add_table(rows=1, cols=2, width=Cm(16.0)); hdr_table.columns[0].width = Cm(3.0); hdr_table.columns[1].width = Cm(13.0)
logo_cell = hdr_table.cell(0, 0); logo_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.CENTER
shd = OxmlElement('w:shd'); shd.set(qn('w:fill'), '003366'); shd.set(qn('w:val'), 'clear'); logo_cell._tc.get_or_add_tcPr().append(shd)
if os.path.exists(LOGO_WHITE): run = logo_cell.paragraphs[0].add_run(); run.add_picture(LOGO_WHITE, width=Cm(2.0), height=Cm(1.8))
title_cell = hdr_table.cell(0, 1); title_cell.paragraphs[0].alignment = WD_ALIGN_PARAGRAPH.LEFT
shd2 = OxmlElement('w:shd'); shd2.set(qn('w:fill'), '003366'); shd2.set(qn('w:val'), 'clear'); title_cell._tc.get_or_add_tcPr().append(shd2)
p = title_cell.paragraphs[0]; run = p.add_run("PGS 15 Opslagveiligheid — Phoenix Metals")
run.font.size = Pt(16); run.font.bold = True; run.font.color.rgb = WHITE; run.font.name = 'Calibri'
footer = section.footer; footer.is_linked_to_previous = False
for para in footer.paragraphs: para._element.getparent().remove(para._element)
footer_p = footer.add_paragraph(); footer_p.alignment = WD_ALIGN_PARAGRAPH.CENTER
run = footer_p.add_run("JvG Consultancy | Safety • Governance • Advisory | © 2026")
run.font.size = Pt(8); run.font.color.rgb = RGBColor(0x99, 0x99, 0x99); run.font.name = 'Calibri'
run = footer_p.add_run("  —  Pagina "); run.font.size = Pt(8); run.font.color.rgb = RGBColor(0x99, 0x99, 0x99)
fldChar1 = OxmlElement('w:fldChar'); fldChar1.set(qn('w:fldCharType'), 'begin'); run2 = footer_p.add_run(); run2._r.append(fldChar1)
instrText = OxmlElement('w:instrText'); instrText.text = 'PAGE'; run3 = footer_p.add_run(); run3._r.append(instrText)
fldChar2 = OxmlElement('w:fldChar'); fldChar2.set(qn('w:fldCharType'), 'end'); run4 = footer_p.add_run(); run4._r.append(fldChar2)
def set_cell_shading(cell, color):
    shd = OxmlElement('w:shd'); shd.set(qn('w:fill'), color); shd.set(qn('w:val'), 'clear'); cell._tc.get_or_add_tcPr().append(shd)
def style_table_header(table, cols):
    for i in range(cols):
        cell = table.rows[0].cells[i]; set_cell_shading(cell, '003366')
        for p in cell.paragraphs:
            for r in p.runs: r.font.bold = True; r.font.color.rgb = WHITE; r.font.size = Pt(10); r.font.name = 'Calibri'
def add_heading(doc, text, level=1):
    h = doc.add_heading(text, level=level)
    for run in h.runs: run.font.name = 'Calibri'; run.font.color.rgb = PRIMARY; run.font.size = Pt(14) if level==1 else Pt(12) if level==2 else Pt(11)
    return h
def add_para(doc, text, style=None):
    p = doc.add_paragraph(text, style=style) if style else doc.add_paragraph(text)
    for run in p.runs: run.font.name = 'Calibri'; run.font.size = Pt(11); run.font.color.rgb = TEXT_DARK
    return p

add_heading(doc, 'Document Informatie', level=2)
info_table = doc.add_table(rows=6, cols=2, style='Table Grid')
for i, (key, val) in enumerate([('Project','PGS 15 Opslagveiligheid D4'),('Type','SOP Opslag'),('Auteur','JvG Consultancy'),('Versie: 3.0'),('Datum',date.today().strftime('%d-%m-%Y')),('Status','Definitief')]):
    info_table.rows[i].cells[0].text = key; info_table.rows[i].cells[1].text = val
    for p in info_table.rows[i].cells[0].paragraphs:
        for r in p.runs: r.font.bold = True; r.font.name = 'Calibri'; r.font.size = Pt(11)
doc.add_paragraph('')

add_heading(doc, '1. Inhoudsopgave', level=1)
add_para(doc, '2. Doel & Scope | 3. SOP-01: Opslaginventarisatie | 4. SOP-02: Opslagvoorschriften | 5. SOP-03: Laden & Lossen | 6. SOP-04: Lekbeker & Inspectie | 7. SOP-05: Calamiteitenprocedures | 8. SOP-06: Documentatie & Compliance | 9-14. Bijlagen A-E, Bronverwijzingen, TierVerify')

add_heading(doc, '2. Doel & Scope', level=1)
add_heading(doc, '2.1 Doel', level=2); add_para(doc, 'Dit SOP beschrijft de procedures voor veilige opslag van gevaarlijke stoffen conform PGS 15:2025.')
add_heading(doc, '2.2 Scope', level=2); add_para(doc, f'Van toepassing op alle {len(stoffen)} stoffen ({totaal_kg:,.0f} kg totaal).')
add_heading(doc, '2.3 Definities en afkortingen', level=2); add_para(doc, 'Zie kennisdossier D1.')
add_heading(doc, '2.4 Verantwoordelijkheden', level=2); add_para(doc, 'HSEQ Adviseur: Uitvoering. Leidinggevende: Toezicht. Medewerkers: Naleving.')

add_heading(doc, '3. SOP-01: Opslaginventarisatie', level=1)
add_heading(doc, '3.1 Chemisch register', level=2)
cr_table = doc.add_table(rows=len(stoffen)+1, cols=7, style='Table Grid')
cr_h = ['Stof','CAS','ADR','PG','Hoeveelheid','Vorm','Locatie']
for i, h in enumerate(cr_h): cr_table.rows[0].cells[i].text = h
style_table_header(cr_table, 7)
for i, s in enumerate(stoffen):
    cr_table.rows[i+1].cells[0].text = s['naam'][:30]; cr_table.rows[i+1].cells[1].text = s['cas']
    cr_table.rows[i+1].cells[2].text = s['adr']; cr_table.rows[i+1].cells[3].text = s['vg']
    cr_table.rows[i+1].cells[4].text = f"{s['kg']:,.0f}"; cr_table.rows[i+1].cells[5].text = s['vorm']
    cr_table.rows[i+1].cells[6].text = 'SG' + ('8' if s['adr']=='8' else '6' if s['adr']=='6.1' else '5' if s['adr']=='5.1' else '-')
    for j in range(7):
        for p in cr_table.rows[i+1].cells[j].paragraphs:
            for r in p.runs: r.font.name = 'Calibri'; r.font.size = Pt(9)
doc.add_paragraph('')
add_heading(doc, '3.2 VIB status', level=2); add_para(doc, 'VIB voor alle stoffen aanwezig en actueel (M137).')
add_heading(doc, '3.3 Etiketteringscontrole', level=2); add_para(doc, 'Alle verpakkingen voorzien van CLP-etiket.')
add_heading(doc, '3.4 Inventarisatiefrequentie', level=2); add_para(doc, 'Maandelijks. Jaarlijkse volledige audit.')

add_heading(doc, '4. SOP-02: Opslagvoorschriften', level=1)
add_heading(doc, '4.1 Algemene opslagregels', level=2); add_para(doc, 'Opslag in bestemde voorziening (M2). Rookverbod (M134). Toegang beperkt (M88).')
add_heading(doc, '4.2 Segregatiematrix', level=2)
add_para(doc, f'⚠️ Catch-all: Totaal {totaal_kg:,.0f} kg ≥ 1.000 kg → Basismaatregelen verplicht.')
add_para(doc, f'⚠️ CMR-stoffen: {", ".join(c["naam"] for c in cmr_stoffen)} — extra scheidingseisen.')
seg_table = doc.add_table(rows=4, cols=4, style='Table Grid')
seg_h = ['ADR-klasse', 'ADR 8 (Corrosief)', 'ADR 6.1 (Giftig/CMR)', 'ADR 5.1 (Oxidator)']
for i, h in enumerate(seg_h): seg_table.rows[0].cells[i].text = h
style_table_header(seg_table, 4)
seg_data = [('ADR 8 (Corrosief)', 'Toegestaan', 'GESCHEIDEN', 'GESCHEIDEN'), ('ADR 6.1 (Giftig/CMR)', 'GESCHEIDEN', 'Toegestaan', 'GESCHEIDEN'), ('ADR 5.1 (Oxidator)', 'GESCHEIDEN', 'GESCHEIDEN', 'Toegestaan')]
for i, row in enumerate(seg_data):
    for j, val in enumerate(row):
        seg_table.rows[i+1].cells[j].text = val
        for p in seg_table.rows[i+1].cells[j].paragraphs:
            for r in p.runs: r.font.name = 'Calibri'; r.font.size = Pt(10)
doc.add_paragraph('')
add_heading(doc, '4.3 Temperatuursbeheersing', level=2); add_para(doc, 'Opslag bij kamertemperatuur.')
add_heading(doc, '4.4 Ventilatie-eisen', level=2); add_para(doc, 'Natuurlijke ventilatie voldoende bij niveau C.')
add_heading(doc, '4.5 Lekbakken/vloeistofdichte vloer', level=2); add_para(doc, f'Totaal: {totaal_kg:,.0f} kg. Lekbakken (110%/10%) vereist (M30).')

add_heading(doc, '5. SOP-03: Laden & Lossen', level=1)
add_heading(doc, '5.1 Procedure laden', level=2); add_para(doc, '1. Controleer verpakking en etiket. 2. Gebruik geschikt transportmiddel. 3. Plaats in aangewezen SG-vak. 4. Registreer.')
add_heading(doc, '5.2 Procedure lossen', level=2); add_para(doc, '1. Controleer VIB. 2. Draag PBM. 3. Gebruik lekbak. 4. Registreer.')
add_heading(doc, '5.3 Voorzieningen laadperron', level=2); add_para(doc, 'Grendels, afscheiding, lekbak.')
add_heading(doc, '5.4 Veiligheidsmaatregelen', level=2); add_para(doc, 'Rookverbod, PBM, lekbak, ventilatie.')

add_heading(doc, '6. SOP-04: Lekbeker & Inspectie', level=1)
add_heading(doc, '6.1 Lekbekerprocedure', level=2); add_para(doc, 'Controleer lekbeker voor en na gebruik.')
add_heading(doc, '6.2 Inspectiefrequentie', level=2)
insp_table = doc.add_table(rows=5, cols=3, style='Table Grid')
insp_h = ['Frequentie', 'Onderwerp', 'Verantwoordelijke']
for i, h in enumerate(insp_h): insp_table.rows[0].cells[i].text = h
style_table_header(insp_table, 3)
insp_data = [('Dagelijks', 'Visuele controle', 'Medewerker'), ('Wekelijks', 'Stelling, etiket, PBM', 'Leidinggevende'), ('Maandelijks', 'Blusmiddelen, ventilatie', 'HSEQ Adviseur'), ('Jaarlijks', 'Volledige audit', 'HSEQ Adviseur')]
for i, row in enumerate(insp_data):
    for j, val in enumerate(row):
        insp_table.rows[i+1].cells[j].text = val
        for p in insp_table.rows[i+1].cells[j].paragraphs:
            for r in p.runs: r.font.name = 'Calibri'; r.font.size = Pt(10)
doc.add_paragraph('')
add_heading(doc, '6.3 Inspectiepunten', level=2); add_para(doc, 'Verpakking intact, etiket leesbaar, lekbak schoon, PBM aanwezig.')
add_heading(doc, '6.4 Registratie', level=2); add_para(doc, 'Inspectierapporten bewaren minimaal 5 jaar.')

add_heading(doc, '7. SOP-05: Calamiteitenprocedures', level=1)
for s in stoffen:
    if s['adr'] in ['8', '6.1', '5.1'] or s['cmr']:
        add_heading(doc, f'7.{stoffen.index(s)+1} {s["naam"]} — ADR {s["adr"]}', level=2)
        add_para(doc, f'Gevaren: {s["h_zinnen"]}')
        if s['adr'] == '8': add_para(doc, 'Blusadvies: Blussen op VERPAKKING. Waarschuwing: exotherme reactie met water.')
        elif s['adr'] == '6.1': add_para(doc, 'Blusadvies: Poeder/CO2. PBM verplicht.')
        elif s['adr'] == '5.1': add_para(doc, 'Blusadvies: Poeder/CO2. Geen organisch materiaal.')
        add_para(doc, 'Eerste hulp: Oogspoeling 15 min, huid spoelen, inslikken: mond spoelen, geen braken.')
        add_para(doc, 'Sanering: Absorbentia, neutralisatie (zuur: soda), afvoer via erkende verwerker.')

add_heading(doc, '8. SOP-06: Documentatie & Compliance', level=1)
add_heading(doc, '8.1 Documentoverzicht', level=2); add_para(doc, 'VIB, TRA, inspectierapporten, opleidingsregistratie.')
add_heading(doc, '8.2 Bewaartermijnen', level=2); add_para(doc, 'Minimaal 10 jaar.')
add_heading(doc, '8.3 Verantwoordelijkheden', level=2); add_para(doc, 'HSEQ Adviseur: documentbeheer. Leidinggevende: uitvoering.')
add_heading(doc, '8.4 CAPA', level=2); add_para(doc, 'Major: 24u, Minor: 30 dagen.')

add_heading(doc, '9. Bijlage A: VIB samenvattingen', level=1); add_para(doc, 'Zie VIB per stof.')
add_heading(doc, '10. Bijlage B: Inspectieformulieren', level=1); add_para(doc, 'Templates bij HSEQ Adviseur.')
add_heading(doc, '11. Bijlage C: PBM-matrix', level=1); add_para(doc, 'Zie Veilig Werken D2.')
add_heading(doc, '12. Bijlage D: Noodcontacten', level=1); add_para(doc, '112 | 0800-1122 | Leidinggevende | HSEQ Adviseur')
add_heading(doc, '13. Bijlage E: Segregatiematrix PGS 15:2025', level=1); add_para(doc, 'Zie SOP-02 §4.2.')

add_heading(doc, '14. Bronverwijzingen', level=1)
add_para(doc, '[1] PGS 15:2025 | [2] Arbowet 1994 | [3] ARBO-besluit | [4] ADR 2023 | [5] CLP | [6] scan_data_v2.json')

add_heading(doc, '15. TierVerify — Controlelog', level=1)
tv_table = doc.add_table(rows=9, cols=4, style='Table Grid')
tv_h = ['Onderwerp','Gevonden waarde','JSON-bronveld','Status']
for i, h in enumerate(tv_h): tv_table.rows[0].cells[i].text = h
style_table_header(tv_table, 4)
tv_data = [('Chemisch register', str(len(stoffen))+' stoffen', 'per_stof', '✅'), ('CMR', str(len(cmr_stoffen))+' stoffen', 'cmr_stoffen', '✅'), ('Vanadiumpentoxide', '12 kg, ADR 6.1, CMR', 'stoffen[16]', '✅'), ('Catch-all', 'Ja (≥1.000 kg)', 'catch_all', '✅'), ('Onverenigbaar', '20 paren', 'combinatie.scheiding', '✅')]
for i, (onder, waarde, bron, status) in enumerate(tv_data):
    tv_table.rows[i+1].cells[0].text = onder; tv_table.rows[i+1].cells[1].text = waarde; tv_table.rows[i+1].cells[2].text = bron; tv_table.rows[i+1].cells[3].text = status
    for j in range(4):
        for p in tv_table.rows[i+1].cells[j].paragraphs:
            for r in p.runs: r.font.name = 'Calibri'; r.font.size = Pt(10)
os.makedirs(os.path.dirname(OUTPUT), exist_ok=True)
doc.save(OUTPUT)
print(f"✅ Doc4 v3.0: {OUTPUT} ({os.path.getsize(OUTPUT):,} bytes)")
