#!/usr/bin/env python3
"""Test script voor PGS 15:2025 Compliance Engine v2.0

Test de Phoenix Metals stoffenlijst (18 stoffen, 150 kg elk) tegen de v2 engine.
Alle asserts MOETEN slagen.
"""

import sys
import os

# Add engine directory to path
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))

from pgs15_compliance_engine_v2 import (
    determine_niveau, get_strictste_niveau, get_strictste_niveau_v2,
    bereken_geaggregeerd_niveau, PGS15ComplianceAnalyzer, _is_brandbaar,
)

# ═══════════════════════════════════════════════════════════════════
# PHOENIX METALS TESTDATA
# ═══════════════════════════════════════════════════════════════════

PHOENIX_STOFFEN = [
    {
        'naam': 'H₂SO₄ 25%',
        'cas': '7664-93-9',
        'adr_klasse': '8',
        'vg': 'II',
        'hoeveelheid': 150,
        'vorm': 'vloeistof',
        'h_zinnen': ['H314'],  # Corrosief, NIET brandbaar
    },
    {
        'naam': 'H₂O₂ 30%',
        'cas': '7722-84-1',
        'adr_klasse': '5.1',
        'vg': 'II',
        'hoeveelheid': 150,
        'vorm': 'vloeistof',
        'h_zinnen': ['H271', 'H302', 'H314', 'H332'],
    },
    {
        'naam': 'Na-persulfate',
        'cas': '7775-27-1',
        'adr_klasse': '5.1',
        'vg': 'III',
        'hoeveelheid': 150,
        'vorm': 'vast',
        'h_zinnen': ['H272', 'H302', 'H315', 'H317', 'H319', 'H335', 'H412'],
    },
    {
        'naam': 'HCl 20%',
        'cas': '7647-01-0',
        'adr_klasse': '8',
        'vg': 'II',
        'hoeveelheid': 150,
        'vorm': 'vloeistof',
        'h_zinnen': ['H314', 'H335'],  # Corrosief, NIET brandbaar
    },
    {
        'naam': 'Na-metavanadate',
        'cas': '13718-26-8',
        'adr_klasse': '6.1',
        'vg': 'III',
        'hoeveelheid': 150,
        'vorm': 'vast',
        'h_zinnen': ['H301', 'H311', 'H331', 'H410'],  # Acute tox cat 2, niet brandbaar
    },
    {
        'naam': 'Na-aluminate',
        'cas': '1302-42-7',
        'adr_klasse': '8',
        'vg': 'II',
        'hoeveelheid': 150,
        'vorm': 'vast',
        'h_zinnen': ['H314'],  # Corrosief, NIET brandbaar
    },
    {
        'naam': 'V₂O₅',
        'cas': '1314-62-1',
        'adr_klasse': '6.1',
        'vg': 'III',
        'hoeveelheid': 150,
        'vorm': 'vast',
        'h_zinnen': ['H330', 'H302', 'H372', 'H411'],  # Acute tox cat 1, niet brandbaar
    },
    {
        'naam': 'V-electrolyte',
        'cas': 'mixed',
        'adr_klasse': '8',
        'vg': 'II',
        'hoeveelheid': 150,
        'vorm': 'vloeistof',
        'h_zinnen': ['H314', 'H335'],  # Corrosief, NIET brandbaar
    },
]

# Expected results per substance
EXPECTED = {
    'H₂SO₄ 25%':        {'niveau': 'Basis', 'route_contains': 'Voetnoot c'},
    'H₂O₂ 30%':          {'niveau': 'C',     'route_contains': 'ADR 5.1'},
    'Na-persulfate':      {'niveau': 'C',     'route_contains': 'ADR 5.1'},
    'HCl 20%':            {'niveau': 'Basis', 'route_contains': 'Voetnoot c'},
    'Na-metavanadate':    {'niveau': 'C',     'route_contains': 'ADR 6.1'},
    'Na-aluminate':       {'niveau': 'Basis', 'route_contains': 'Voetnoot c'},
    'V₂O₅':              {'niveau': 'C',     'route_contains': 'ADR 6.1'},
    'V-electrolyte':      {'niveau': 'Basis', 'route_contains': 'Voetnoot c'},
}


