#!/usr/bin/env python3
"""Injecte les screenshots de nodes/chaînes dans particles.html.

Run-once: idempotent (skip si node-shot déjà présent).
"""
import re
import sys
from pathlib import Path

DOC = Path(__file__).parent.parent / 'particles.html'

# (titre affiché dans h3, slug du fichier PNG)
NODES = [
    # Spawn
    ('Spawn Continuous',           'spawn_continuous'),
    ('Spawn External',             'spawn_external'),
    ('Spawn From Particle',        'spawn_from_particle'),
    # Init
    ('Init Position',              'init_position'),
    ('Init Velocity',              'init_velocity'),
    ('Init Appearance',            'init_appearance'),
    # Forces
    ('Force Gravity',              'force_gravity'),
    ('Force Wind',                 'force_wind'),
    ('Force Drag',                 'force_drag'),
    ('Force Attractor',            'force_attractor'),
    ('Force Turbulence',           'force_turbulence'),
    ('Force Curl Noise',           'force_curl_noise'),
    ('Force Field Image',          'force_field_image'),
    # Behaviors
    ('Behavior Collide',           'behavior_collide'),
    ('Behavior Flock',             'behavior_flock'),
    ('Behavior Target',            'behavior_target'),
    ('Behavior Seek Image',        'behavior_seek_image'),
    ('Behavior Path Follow',       'behavior_path_follow'),
    # Modify
    ('Modify Color By Index',      'modify_color_by_index'),
    ('Modify Color From Image',    'modify_color_from_image'),
    ('Modify Rotation By Index',   'modify_rotation_by_index'),
    ('Modify Rotation Over Life',  'modify_rotation_over_life'),
    ('Modify Velocity Ramp',       'modify_velocity_ramp'),
    ('Modify From Density',        'modify_from_density'),
    ('Modify Over Life',           'modify_over_life'),
    ('Modify Position',            'modify_position'),
    ('Modify Rotation',            'modify_rotation'),
    ('Modify Scale',               'modify_scale'),
    ('Reduce Smooth',              'modify_reduce_smooth'),
    # Patterns
    ('Pattern Linear',             'pattern_linear'),
    ('Pattern Radial',             'pattern_radial'),
    ('Pattern Grid',               'pattern_grid'),
    ('Pattern Honeycomb',          'pattern_honeycomb'),
    # Clone — attention, "Clone" est trop générique, on cible via le contexte
    # Render
    ('Render Points',              'render_points'),
    ('Render Sprite',              'render_sprite'),
    ('Render Metaball',            'render_metaball'),
    ('Render Connect',             'render_connect'),
    ('Render Trail',               'render_trail'),
    ('Render Ribbon',              'render_ribbon'),
    ('Render JFA Field',           'render_jfa_field'),
    # Bridges
    ('Particle To Shapes',         'particle_to_shapes'),
    ('Particle To Trails',         'particle_to_trails'),
]

# Recettes : (titre dans h3, slug PNG)
RECIPES = [
    ('Tempête de neige',             'snowstorm'),
    ('Nébuleuse vivante',            'nebula'),
    ("Nuée d'oiseaux",               'bird_flock'),
    ("Feu d'artifice radial",        'fireworks'),
    ('Calligraphie vivante',         'calligraphy'),
    ('Lave en fusion',               'lava'),
    ("Silhouette qui s'émiette",     'silhouette'),
    ('Constellation interactive',    'constellation'),
]


