#!/usr/bin/env python3
"""PGS-15 Management Presentatie — Phoenix Metals IJmuiden"""
from pptx import Presentation
from pptx.util import Inches, Pt
from pptx.dml.color import RGBColor
from pptx.enum.text import PP_ALIGN, MSO_ANCHOR
from pptx.enum.shapes import MSO_SHAPE
import os

PRIMARY = RGBColor(0x00, 0x33, 0x66)
ACCENT  = RGBColor(0x3B, 0x82, 0xF6)
WHITE   = RGBColor(0xFF, 0xFF, 0xFF)
DARK    = RGBColor(0x33, 0x33, 0x33)
RED     = RGBColor(0xDC, 0x26, 0x26)
GREEN   = RGBColor(0x16, 0xA3, 0x5A)
ORANGE  = RGBColor(0xF5, 0x9E, 0x0B)
LIGHT_BG = RGBColor(0xF0, 0xF4, 0xF8)

LOGO = "/root/projects/jg/2026-pgs15-phoenix-metals/assets/branding/jvg-consultancy-logo.jpg"
DATE_STR = "26 april 2026"
SW = Inches(13.333)
SH = Inches(7.5)

prs = Presentation()
prs.slide_width = SW
prs.slide_height = SH

BL = prs.slide_layouts[6]

def logo(s, large=False):
    if not os.path.exists(LOGO): return
    w = Inches(2.2) if large else Inches(1.1)
    h = Inches(1.1) if large else Inches(0.55)
    s.shapes.add_picture(LOGO, SW - w - Inches(0.3), Inches(0.2), w, h)

def footer(s, n):
    tb = s.shapes.add_textbox(Inches(0.5), SH - Inches(0.45), Inches(8), Inches(0.35))
    p = tb.text_frame.paragraphs[0]
    p.text = f"{n}  |  JvG Consultancy | PGS-15  |  {DATE_STR}"
    p.font.size = Pt(9); p.font.color.rgb = RGBColor(0x99,0x99,0x99)

def titlebar(s, txt):
    bar = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, 0, SW, Inches(1.1))
    bar.fill.solid(); bar.fill.fore_color.rgb = PRIMARY; bar.line.fill.background()
    tf = bar.text_frame; tf.word_wrap = True; tf.margin_left = Inches(0.5)
    tf.vertical_anchor = MSO_ANCHOR.MIDDLE
    p = tf.paragraphs[0]; p.text = txt; p.font.size = Pt(28); p.font.color.rgb = WHITE; p.font.bold = True

def txtbox(s, l, t, w, h, text, sz=14, bold=False, color=DARK, align=PP_ALIGN.LEFT):
    tb = s.shapes.add_textbox(l, t, w, h)
    tf = tb.text_frame; tf.word_wrap = True
    p = tf.paragraphs[0]; p.text = text; p.font.size = Pt(sz); p.font.bold = bold; p.font.color.rgb = color; p.alignment = align
    return tf

def bullet(tf, text, sz=13, color=DARK, bold=False):
    p = tf.add_paragraph(); p.text = text; p.font.size = Pt(sz); p.font.color.rgb = color; p.font.bold = bold; p.space_before = Pt(3)

def scell(cell, text, sz=11, bold=False, color=DARK, fill=None, align=PP_ALIGN.LEFT):
    cell.text = ""
    p = cell.text_frame.paragraphs[0]; p.text = text; p.font.size = Pt(sz); p.font.bold = bold; p.font.color.rgb = color; p.alignment = align
    cell.vertical_anchor = MSO_ANCHOR.MIDDLE
    if fill: cell.fill.solid(); cell.fill.fore_color.rgb = fill

def hrow(tbl, texts, fill=PRIMARY):
    for i, t in enumerate(texts):
        scell(tbl.cell(0, i), t, sz=11, bold=True, color=WHITE, fill=fill, align=PP_ALIGN.CENTER)

def mktable(s, rows, cols, l, t, w, h):
    return s.shapes.add_table(rows, cols, l, t, w, h).table