def test_individuele_stoffen():
    """Test individuele stoffen tegen verwachte niveaus."""
    print("=" * 60)
    print("TEST 1: Individuele stoffen")
    print("=" * 60)
    
    all_pass = True
    for stof in PHOENIX_STOFFEN:
        naam = stof['naam']
        niveau, route, arbo_flags = get_strictste_niveau_v2(
            stof['adr_klasse'], stof['vg'], stof['hoeveelheid'],
            stof['h_zinnen'], stof['vorm'], stof.get('flashpoint'),
        )
        
        expected = EXPECTED[naam]
        niveau_ok = niveau == expected['niveau']
        route_ok = expected['route_contains'] in route
        
        status = "✅ PASS" if (niveau_ok and route_ok) else "❌ FAIL"
        if not (niveau_ok and route_ok):
            all_pass = False
        
        print(f"  {status} {naam:25s} → Niveau: {niveau:10s} Route: {route}")
        if not niveau_ok:
            print(f"       VERWACHT niveau: {expected['niveau']}, GEKREGEN: {niveau}")
        if not route_ok:
            print(f"       VERWACHT route bevat: '{expected['route_contains']}', GEKREGEN: '{route}'")
    
    assert all_pass, "Niet alle individuele stoffen testen geslaagd!"
    print()


def test_aggregatie():
    """Test geaggregeerd niveau voor Phoenix Metals."""
    print("=" * 60)
    print("TEST 2: Geaggregeerd niveau")
    print("=" * 60)
    
    result = bereken_geaggregeerd_niveau(PHOENIX_STOFFEN)
    
    print(f"  Eindniveau: {result['eindniveau']}")
    print(f"  Eindroute:  {result.get('eindroute', '')}")
    print(f"  Totaal:     {result['totaal_alle_stoffen_kg']} kg")
    print(f"  Catch-all:  {result['catch_all']}")
    print()
    
    for key, groep in result['per_groep'].items():
        print(f"  Groep {key}: {groep['niveau']:10s} ({groep['totale_hoeveelheid']} kg, {groep['aantal_stoffen']} stoffen)")
    
    # Assertions
    # ADR 8 PG II: 4 × 150 = 600 kg > 250 kg → maar onbrandbaar → voetnoot c → Basis
    adr8_key = ('8', 'II')
    if adr8_key in result['per_groep']:
        assert result['per_groep'][adr8_key]['niveau'] == 'Basis', \
            f"ADR 8 PG II aggregatie zou Basis moeten zijn (voetnoot c), niet {result['per_groep'][adr8_key]['niveau']}"
        print(f"  ✅ ADR 8 PG II aggregatie (600 kg, onbrandbaar) → Basis (voetnoot c)")
    
    # ADR 5.1: 2 × 150 = 300 kg < 2500 kg → C
    for vg in ('II', 'III'):
        key = ('5.1', vg)
        if key in result['per_groep']:
            assert result['per_groep'][key]['niveau'] == 'C', \
                f"ADR 5.1 VG {vg} zou C moeten zijn, niet {result['per_groep'][key]['niveau']}"
            print(f"  ✅ ADR 5.1 VG {vg} aggregatie ({result['per_groep'][key]['totale_hoeveelheid']} kg) → C")
    
    # ADR 6.1 PG III: 2 × 150 = 300 kg < 10000 kg → C
    adr61_key = ('6.1', 'III')
    if adr61_key in result['per_groep']:
        assert result['per_groep'][adr61_key]['niveau'] == 'C', \
            f"ADR 6.1 PG III zou C moeten zijn, niet {result['per_groep'][adr61_key]['niveau']}"
        print(f"  ✅ ADR 6.1 PG III aggregatie ({result['per_groep'][adr61_key]['totale_hoeveelheid']} kg) → C")
    
    # Eindniveau: C (strengste van C, Basis)
    assert result['eindniveau'] == 'C', \
        f"Eindniveau zou C moeten zijn, niet {result['eindniveau']}"
    print(f"  ✅ Eindniveau = C (strengste van: ADR 5.1→C, ADR 6.1→C, ADR 8→Basis)")
    
    print()


