#!/usr/bin/env python3
"""REACH Compliance Scanner — Engine v2 (Deep Per-Substance Analysis)

Elke stof krijgt 12 genummerde secties conform PGS 15 kwaliteit.
"""

import json, logging
from reach_data import (
    ROLLEN, TONNAGE_BANDS, get_tonnage_band, SVHC_CATEGORIEEN,
    NOTIFICATIES, DOCUMENTEN, VRIJSTELLINGEN, RISICO_MATRIX,
    REACH_ARTIKELEN, MAATREGELEN_PBM, MAATREGELEN_TECHNISCH, MAATREGELEN_ORGANISATORISCH,
)
from stof_lookup import lookup_combined, find_consortium, ANNEX_VEREISTEN, match_stof_specifiek, STOF_SPECIFIEK

log = logging.getLogger(__name__)

# ── PICTOGRAM MAPPING ──
PICTOGRAM_MAP = {
    'GHS01': '💣', 'GHS02': '🔥', 'GHS03': '🔥', 'GHS04': '🧪',
    'GHS05': '☢️', 'GHS06': '💀', 'GHS07': '⚠️', 'GHS08': '🛡️',
    'GHS09': '🌿',
}

# ── GEVARENKLAS KLEUREN ──
GEVAAR_COLORS = {
    'cmr': '#EF4444', 'mutageen': '#EF4444', 'reprotoxisch': '#EF4444',
    'acuut_toxisch': '#F59E0B', 'brandbaar': '#F97316', 'corrosief': '#DC2626',
    'milieu': '#22C55E', 'pbt_vpvb': '#A855F7',
}