# Slide 1 - Title
s = prs.slides.add_slide(BL)
bg = s.background.fill; bg.solid(); bg.fore_color.rgb = PRIMARY
bar = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, SH - Inches(1.8), SW, Inches(0.06))
bar.fill.solid(); bar.fill.fore_color.rgb = ACCENT; bar.line.fill.background()
logo(s, True)
txtbox(s, Inches(0.8), Inches(1.5), Inches(10), Inches(1), "PGS-15 Compliance Analyse", 40, True, WHITE)
txtbox(s, Inches(0.8), Inches(2.6), Inches(10), Inches(0.6), "Phoenix Metals IJmuiden — Pilotfaciliteit", 24, False, RGBColor(0xBB,0xCC,0xDD))
txtbox(s, Inches(0.8), Inches(3.5), Inches(10), Inches(0.5), "Management Briefing", 20, True, ACCENT)
txtbox(s, Inches(0.8), Inches(4.5), Inches(10), Inches(0.4), DATE_STR, 16, False, WHITE)
txtbox(s, Inches(0.8), Inches(5.2), Inches(10), Inches(0.4), "JvG Consultancy", 16, True, WHITE)
footer(s, 1)

# Slide 2 - Agenda
s = prs.slides.add_slide(BL)
titlebar(s, "Agenda"); logo(s); footer(s, 2)
tf = txtbox(s, Inches(0.8), Inches(1.4), Inches(7), Inches(5.5), "", 16)
for item in ["1. Project Context & Opdracht","2. Bedrijfsprofiel Phoenix Metals","3. Wettelijk Kader","4. Veiligheidsniveaus & Drempelwaarden","5. Compatibiliteitsmatrix","6. Kritieke Bevinding","7. Voorgestelde Oplossing","8. Maatregelenoverzicht","9. Brandpreventie","10. Schaalbaarheid & Groeiscenario's","11. Actiepunten","12. Vragen & Contact"]:
    bullet(tf, item, 16)

# Slide 3 - Project Context
s = prs.slides.add_slide(BL)
titlebar(s, "Project Context"); logo(s); footer(s, 3)
tf = txtbox(s, Inches(0.8), Inches(1.4), Inches(11), Inches(5.5), "Opdracht", 18, True, PRIMARY)
for t in ["Opdrachtgever: Phoenix Metals IJmuiden","Doel: PGS 15:2025 compliance-analyse opslag gevaarlijke stoffen","Scope: Volledig chemisch register (6 stoffen, ~390 kg)","Referentie: PGS 15:2025 v1.0 (238 pp)"]:
    bullet(tf, t, 14)
bullet(tf, "", 6)
p = tf.add_paragraph(); p.text = "Aanleiding"; p.font.bold=True; p.font.size=Pt(18); p.font.color.rgb=PRIMARY
for t in ["Nieuwe PGS 15:2025 norm — strengere compatibiliteitseisen","Verplichte stoffenscheiding conform Bijlage E, Tabel 9","Verplichte vloeistofopvang (lekbakken) voor ADR 8 stoffen"]:
    bullet(tf, t, 14)
bullet(tf, "", 6)
p = tf.add_paragraph(); p.text = "Belang"; p.font.bold=True; p.font.size=Pt(18); p.font.color.rgb=PRIMARY
bullet(tf, "Aansprakelijkheid directie bij niet-compliance", 14)
bullet(tf, "Risico op exotherme reactie bij huidige opslagconfiguratie", 14)