def test_clp_route_alleen_zonder_adr():
    """Test dat CLP-route NIET wordt toegepast op ADR-geclassificeerde stoffen."""
    print("=" * 60)
    print("TEST 3: CLP-route alleen voor stoffen ZONDER ADR")
    print("=" * 60)
    
    # Stoffen MET ADR en CMR: CMR mag NIET niveau bepalen
    stof_adr_cmr = {
        'naam': 'Benzine (met CMR)',
        'adr_klasse': '3',
        'vg': 'II',
        'hoeveelheid': 500,
        'h_zinnen': ['H225', 'H304', 'H315', 'H336', 'H340', 'H350'],  # Carc. 1A + Muta. 1B
    }
    niveau, route, arbo_flags = get_strictste_niveau_v2(
        stof_adr_cmr['adr_klasse'], stof_adr_cmr['vg'],
        stof_adr_cmr['hoeveelheid'], stof_adr_cmr['h_zinnen'],
    )
    
    assert 'ADR' in route, f"Route zou ADR moeten zijn, niet {route}"
    assert arbo_flags['cmr'] == True, "CMR flag zou True moeten zijn"
    assert arbo_flags['clp_route_toegepast'] == False, "CLP-route mag NIET zijn toegepast bij ADR-stof"
    print(f"  ✅ ADR + CMR stof: niveau via ADR-route, CMR in arbo_flags")
    
    # Stoffen ZONDER ADR maar WEL CMR: alleen Basis
    stof_clp_cmr = {
        'naam': 'CMR-stof zonder ADR',
        'adr_klasse': '',
        'vg': '',
        'hoeveelheid': 100,
        'h_zinnen': ['H350', 'H341'],
    }
    niveau2, route2, arbo_flags2 = get_strictste_niveau_v2(
        stof_clp_cmr['adr_klasse'], stof_clp_cmr['vg'],
        stof_clp_cmr['hoeveelheid'], stof_clp_cmr['h_zinnen'],
    )
    
    assert niveau2 == 'Basis', f"CMR zonder ADR zou Basis moeten zijn, niet {niveau2}"
    assert arbo_flags2['clp_route_toegepast'] == True, "CLP-route WEL toegepast bij niet-ADR stof"
    print(f"  ✅ CMR zonder ADR: niveau = Basis, CLP-route toegepast")
    
    print()


def test_voetnoot_c():
    """Test voetnoot c: ADR 8 onbrandbaar → Basis."""
    print("=" * 60)
    print("TEST 4: Voetnoot c (ADR 8+9 onbrandbaar)")
    print("=" * 60)
    
    # ADR 8 PG II onbrandbaar, 300 kg (>250 ondergrens) → Basis (voetnoot c)
    n1 = determine_niveau('8', 'II', 300, is_brandbaar_k8=False)
    assert n1 == 'Basis', f"ADR 8 PG II onbrandbaar 300kg → Basis (voetnoot c), niet {n1}"
    print(f"  ✅ ADR 8 PG II onbrandbaar 300 kg → Basis (voetnoot c)")
    
    # ADR 8 PG II brandbaar, 300 kg → C
    n2 = determine_niveau('8', 'II', 300, is_brandbaar_k8=True)
    assert n2 == 'C', f"ADR 8 PG II brandbaar 300kg → C, niet {n2}"
    print(f"  ✅ ADR 8 PG II brandbaar 300 kg → C")
    
    # ADR 8 PG II onbrandbaar, 150 kg (<250 ondergrens) → Basis (voetnoot c geldt altijd)
    n3 = determine_niveau('8', 'II', 150, is_brandbaar_k8=False)
    assert n3 == 'Basis', f"ADR 8 PG II onbrandbaar 150kg → Basis (voetnoot c), niet {n3}"
    print(f"  ✅ ADR 8 PG II onbrandbaar 150 kg → Basis (voetnoot c geldt altijd)")
    
    # ADR 8 PG II brandbaar, 150 kg (<250 ondergrens) → Buiten scope
    n4 = determine_niveau('8', 'II', 150, is_brandbaar_k8=True)
    assert n4 == 'Buiten scope', f"ADR 8 PG II brandbaar 150kg → Buiten scope, niet {n4}"
    print(f"  ✅ ADR 8 PG II brandbaar 150 kg → Buiten scope (ondergrens)")
    
    # ADR 9 PG II onbrandbaar, 500 kg → Basis
    n5 = determine_niveau('9', 'II', 500, is_brandbaar_k8=False)
    assert n5 == 'Basis', f"ADR 9 PG II onbrandbaar 500kg → Basis, niet {n5}"
    print(f"  ✅ ADR 9 PG II onbrandbaar 500 kg → Basis (voetnoot c)")
    
    print()