def inject_node_shots(html: str) -> tuple[str, list[str]]:
    """Insère .node-shot après chaque h3 de node-card, juste avant <p class="node-hook">."""
    missing = []
    inserted = 0
    for title, slug in NODES:
        # Skip si déjà présent (idempotence)
        marker = f'src="img/nodes/{slug}.png"'
        if marker in html:
            continue
        # Pattern: <h3>(...SVG?...) TITLE</h3> followed by whitespace then <p class="node-hook">
        # On capture le h3 complet + l'indentation + le <p>
        title_re = re.escape(title)
        # Le h3 peut contenir un SVG inline (avec son contenu sur même ligne ou multi-lignes)
        pattern = re.compile(
            r'(<h3>(?:[^<]|<svg[^>]*>(?:[^<]|<(?!/svg))*</svg>|<[^>]+>)*?\s*'
            + title_re
            + r'</h3>)\s*\n(\s*)<p class="node-hook">',
            re.MULTILINE
        )
        match = pattern.search(html)
        if not match:
            missing.append(title)
            continue
        h3, indent = match.group(1), match.group(2)
        node_shot = (
            f'\n{indent}<div class="node-shot"><img src="img/nodes/{slug}.png" alt="{title}" loading="lazy"></div>'
        )
        replacement = h3 + node_shot + f'\n{indent}<p class="node-hook">'
        html = html[:match.start()] + replacement + html[match.end():]
        inserted += 1
    print(f'  Nodes: {inserted} insérés, {len(missing)} manquants: {missing}')
    return html, missing


def inject_clone_shot(html: str) -> str:
    """Le titre "Clone" est trop générique. On l'injecte via son contexte spécifique."""
    if 'src="img/nodes/clone.png"' in html:
        return html
    # On cherche le h3>Clone</h3> qui est dans .node-card.clone (donc précédé d'un h3 avec ce wrapping)
    # Pattern : <div class="node-card clone"> ... <h3>Clone</h3>
    pattern = re.compile(
        r'(<div class="node-card clone">\s*\n\s*<h3>Clone</h3>)\s*\n(\s*)<p class="node-hook">',
        re.MULTILINE
    )
    m = pattern.search(html)
    if not m:
        print('  Clone: non trouvé')
        return html
    h3, indent = m.group(1), m.group(2)
    node_shot = (
        f'\n{indent}<div class="node-shot"><img src="img/nodes/clone.png" alt="Clone" loading="lazy"></div>'
    )
    html = html[:m.start()] + h3 + node_shot + f'\n{indent}<p class="node-hook">' + html[m.end():]
    print('  Clone: inséré')
    return html


def inject_recipe_shots(html: str) -> tuple[str, list[str]]:
    """Insère une chain-image au début de chaque recette, après le <h3> titre."""
    missing = []
    inserted = 0
    for title, slug in RECIPES:
        marker = f'src="img/chains/{slug}.png"'
        if marker in html:
            continue
        # Pattern: <h3>... title ...</h3> followed by <p class="recipe-tagline">
        # Les recettes ont des emojis avant le titre (❄️, 🌌, etc.)
        title_re = re.escape(title)
        pattern = re.compile(
            r'(<h3>[^<]*?' + title_re + r'</h3>)\s*\n(\s*)<p class="recipe-tagline">',
            re.MULTILINE
        )
        m = pattern.search(html)
        if not m:
            missing.append(title)
            continue
        h3, indent = m.group(1), m.group(2)
        chain_shot = (
            f'\n{indent}<div class="recipe-shot"><img src="img/chains/{slug}.png" alt="{title} — chain layout" loading="lazy"></div>'
        )
        replacement = h3 + chain_shot + f'\n{indent}<p class="recipe-tagline">'
        html = html[:m.start()] + replacement + html[m.end():]
        inserted += 1
    print(f'  Recipes: {inserted} insérés, {len(missing)} manquants: {missing}')
    return html, missing


def main():
    html = DOC.read_text(encoding='utf-8')
    print(f'Reading {DOC} ({len(html)} chars)')
    html, missing_nodes = inject_node_shots(html)
    html = inject_clone_shot(html)
    html, missing_recipes = inject_recipe_shots(html)
    DOC.write_text(html, encoding='utf-8')
    print(f'Wrote {DOC} ({len(html)} chars)')
    if missing_nodes or missing_recipes:
        print('!!! Some matches missing — check manually')
        sys.exit(1)


if __name__ == '__main__':
    main()