# Slide 4 - Bedrijfsprofiel
s = prs.slides.add_slide(BL)
titlebar(s, "Bedrijfsprofiel Phoenix Metals"); logo(s); footer(s, 4)
txtbox(s, Inches(0.8), Inches(1.3), Inches(11), Inches(0.5), "Locatie: IJmuiden  |  Pilotfaciliteit  |  Opslag: rack vóór lab", 13)
t = mktable(s, 7, 7, Inches(0.5), Inches(2.0), Inches(12), Inches(3.8))
hrow(t, ["#", "Stof", "CAS", "ADR", "PG", "Volume", "Verpakking"])
data = [
    ["1","Sodium hydroxide 50%","1310-73-2","8","II","100 l (4×25)","Jerrycan"],
    ["2","Sulfuric acid 75%","7664-93-9","8","II","30 l (3×10)","Jerrycan"],
    ["3","Sulfuric acid 50%","7664-93-9","8","II","70 l (7×10)","Jerrycan"],
    ["4","Sulfuric acid 25%","7664-93-9","8","II","100 l (4×25)","Jerrycan"],
    ["5","Aluminum sulfate","7784-31-8","N/A","—","5 kg","Canister"],
    ["6","Calcium oxide","1305-78-8","N/A","—","10 kg","Canister"],
]
for r, row in enumerate(data):
    for c, v in enumerate(row):
        scell(t.cell(r+1,c), v, 10, fill=LIGHT_BG if r%2==0 else WHITE, align=PP_ALIGN.CENTER)
txtbox(s, Inches(0.5), Inches(6.1), Inches(10), Inches(0.4), "Totaal ADR 8: 300 l | Niet-ADR: 15 kg | SVHC: Geen | CMR: Geen", 11, True, PRIMARY)

# Slide 5 - Wettelijk Kader
s = prs.slides.add_slide(BL)
titlebar(s, "Wettelijk Kader"); logo(s); footer(s, 5)
t = mktable(s, 5, 2, Inches(0.8), Inches(1.5), Inches(11), Inches(4.5))
hrow(t, ["Wetgeving", "Relevantie"])
laws = [["PGS 15:2025","Primaire richtlijn opslag verpakte gevaarlijke stoffen"],["Omgevingswet (Bbl/Bkl)","Brandcompartimentering, externe veiligheidsafstanden"],["Arbowet (Arbobesluit art. 4.1c)","RI&E verplicht, veilige werkomstandigheden"],["ADR","Transportclassificatie → opslagklasse-indeling"]]
for r,(a,b) in enumerate(laws):
    scell(t.cell(r+1,0), a, 12, True, PRIMARY, LIGHT_BG if r%2==0 else WHITE)
    scell(t.cell(r+1,1), b, 11, fill=LIGHT_BG if r%2==0 else WHITE)
txtbox(s, Inches(0.8), Inches(6.2), Inches(11), Inches(0.5), "PGS 15 is een invulling van de zorgplicht uit de Omgevingswet.", 11)

# Slide 6 - Veiligheidsniveaus
s = prs.slides.add_slide(BL)
titlebar(s, "Veiligheidsniveaus & Drempelwaarden"); logo(s); footer(s, 6)
txtbox(s, Inches(0.8), Inches(1.3), Inches(11), Inches(0.5), "ADR 8 VG II onbrandbaar — Phoenix Metals: ~390 kg", 14, True, PRIMARY)
t = mktable(s, 5, 4, Inches(0.8), Inches(2.0), Inches(11), Inches(2.8))
hrow(t, ["Niveau","Volume","Vereisten","Status PM"])
for r, row in enumerate([["Basis","< 10.000 kg","Lekbakken, ventilatie, scheiding","✅ HUIDIG"],["VN-B","10.000–20.000 kg","Blusinstallatie + detectie + 60 min","N.v.t."],["VN-A","> 20.000 kg","VBB + PAC-doormelding","N.v.t."],["VN-C","Verspreiding","Specifieke controle","N.v.t."]]):
    for c, v in enumerate(row):
        fc = GREEN if r==0 else DARK
        fb = RGBColor(0xEC,0xFD,0xF5) if r==0 else (LIGHT_BG if r%2==1 else WHITE)
        scell(t.cell(r+1,c), v, 11, r==0, fc, fb)
txtbox(s, Inches(0.8), Inches(5.2), Inches(11), Inches(1), "⚠️ Stoffenscheiding (Tabel 9, M35) geldt ONGEACHT veiligheidsniveau!\n390 kg = 15,6% van ondergrens (2.500 kg).", 13, True, RED)