def test_brandbaarheid_detectie():
    """Test _is_brandbaar helper."""
    print("=" * 60)
    print("TEST 5: Brandbaarheid detectie")
    print("=" * 60)
    
    assert _is_brandbaar(['H225']) == True, "H225 = brandbaar"
    assert _is_brandbaar(['H314']) == False, "H314 = corrosief, NIET brandbaar"
    assert _is_brandbaar(['H271']) == True, "H271 = oxiderend, WEL brandbaar gevaar"
    assert _is_brandbaar(['H314', 'H335']) == False, "Alleen corrosief + STOT = niet brandbaar"
    assert _is_brandbaar([], flashpoint=50) == True, "Vlampunt 50°C = brandbaar"
    assert _is_brandbaar([], flashpoint=150) == False, "Vlampunt 150°C = niet brandbaar"
    assert _is_brandbaar([]) == False, "Geen H-zinnen, geen vlampunt = niet brandbaar"
    
    print(f"  ✅ Alle brandbaarheid checks correct")
    print()


def test_v1_backwards_compat():
    """Test v1 API backwards compatibility."""
    print("=" * 60)
    print("TEST 6: V1 backwards compatibility")
    print("=" * 60)
    
    # get_strictste_niveau (v1 signature) moet nog steeds werken
    n, r = get_strictste_niveau('8', 'II', 300, ['H314'], 'vloeistof', None)
    assert n == 'Basis', f"V1 API: ADR 8 II onbrandbaar 300kg → Basis, niet {n}"
    print(f"  ✅ get_strictste_niveau() (v1) werkt: {n}, {r}")
    
    # determine_niveau moet nog steeds werken
    n2 = determine_niveau('5.1', 'II', 150)
    assert n2 == 'C', f"determine_niveau() ADR 5.1 II 150kg → C, niet {n2}"
    print(f"  ✅ determine_niveau() werkt: {n2}")
    
    print()


def test_full_analyzer():
    """Test volledige analyzer met Phoenix Metals."""
    print("=" * 60)
    print("TEST 7: Volledige PGS15ComplianceAnalyzer")
    print("=" * 60)
    
    analyzer = PGS15ComplianceAnalyzer()
    result = analyzer.analyseer(PHOENIX_STOFFEN)
    
    assert result['samenvatting']['totaal_stoffen'] == 8
    assert result['samenvatting']['geaggregeerd_niveau'] == 'C'
    print(f"  ✅ Analyzer: 8 stoffen, geaggregeerd niveau = C")
    
    # Check individuele resultaten
    for stof_result in result['per_stof']:
        naam = stof_result['naam']
        if naam in EXPECTED:
            expected_niveau = EXPECTED[naam]['niveau']
            assert stof_result['niveau'] == expected_niveau, \
                f"{naam}: verwacht {expected_niveau}, gekregen {stof_result['niveau']}"
    
    print(f"  ✅ Alle individuele niveaus correct in analyzer output")
    print()


# ═══════════════════════════════════════════════════════════════════
# MAIN
# ═══════════════════════════════════════════════════════════════════

if __name__ == '__main__':
    print("\n" + "═" * 60)
    print("PGS 15:2025 COMPLIANCE ENGINE v2.0 — TEST SUITE")
    print("═" * 60 + "\n")
    
    try:
        test_individuele_stoffen()
        test_aggregatie()
        test_clp_route_alleen_zonder_adr()
        test_voetnoot_c()
        test_brandbaarheid_detectie()
        test_v1_backwards_compat()
        test_full_analyzer()
        
        print("=" * 60)
        print("🎉 ALLE TESTEN GESLAAGEN — Engine v2.0 is PGS 15:2025 compliant!")
        print("=" * 60)
    except AssertionError as e:
        print(f"\n❌ TEST GEFAALD: {e}")
        sys.exit(1)
    except Exception as e:
        print(f"\n❌ FOUT: {e}")
        import traceback
        traceback.print_exc()
        sys.exit(1)