class REACHAnalyzer:
    def __init__(self, session_data):
        self.data = session_data

    def analyseer(self):
        stoffen_result = []
        for stof in self.data.get('stoffen', []):
            stoffen_result.append(self._analyseer_stof(stof))

        verplichtingen = self._bepaal_verplichtingen(stoffen_result)
        notificaties = self._bepaal_notificaties(stoffen_result)
        documenten = self._bepaal_documenten()
        maatregelen = self._bepaal_maatregelen(stoffen_result)
        actiepunten = self._genereer_actiepunten(stoffen_result)
        consortia = self._bepaal_consortia(stoffen_result)

        samenvatting = {
            'totaal_stoffen': len(stoffen_result),
            'svhc_stoffen': sum(1 for s in stoffen_result if s.get('s1', {}).get('svhc')),
            'restrictie_stoffen': sum(1 for s in stoffen_result if s.get('s5', {}).get('heeft_restricties')),
            'registratie_verplicht': sum(1 for s in stoffen_result if s.get('s3', {}).get('registratie_verplicht')),
            'totaal_verplichtingen': len(verplichtingen),
            'totaal_notificaties': len(notificaties),
            'totaal_documenten': len(documenten),
            'totaal_actiepunten': len(actiepunten),
            'kritieke_acties': sum(1 for a in actiepunten if a.get('prioriteit') == 'KRITIEK'),
            'hoog_acties': sum(1 for a in actiepunten if a.get('prioriteit') == 'HOOG'),
            'consortia_gevonden': len(consortia),
            'stofspecifieke_categorieen': list(set(
                c for s in stoffen_result for c in s.get('s7', {}).get('categorieen', [])
            )),
        }

        return {
            'bedrijf': self._bedrijfsinfo(),
            'per_stof': stoffen_result,
            'verplichtingen': verplichtingen,
            'notificaties': notificaties,
            'documenten': documenten,
            'maatregelen': maatregelen,
            'actiepunten': actiepunten,
            'consortia': consortia,
            'samenvatting': samenvatting,
        }

    def _bedrijfsinfo(self):
        rol = self.data.get('rol', 'downstream_gebruiker')
        ri = ROLLEN.get(rol, ROLLEN['downstream_gebruiker'])
        return {
            'naam': self.data.get('bedrijfsnaam', 'Onbekend'),
            'rol': rol, 'rol_label': ri['label'], 'rol_art': ri['art'],
            'locatie': self.data.get('locatie', 'EU'),
            'sector': self.data.get('sector', 'Overig'),
            'heeft_registratie_verplichting': ri['tonnage_bands'],
        }

    # ═══════════════════════════════════════════════════════════════
    # PER-STOF ANALYSE — 12 SECTIES
    # ═══════════════════════════════════════════════════════════════

    def _analyseer_stof(self, stof):
        cas = stof.get('cas', '').strip()
        naam = stof.get('naam', '').strip()
        volume = float(stof.get('volume', 0) or 0)
        stofvorm = stof.get('stofvorm', 'vast')
        is_mengsel = stof.get('mengsel', False)
        percentage = float(stof.get('percentage', 100) or 100)
        toepassing = stof.get('toepassing', '')
        vrijstelling_key = stof.get('vrijstelling', None)

        # Lookup
        lookup = {}
        try:
            if cas or naam:
                lookup = lookup_combined(cas=cas, naam=naam)
        except Exception as e:
            log.warning(f'Lookup failed for {cas}/{naam}: {e}')
            lookup = {}

        stof_naam = lookup.get('naam') or naam or cas
        found_cas = lookup.get('cas') or cas
        h_zinnen = lookup.get('h_zinnen', [])
        p_zinnen = lookup.get('p_zinnen', [])
        formule = lookup.get('formule', '')
        molgewicht = lookup.get('molgewicht')
        echa = lookup.get('echa', {})
        consortium = lookup.get('consortium')
        consortia_all = lookup.get('consortia', [])  # Alle consortia
        stof_categories = lookup.get('stof_categories', [])
        stof_specifiek = lookup.get('stof_specifiek', [])

        band_key, band = get_tonnage_band(volume)
        annex_info = ANNEX_VEREISTEN.get(band_key, ANNEX_VEREISTEN.get('1-10', {}))
        rol = self.data.get('rol', 'downstream_gebruiker')
        rol_info = ROLLEN.get(rol, ROLLEN['downstream_gebruiker'])

        svhc = echa.get('svhc', False)
        svhc_reden = echa.get('svhc_reden')
        autorisatie = echa.get('annex_xiv', False)
        restrictie = echa.get('annex_xvii', False)
        vrijstelling = VRIJSTELLINGEN.get(vrijstelling_key) if vrijstelling_key else None

        # ── SECTIE 1: Productgegevens ──
        s1 = {
            'cas': found_cas, 'naam': stof_naam, 'formule': formule,
            'molgewicht': molgewicht, 'stofvorm': stofvorm, 'volume': volume,
            'tonnage_band': band_key, 'band_label': band['label'],
            'is_mengsel': is_mengsel, 'percentage': percentage,
            'toepassing': toepassing, 'svhc': svhc, 'autorisatie': autorisatie,
            'restrictie': restrictie,
            'gevonden': lookup.get('gevonden', False),
            'bronnen': lookup.get('bronnen', []),
        }

        # ── SECTIE 2: REACH Toepassing ──
        reach_toepassing = 'Van toepassing'
        artikelen = ['Art. 1 (reikwijdte)']
        vrijstelling_detail = None
        if vrijstelling:
            reach_toepassing = 'Vrijgesteld'
            vrijstelling_detail = vrijstelling
            artikelen = [vrijstelling['art']]
        elif volume < 1:
            artikelen.append('Art. 2(1) — < 1 ton/jaar, geen registratieplicht')
        else:
            artikelen.append('Art. 5, 6 — Registratieverplichting')
            if is_mengsel:
                artikelen.append('Art. 3(2) — Als mengsel op de markt')
            else:
                artikelen.append('Art. 3(1) — Als zuivere stof')

        s2 = {
            'van_toepassing': not bool(vrijstelling),
            'status': 'vrijgesteld' if vrijstelling else 'van_toepassing',
            'toepassing': reach_toepassing,
            'artikelen': artikelen,
            'vrijstelling': vrijstelling_detail,
        }

        # ── SECTIE 3: Registratieverplichting ──
        registratie_nodig = (rol_info.get('tonnage_bands', False)
                             and band.get('registratie', False)
                             and not vrijstelling)

        # Bandbreedte-ladder: toon ALLE tonnage-banden met hun eisen
        band_ladder = []
        band_keys_ordered = ['<1', '1-10', '10-100', '100-1000', '>1000']
        band_labels = {
            '<1': '< 1 ton/jaar',
            '1-10': '1 – 10 ton/jaar',
            '10-100': '10 – 100 ton/jaar',
            '100-1000': '100 – 1.000 ton/jaar',
            '>1000': '> 1.000 ton/jaar',
        }
        band_colors = {
            '<1': '#22C55E', '1-10': '#3B82F6', '10-100': '#F59E0B',
            '100-1000': '#F97316', '>1000': '#EF4444',
        }
        for bk in band_keys_ordered:
            bi = TONNAGE_BANDS.get(bk, {})
            ai = ANNEX_VEREISTEN.get(bk, {})
            is_huidig = (bk == band_key)
            # Bepaal status
            if volume < 1 and bk == '<1':
                status = 'huidig'
            elif is_huidig:
                status = 'huidig'
            elif band_keys_ordered.index(bk) < band_keys_ordered.index(band_key):
                status = 'overschreden'
            else:
                status = 'toekomst'
            band_ladder.append({
                'band_key': bk,
                'label': band_labels.get(bk, bk),
                'drempel': bi.get('drempel', bk),
                'status': status,
                'is_huidig': is_huidig,
                'color': band_colors.get(bk, '#6B7280'),
                'registratie_verplicht': bi.get('registratie', False),
                'annex': ai.get('annex', 'Geen'),
                'tests': ai.get('tests', []),
                'aantal_tests': len(ai.get('tests', [])),
                'kosten_indicatie': ai.get('kosten_indicatie', '-'),
                'doorlooptijd': ai.get('doorlooptijd', '-'),
                'vereisten': bi.get('vereisten', []),
                'documenten': bi.get('documenten', []),
                'extra_vs_huidig': [],  # wordt hieronder gevuld
            })

        # Vul "extra_vs_huidig" in: wat heb je EXTRA nodig t.o.v. huidige band
        huidig_idx = band_keys_ordered.index(band_key)
        for idx, rung in enumerate(band_ladder):
            if idx > huidig_idx and idx > 0:
                extra = []
                prev_annex = band_ladder[idx-1].get('annex', '')
                curr_annex = rung.get('annex', '')
                if curr_annex and curr_annex != prev_annex:
                    extra.append(f'+ {curr_annex} testen')
                prev_tests = band_ladder[idx-1].get('aantal_tests', 0)
                curr_tests = rung.get('aantal_tests', 0)
                if curr_tests > prev_tests:
                    extra.append(f'+{curr_tests - prev_tests} extra testen')
                if idx >= 2:  # 10+ ton
                    extra.append('CSR verplicht')
                if idx >= 4:  # >1000 ton
                    extra.append('Volledige Annex X dataset')
                rung['extra_vs_huidig'] = extra

        s3 = {
            'registratie_verplicht': registratie_nodig,
            'rol_registreert': rol_info.get('tonnage_bands', False),
            'band_key': band_key, 'band_label': band['label'],
            'annex': annex_info.get('annex', ''),
            'tests': annex_info.get('tests', []),
            'kosten_indicatie': annex_info.get('kosten_indicatie', ''),
            'doorlooptijd': annex_info.get('doorlooptijd', ''),
            'vereisten': band.get('vereisten', []),
            'documenten_lijst': band.get('documenten', []),
            'osor': 'One Substance, One Registration (OSOR) — SIEF-deelname verplicht voor datadeling en kostendeling' if registratie_nodig else None,
            'band_ladder': band_ladder,
            'volume': volume,
        }

        # ── SECTIE 4: SVHC Status ──
        s4 = {
            'svhc': svhc,
            'svhc_reden': svhc_reden,
            'autorisatie': autorisatie,
            'restrictie': restrictie,
            'candidate_list': svhc,
            'annex_xiv': autorisatie,
            'annex_xvii': restrictie,
            'info_art33': 'Art. 33 informatieplicht: kopers informeren >0.1% gewicht in artikel, consumenten op aanvraag binnen 45 dagen' if svhc else None,
            'info_scip': 'SCIP-melding verplicht via ECHA (Art. 33 WFD) bij >0.1% gewicht in artikel' if svhc else None,
            'info_autorisatie': 'Autorisatie AANVRAGEN vereist vóór sunset date (Annex XIV, Art. 56)' if autorisatie else None,
        }

        # ── SECTIE 5: Restricties ──
        restricties_lijst = []
        if restrictie:
            restricties_lijst.append({
                'bron': 'Annex XVII',
                'beschrijving': 'Stof staat op Annex XVII — controleer specifieke gebruiksbeperkingen op ECHA',
                'actie': 'Restrictievoorwaarden controleren en naleven',
            })
        if svhc:
            restricties_lijst.append({
                'bron': 'SVHC Candidate List',
                'beschrijving': f'Identificatie als SVHC: {svhc_reden}',
                'actie': 'Vervanging overwegen (Art. 55), autorisatie aanvragen indien geen alternatief',
            })
        if autorisatie:
            restricties_lijst.append({
                'bron': 'Annex XIV (Autorisatie)',
                'beschrijving': 'Stof vereist autorisatie voor gebruik na sunset date',
                'actie': 'Autorisatie aanvragen bij ECHA of overstappen op alternatief',
            })
        s5 = {
            'heeft_restricties': bool(restricties_lijst),
            'restricties': restricties_lijst,
        }

        # ── SECTIE 6: CLP Classificatie ──
        # Parse H-zinnen into categories
        h_categories = self._parse_h_categories(h_zinnen)
        pictogrammen = self._derive_pictograms(h_zinnen)
        s6 = {
            'h_zinnen': h_zinnen,
            'p_zinnen': p_zinnen,
            'pictogrammen': pictogrammen,
            'gevarencategorieen': h_categories,
            'geharmoniseerd': lookup.get('gevonden', False),
            'signaalwoord': 'Gevaar' if any(h[1] in ('3','4') for h in h_zinnen if len(h)>=2 and h[1].isdigit()) else 'Waarschuwing' if h_zinnen else '',
        }

        # ── SECTIE 7: Stofspecifieke categorieën ──
        s7_categories = []
        for cat_key in stof_categories:
            spec = STOF_SPECIFIEK.get(cat_key, {})
            color = GEVAAR_COLORS.get(cat_key, '#F59E0B')
            s7_categories.append({
                'key': cat_key,
                'label': cat_key.replace('_', ' ').title(),
                'color': color,
                'extra_verplichtingen': spec.get('extra_verplichtingen', []),
                'maatregelen': spec.get('maatregelen', []),
                'consortium_tip': spec.get('consortium_tip', ''),
            })
        s7 = {
            'categorieen': s7_categories,
            'aantal': len(s7_categories),
        }

        # ── SECTIE 8: Consortium & SIEF ──
        s8 = {
            'consortium': consortium,
            'consortia': consortia_all,  # Alle consortia voor deze stof
            'sief_verplicht': registratie_nodig,
            'waarom_deelnemen': [
                'Kostendeling test-data (besparing tot 80%)',
                'Gezamenlijke CSR opstelling',
                'OSOR-principe: One Substance, One Registration',
                'Toegang tot bestaande registratiedata via Letter of Access (LoA)',
                'SIEF-verplichting (Art. 29 REACH)',
            ],
        }

        # ── SECTIE 9: Documenten ──
        s9_docs = []
        rol_docs = {k: v for k, v in DOCUMENTEN.items() if rol in v.get('rollen', [])}
        for dk, doc in rol_docs.items():
            verplicht = True
            if dk == 'csr' and volume < 10:
                verplicht = False
            if dk == 'registratiedossier' and volume < 1:
                verplicht = False
            if dk == 'sds' and not h_zinnen:
                verplicht = False
            s9_docs.append({
                'key': dk, 'label': doc['label'], 'art': doc['art'],
                'wanneer': doc['wanneer'], 'structuur': doc['structuur'],
                'frequentie': doc['frequentie'], 'verplicht': verplicht,
            })
        s9 = {'documenten': s9_docs}

        # ── SECTIE 10: Maatregelen ──
        blootstelling = self.data.get('blootstelling_routes', [])
        tech = list(MAATREGELEN_TECHNISCH.values())
        pbm_list = []
        org_list = list(MAATREGELEN_ORGANISATORISCH.values())

        if any(h.startswith('H3') for h in h_zinnen):
            pbm_list.append(MAATREGELEN_PBM['handschoenen'])
            pbm_list.append(MAATREGELEN_PBM['veiligheidsbril'])
        if any(h.startswith(('H330','H331','H311','H335')) for h in h_zinnen):
            pbm_list.append(MAATREGELEN_PBM['gelaatsmasker'])
        if any(h.startswith('H314') for h in h_zinnen):
            tech.append(MAATREGELEN_TECHNISCH['nooddouche'])
            pbm_list.append(MAATREGELEN_PBM['beschermlaag'])
        if any(h.startswith('H2') for h in h_zinnen):
            tech.append(MAATREGELEN_TECHNISCH['atex'])
        if any(h.startswith('H4') for h in h_zinnen):
            tech.append(MAATREGELEN_TECHNISCH['lekopvang'])
        if 'Inhalatie (ademhaling)' in blootstelling:
            tech.append(MAATREGELEN_TECHNISCH['afzuiging'])
            pbm_list.append(MAATREGELEN_PBM['gelaatsmasker'])
        if 'Huidcontact' in blootstelling:
            pbm_list.append(MAATREGELEN_PBM['handschoenen'])
        if 'Oogcontact' in blootstelling:
            pbm_list.append(MAATREGELEN_PBM['veiligheidsbril'])

        for spec in stof_specifiek:
            for m in spec.get('maatregelen', []):
                if m not in tech:
                    tech.append(m)

        # Dedup
        def dedup(items):
            seen = set()
            out = []
            for m in items:
                k = m.get('label', str(m)) if isinstance(m, dict) else str(m)
                if k not in seen:
                    seen.add(k)
                    out.append(m)
            return out

        s10 = {
            'technisch': dedup(tech),
            'pbm': dedup(pbm_list),
            'organisatorisch': dedup(org_list),
        }

        # ── SECTIE 11: Notificaties ──
        s11_notifs = []
        for key, notif in NOTIFICATIES.items():
            van_toepassing = False
            if key == 'pcn_ufi' and is_mengsel:
                van_toepassing = True
            if key in ('svhc_scip', 'svhc_informatie') and svhc:
                van_toepassing = True
            if key == 'classificatie_melding' and rol in ('fabrikant', 'importeur', 'enkel_importeur'):
                van_toepassing = True
            if key == 'du_gebruik' and rol == 'downstream_gebruiker':
                van_toepassing = True
            if key == 'artikel_7' and rol in ('fabrikant', 'importeur', 'enkel_importeur') and volume >= 1:
                van_toepassing = True
            if van_toepassing:
                s11_notifs.append({
                    'key': key, 'label': notif['label'], 'art': notif['art'],
                    'voorwaarde': notif['voorwaarde'], 'deadline': notif['deadline'],
                    'actie': notif['actie'],
                })
        s11 = {'notificaties': s11_notifs}

        # ── SECTIE 12: Actiepunten ──
        s12 = self._stof_actiepunten(stof_naam, volume, band, rol, svhc, autorisatie, restrictie, h_zinnen, is_mengsel, stof_specifiek, consortium, registratie_nodig, annex_info)

        # ── Compliance matrix (compact) ──
        compliance = [
            {'domein': 'REACH Toepassing', 'status': s2['status'], 'details': s2['toepassing']},
            {'domein': 'Registratie', 'status': 'verplicht' if s3['registratie_verplicht'] else 'niet_verplicht', 'details': f"{s3['band_label']} — {'REGISTRATIE VERPLICHT' if s3['registratie_verplicht'] else 'Geen registratieplicht'}"},
            {'domein': 'SVHC', 'status': 'svhc' if svhc else 'ok', 'details': f"SVHC — {svhc_reden}" if svhc else 'Niet op Candidate List'},
            {'domein': 'CLP', 'status': 'geclassificeerd' if h_zinnen else 'onbekend', 'details': ', '.join(h_zinnen) if h_zinnen else 'Niet gevonden'},
            {'domein': 'SDS', 'status': 'verplicht' if h_zinnen else 'aanbevolen', 'details': 'Verplicht bij gevaarlijke stoffen' if h_zinnen else 'Aanbevolen'},
            {'domein': 'CSR', 'status': 'verplicht' if volume >= 10 and registratie_nodig else 'niet_verplicht', 'details': 'VERPLICHT bij ≥10 ton/jaar' if volume >= 10 and registratie_nodig else 'Niet verplicht'},
        ]
        if autorisatie:
            compliance.append({'domein': 'Autorisatie', 'status': 'autorisatie_verplicht', 'details': '⚠️ AUTORISATIE VEREIST — Annex XIV'})
        if restrictie:
            compliance.append({'domein': 'Restrictie', 'status': 'beperkt', 'details': 'Annex XVII restricties'})

        return {
            'naam': stof_naam, 'cas': found_cas, 'volume': volume, 'stofvorm': stofvorm,
            'is_mengsel': is_mengsel, 'percentage': percentage,
            'tonnage_band': band_key, 'band_info': band,
            'compliance_matrix': compliance,
            's1': s1, 's2': s2, 's3': s3, 's4': s4, 's5': s5,
            's6': s6, 's7': s7, 's8': s8, 's9': s9, 's10': s10,
            's11': s11, 's12': s12,
        }

    def _parse_h_categories(self, h_zinnen):
        cats = []
        h_codes = set()
        for h in h_zinnen:
            m = __import__('re').match(r'H(\d{3})', h)
            if m:
                h_codes.add(int(m.group(1)))
        if h_codes & {300, 310, 330, 301, 311, 331}:
            cats.append({'label': 'Acute toxiciteit', 'color': '#F59E0B'})
        if h_codes & {314}:
            cats.append({'label': 'Huidcorrosie/irritatie', 'color': '#DC2626'})
        if h_codes & {318}:
            cats.append({'label': 'Oogschade/irritatie', 'color': '#F97316'})
        if h_codes & {317, 334}:
            cats.append({'label': 'Sensibilisatie', 'color': '#A855F7'})
        if h_codes & {340, 350}:
            cats.append({'label': 'Carcinogeen', 'color': '#EF4444'})
        if h_codes & {360, 370}:
            cats.append({'label': 'Reprotoxisch', 'color': '#EF4444'})
        if h_codes & {220, 224, 240, 241, 250, 260, 261}:
            cats.append({'label': 'Ontvlambaarheid', 'color': '#F97316'})
        if h_codes & {400, 410, 411}:
            cats.append({'label': 'Milieugevaarlijk', 'color': '#22C55E'})
        return cats

    def _derive_pictograms(self, h_zinnen):
        pics = []
        h_codes = set()
        for h in h_zinnen:
            m = __import__('re').match(r'H(\d{3})', h)
            if m:
                h_codes.add(int(m.group(1)))
        if h_codes & {200, 201, 202, 203, 204, 240, 241, 242, 260, 261}:
            pics.append({'code': 'GHS02', 'label': 'Brandbaar', 'icon': '🔥'})
        if h_codes & {300, 310, 311, 330, 331}:
            pics.append({'code': 'GHS06', 'label': 'Acuut toxisch', 'icon': '💀'})
        if h_codes & {314}:
            pics.append({'code': 'GHS05', 'label': 'Corrosief', 'icon': '☢️'})
        if h_codes & {318, 319}:
            pics.append({'code': 'GHS07', 'label': 'Irriterend', 'icon': '⚠️'})
        if h_codes & {400, 410, 411}:
            pics.append({'code': 'GHS09', 'label': 'Milieugevaarlijk', 'icon': '🌿'})
        if h_codes & {340, 350, 360, 370}:
            pics.append({'code': 'GHS08', 'label': 'Gezondheidsgevaar', 'icon': '🛡️'})
        return pics

    def _stof_actiepunten(self, naam, volume, band, rol, svhc, autorisatie, restrictie, h_zinnen, is_mengsel, stof_specifiek, consortium, registratie_nodig, annex_info):
        acties = []
        if registratie_nodig:
            acties.append({
                'prioriteit': 'KRITIEK', 'actie': f'REACH-registratie starten — {band.get("label","")}',
                'deadline': 'Voorafgaand aan marktintroductie', 'art': 'REACH Art. 5, 6',
                'details': f"Annex: {annex_info.get('annex','')} | Kosten: {annex_info.get('kosten_indicatie','')} | Doorlooptijd: {annex_info.get('doorlooptijd','')}",
            })
        if consortium:
            acties.append({
                'prioriteit': 'HOOG', 'actie': f'Consortium/SIEF deelnemen: {consortium["naam"]}',
                'deadline': 'Zo snel mogelijk — kostendeling + datadeling', 'art': 'REACH Art. 29-30',
                'details': f"Website: {consortium.get('website','')} | LoA: {'Ja' if consortium.get('letter_of_access') else 'Onbekend'} | {consortium.get('opmerking','')}",
            })
        if svhc:
            acties.append({'prioriteit': 'KRITIEK', 'actie': 'SVHC-maatregelen: klanten informeren, SCIP-melding, alternatieven beoordelen', 'deadline': 'Onmiddellijk', 'art': 'REACH Art. 33'})
        if autorisatie:
            acties.append({'prioriteit': 'KRITIEK', 'actie': 'AUTORISATIE AANVRAGEN (Annex XIV)', 'deadline': 'Vóór sunset date', 'art': 'REACH Art. 56'})
        if restrictie:
            acties.append({'prioriteit': 'HOOG', 'actie': 'Restrictievoorwaarden controleren (Annex XVII)', 'deadline': 'Onmiddellijk', 'art': 'Annex XVII'})
        if volume >= 10 and registratie_nodig:
            acties.append({'prioriteit': 'HOOG', 'actie': 'CSR opstellen (inclusief blootstellingsscenario\'s)', 'deadline': 'Onderdeel van registratiedossier', 'art': 'REACH Art. 14'})
        if h_zinnen:
            acties.append({'prioriteit': 'HOOG', 'actie': 'SDS beschikbaar en actueel houden', 'deadline': 'Bij inwerkingtreding', 'art': 'REACH Art. 31'})
        if is_mengsel:
            acties.append({'prioriteit': 'MEDIUM', 'actie': 'PCN-melding + UFI genereren voor mengsel', 'deadline': 'Bij marktintroductie', 'art': 'CLP Annex VIII'})
        for spec in stof_specifiek:
            acties.append({
                'prioriteit': 'HOOG',
                'actie': f'[{spec["categorie"].upper()}] Extra verplichtingen — {spec.get("consortium_tip", "")}',
                'deadline': 'Onmiddellijk', 'art': 'REACH/CLP/Arbowet',
            })
        acties.append({'prioriteit': 'MEDIUM', 'actie': 'CLP-classificatie verifiëren', 'deadline': 'Continue', 'art': 'CLP Art. 4'})
        acties.append({'prioriteit': 'MEDIUM', 'actie': 'RI&E bijwerken met REACH/CLP aspecten', 'deadline': 'Jaarlijks', 'art': 'Arbowet Art. 5'})
        return acties

    def _bepaal_verplichtingen(self, stoffen_result):
        verp = []
        rol = self.data.get('rol', 'downstream_gebruiker')
        ri = ROLLEN.get(rol, {})
        for v in ri.get('verplichtingen', []):
            verp.append({'verplichting': v, 'bron': ri.get('art', ''), 'prioriteit': 'hoog'})
        for s in stoffen_result:
            naam = s['naam']
            if s.get('s4', {}).get('svhc'):
                verp.extend([
                    {'verplichting': f'SVHC-informatieplicht voor {naam} (Art. 33)', 'bron': 'REACH Art. 33', 'prioriteit': 'kritiek'},
                    {'verplichting': f'SCIP-melding overwegen voor {naam}', 'bron': 'WFD/REACH', 'prioriteit': 'hoog'},
                ])
            if s.get('s4', {}).get('autorisatie'):
                verp.append({'verplichting': f'Autorisatie AANVRAGEN voor {naam}', 'bron': 'REACH Art. 56', 'prioriteit': 'kritiek'})
            if s.get('s5', {}).get('heeft_restricties'):
                verp.append({'verplichting': f'Restrictievoorwaarden naleven voor {naam}', 'bron': 'Annex XVII', 'prioriteit': 'hoog'})
        return verp

    def _bepaal_notificaties(self, stoffen_result):
        seen = set()
        result = []
        for s in stoffen_result:
            for n in s.get('s11', {}).get('notificaties', []):
                key = n.get('key', n.get('label', ''))
                if key not in seen:
                    seen.add(key)
                    result.append(n)
        return result

    def _bepaal_documenten(self):
        rol = self.data.get('rol', 'downstream_gebruiker')
        return [{'key': k, 'label': v['label'], 'art': v['art'], 'wanneer': v['wanneer'], 'structuur': v['structuur'], 'frequentie': v['frequentie']}
                for k, v in DOCUMENTEN.items() if rol in v.get('rollen', [])]

    def _bepaal_maatregelen(self, stoffen_result):
        tech, pbm, org = set(), set(), set()
        for s in stoffen_result:
            m = s.get('s10', {})
            for item in m.get('technisch', []):
                k = item.get('label', str(item)) if isinstance(item, dict) else str(item)
                tech.add(k)
            for item in m.get('pbm', []):
                k = item.get('label', str(item)) if isinstance(item, dict) else str(item)
                pbm.add(k)
            for item in m.get('organisatorisch', []):
                k = item.get('label', str(item)) if isinstance(item, dict) else str(item)
                org.add(k)
        return {'technisch': sorted(tech), 'pbm': sorted(pbm), 'organisatorisch': sorted(org)}

    def _bepaal_consortia(self, stoffen_result):
        consortia = []
        for s in stoffen_result:
            c = s.get('s8', {}).get('consortium')
            if c:
                consortia.append({
                    'stof': s['naam'],
                    'consortium': c['naam'],
                    'website': c.get('website'),
                    'sief_actief': c.get('sief_actief'),
                    'letter_of_access': c.get('letter_of_access'),
                    'opmerking': c.get('opmerking'),
                })
        return consortia

    def _genereer_actiepunten(self, stoffen_result):
        acties = []
        for s in stoffen_result:
            acties.extend(s.get('s12', []))
        return acties