# Slide 7 - Compatibiliteitsmatrix
s = prs.slides.add_slide(BL)
titlebar(s, "Compatibiliteitsmatrix (Tabel 9)"); logo(s); footer(s, 7)
txtbox(s, Inches(0.8), Inches(1.3), Inches(11), Inches(0.5), "ADR 8 ↔ ADR 8 = A (apart brandcompartiment)", 13, True, RED)
t = mktable(s, 5, 4, Inches(1.5), Inches(2.0), Inches(10), Inches(3))
hrow(t, ["Combinatie","Scheiding","Niveau","Toelichting"])
combos = [["NaOH 50% ↔ H₂SO₄","✅ JA","A (kritiek)","Zuur+Base = exotherm"],["NaOH 50% ↔ CaO","✅ JA","B","Beoordeling nodig"],["H₂SO₄ ↔ CaO","✅ JA","A (kritiek)","Zuur+Base, exotherm"],["H₂SO₄ ↔ H₂SO₄ (div.)","❌ NEE","—","Zelfde stof"]]
for r, row in enumerate(combos):
    fb = RGBColor(0xFE,0xF2,0xF2) if "kritiek" in row[2] else (LIGHT_BG if r%2==1 else WHITE)
    for c, v in enumerate(row):
        scell(t.cell(r+1,c), v, 11, c==2 and "kritiek" in v, RED if c==2 and "kritiek" in v else DARK, fb)
txtbox(s, Inches(0.8), Inches(5.3), Inches(11), Inches(0.5), "A = Apart brandcompartiment | B = Beoordelen | — = Geen scheiding nodig", 11, color=RGBColor(0x66,0x66,0x66))

# Slide 8 - Kritieke Bevinding
s = prs.slides.add_slide(BL)
bg = s.background.fill; bg.solid(); bg.fore_color.rgb = RGBColor(0xFE,0xF2,0xF2)
titlebar(s, "⚠️ Kritieke Bevinding"); logo(s); footer(s, 8)
box = s.shapes.add_shape(MSO_SHAPE.ROUNDED_RECTANGLE, Inches(1), Inches(1.5), Inches(11), Inches(1.8))
box.fill.solid(); box.fill.fore_color.rgb = RED; box.line.fill.background()
tf = box.text_frame; tf.word_wrap = True; tf.margin_left = Inches(0.3); tf.vertical_anchor = MSO_ANCHOR.MIDDLE
p = tf.paragraphs[0]; p.text = "HUIDIGE SITUATIE NIET-COMPLIANT"; p.font.size = Pt(24); p.font.bold = True; p.font.color.rgb = WHITE; p.alignment = PP_ALIGN.CENTER
p2 = tf.add_paragraph(); p2.text = "NaOH (base) en H₂SO₄ (zuur) op hetzelfde rack — risico exotherme reactie"; p2.font.size = Pt(14); p2.font.color.rgb = WHITE; p2.alignment = PP_ALIGN.CENTER
tf2 = txtbox(s, Inches(1), Inches(3.8), Inches(11), Inches(3), "Risico's:", 16, True, RED)
for t in ["Exotherme reactie bij menging → hitte + spatten","Giftige dampen als reactieproduct","Directiepersoonlijk aansprakelijk bij incident"]:
    bullet(tf2, t, 14)
bullet(tf2, "", 6)
bullet(tf2, "Bron: PGS 15 Bijlage E, Tabel 9 — ADR 8 ↔ ADR 8 = A-scheiding", 13, PRIMARY, True)

# Slide 9 - Oplossing
s = prs.slides.add_slide(BL)
titlebar(s, "Voorgestelde Oplossing"); logo(s); footer(s, 9)
tf = txtbox(s, Inches(0.8), Inches(1.4), Inches(11), Inches(5), "Zone-indeling (E.3, < 10.000 kg)", 18, True, PRIMARY)
for t in ["Zone A (Basisch): NaOH 50% → aparte lekbak, gemarkeerd vak","Zone B (Zuur): H₂SO₄ alle concentraties → aparte lekbak, gemarkeerd vak","Minimaal 2 m afstand tussen beide zones","Calcium oxide apart van H₂SO₄ (A-scheiding)","Lekbakken ≥ 110% grootste verpakking per stofgroep","Vloermarkering + veiligheidssymbolen per zone"]:
    bullet(tf, t, 14)
bullet(tf, "", 8)
bullet(tf, "Investeringsraming: €5.000 – €15.000", 20, GREEN, True)

# Slide 10 - Maatregelen
s = prs.slides.add_slide(BL)
titlebar(s, "Maatregelenoverzicht"); logo(s); footer(s, 10)
measures = [["MW1","Zorgplicht basisveiligheid","⚠️ Toetsen"],["M2","Opslag in bestemde voorziening","⚠️ Toetsen"],["M3","Verpakkingscontrole","⚠️ Toetsen"],["M14","Werkvoorraad regeling","⚠️ Toetsen"],["M15","Max opp. brandcompartiment","✅ OK"],["M30","Vloeistofopvang + ventilatie","⚠️ Toetsen"],["M35","Stoffenscheiding Tabel 9","❌ Niet compliant"],["M36","Verpakkingsintegriteit","⚠️ Toetsen"],["M45","Stalendeelsysteem","⚠️ Toetsen"],["M77","Fysieke bescherming","⚠️ Toetsen"],["M85","SDS + deskundigheid","⚠️ Toetsen"],["M109","Toegankelijkheid hulpdiensten","⚠️ Toetsen"]]
t = mktable(s, 13, 3, Inches(1), Inches(1.3), Inches(11), Inches(5.5))
hrow(t, ["M-nummer","Maatregel","Status"])
for r, row in enumerate(measures):
    fb = RGBColor(0xFE,0xF2,0xF2) if "❌" in row[2] else (RGBColor(0xEC,0xFD,0xF5) if "✅" in row[2] else (LIGHT_BG if r%2==1 else WHITE))
    for c, v in enumerate(row):
        fc = RED if "❌" in v else (GREEN if "✅" in v else (ORANGE if "⚠️" in v else DARK))
        scell(t.cell(r+1,c), v, 10, c==2, fc, fb, PP_ALIGN.CENTER if c!=1 else PP_ALIGN.LEFT)

# Slide 11 - Brandpreventie
s = prs.slides.add_slide(BL)
titlebar(s, "Brandpreventie"); logo(s); footer(s, 11)
tf = txtbox(s, Inches(0.8), Inches(1.4), Inches(11), Inches(5), "PGS 15 Preventiedoelen (D8–D12)", 18, True, PRIMARY)
for t in ["D8: Voorkomen ontstaan brand — bronmaatregelen","D9: Beperken brandontwikkeling — compartimentering","D10: Beperken smoke & gasontwikkeling","D11: Voorkomen verspreiding brand naar omgeving","D12: Semi-automatische blussing (alleen VN-A)"]:
    bullet(tf, t, 14)
bullet(tf, "", 8)
p = tf.add_paragraph(); p.text = "Phoenix Metals — Basisniveau volstaat"; p.font.bold=True; p.font.size=Pt(18); p.font.color.rgb=GREEN
for t in ["ADR 8 onbrandbaar → geen brandbaarheidsrisico","Volume 390 kg → ver onder alle drempels","Compartimentering: bestaande situatie volstaat","Geen VBB-installatie of blussysteem vereist"]:
    bullet(tf, t, 13)

# Slide 12 - Schaalbaarheid
s = prs.slides.add_slide(BL)
titlebar(s, "Schaalbaarheid & Groeiscenario's"); logo(s); footer(s, 12)
t = mktable(s, 5, 4, Inches(0.8), Inches(1.5), Inches(11), Inches(3.5))
hrow(t, ["Fase","Volume ADR 8","Vereist Niveau","Investeringsraming"])
for r, row in enumerate([["Huidig","~390 kg (~300 l)","Basisniveau","Lekbakken + scheiding (~€5K–15K)"],["Fase 1","< 10.000 kg","Basis + 60 min Wbdbo","Brandwerende constructie (~€15K–30K)"],["Fase 2","10.000–20.000 kg","VN-B (D6, D10, D11)","Blusinstallatie + detectie (~€50K–100K)"],["Fase 3","> 20.000 kg","VN-A (D12)","VBB + PAC-doormelding (~€100K+)"]]):
    for c, v in enumerate(row):
        scell(t.cell(r+1,c), v, 12, r==0, GREEN if r==0 else DARK, RGBColor(0xEC,0xFD,0xF5) if r==0 else (LIGHT_BG if r%2==1 else WHITE))
txtbox(s, Inches(0.8), Inches(5.5), Inches(11), Inches(1), "Bij toevoeging andere ADR-klassen (3, 5.1, 6.1) veranderen compatibiliteitseisen én drempels.\nRaadpleeg JvG Consultancy vóór uitbreiding chemisch register.", 12, color=DARK)

# Slide 13 - Actiepunten
s = prs.slides.add_slide(BL)
titlebar(s, "Actiepunten — Top 5 Directe Acties"); logo(s); footer(s, 13)
t = mktable(s, 6, 4, Inches(0.5), Inches(1.4), Inches(12), Inches(4))
hrow(t, ["#","Actie","Eigenaar","Deadline"])
actions = [["1","Scheid NaOH en H₂SO₄ in aparte zones + lekbakken","Facility Manager","Week 1"],["2","Plaats lekbakken ≥110% voor alle vloeibare ADR 8 stoffen","Facility Manager","Week 1"],["3","Stel SDS-map samen (6 VIB's) + instructiekaart lekkage","HSEQ / Q&A","Week 2"],["4","Calcium oxide apart opslaan van alle H₂SO₄ producten","Facility Manager","Week 1"],["5","Documenteer huidige opslagconfiguratie + foto's","HSEQ","Week 2"]]
for r, row in enumerate(actions):
    for c, v in enumerate(row):
        scell(t.cell(r+1,c), v, 11, c==0, DARK, LIGHT_BG if r%2==0 else WHITE, PP_ALIGN.CENTER if c in [0,3] else PP_ALIGN.LEFT)
txtbox(s, Inches(0.5), Inches(5.8), Inches(12), Inches(1), "Prioriteit: CRITIEK — Actie 1 en 2 zijn direct verplicht conform PGS 15 M35 + M30.\nAansprakelijkheid directie bij niet-tijdige uitvoering.", 12, True, RED)

# Slide 14 - Vragen/Contact
s = prs.slides.add_slide(BL)
bg = s.background.fill; bg.solid(); bg.fore_color.rgb = PRIMARY
bar = s.shapes.add_shape(MSO_SHAPE.RECTANGLE, 0, SH - Inches(1.8), SW, Inches(0.06))
bar.fill.solid(); bar.fill.fore_color.rgb = ACCENT; bar.line.fill.background()
logo(s, True)
txtbox(s, Inches(0.8), Inches(1.5), Inches(10), Inches(0.8), "Vragen?", 36, True, WHITE, PP_ALIGN.CENTER)
txtbox(s, Inches(0.8), Inches(2.8), Inches(10), Inches(0.5), "Bedankt voor uw aandacht", 20, False, RGBColor(0xBB,0xCC,0xDD), PP_ALIGN.CENTER)
txtbox(s, Inches(2), Inches(4.0), Inches(9), Inches(2), "JvG Consultancy\nPGS-15 Compliance Specialist\n\nE: info@jvg-consultancy.nl\nW: www.jvg-consultancy.nl", 16, False, WHITE, PP_ALIGN.CENTER)
footer(s, 14)

# Save
out = "/root/projects/jg/2026-pgs15-phoenix-metals/deliverables/pptx/PGS15_Management_Presentatie_v1.0.pptx"
os.makedirs(os.path.dirname(out), exist_ok=True)
prs.save(out)
sz = os.path.getsize(out)
print(f"✅ Saved: {out} ({sz:,} bytes, {sz/1024:.1f} KB)")