/**
 * Fichier  : assets/js/pages/products-mobile.js
 * Auteur   : KREMER Régis
 * Version  : 2.6.123
 * Date     : 2026-07-28
 * Description : Produits mobile/tablette — cartes natives, filtres en tiroir,
 *               assistant de création et modification par sections accessibles.
 *
 * Changelog :
 *   v2.6.81 (2026-07-17) — PRODUCTS MOBILE FLOW5 : création en cinq étapes,
 *                           modification par sections et cartes métier tactiles.
 *   v2.6.90 (2026-07-20) — SCANNER CONSUMERS MIGRATION3 : recherche Produit via le scanner commun.
 *   v2.6.98 (2026-07-20) — SCANNER TERRAIN CONFIRMATION1 : icône caméra SVG stable sur l’accès Produits.
 *   v2.6.122 (2026-07-28) — PRODUCTS MOBILE DOCUMENT ACTIONS CREATION FLOW FIX1 : actions FT/FDS et assistant mobile compatible clavier.
 *   v2.6.123 (2026-07-28) — PRODUCTS MOBILE DOCUMENT OPENING KEYBOARD FLOW FIX2 : ouverture FT/FS réellement branchée et assistant mobile compact piloté par le viewport visuel.
 */
(function productsMobileFlow(global, document) {
    'use strict';

    if (!global || !document || global.MageStockProductsMobile) return;
    if (typeof ProdPage === 'undefined') return;

    const Page = ProdPage;
    const VERSION = '2.6.123';
    const CREATE_DRAFT_KEY = 'magestock.products.create.v1';
    const MOBILE_BATCH = 36;
    const state = {
        initialized: false,
        patched: false,
        renderCount: MOBILE_BATCH,
        signature: '',
        filterBound: false,
        cardBound: false,
        actionBarBound: false,
        draftTimer: null,
        activeController: null,
        activeMode: '',
        activeProductId: null,
        scannerConsumer: null,
        keyboardCleanup: null
    };

    const UI = () => global.MageStockMobileUI || null;
    const pageRoot = () => document.getElementById('pgProducts');
    const isAdaptive = () => {
        const profile = UI()?.device?.profile;
        return profile ? profile !== 'desktop' : (global.innerWidth || 0) < 1200;
    };
    const esc = (value) => String(value == null ? '' : value)
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;')
        .replace(/'/g, '&#039;');

    const tr = (key, fallback) => typeof global.t === 'function' ? global.t(key, fallback) : fallback;

    function normalizeDocumentUrl(value) {
        const raw = String(value || '').trim();
        if (!raw) return '';
        try {
            const parsed = new URL(raw, global.location.href);
            if (!['http:', 'https:'].includes(parsed.protocol)) return '';
            return parsed.href;
        } catch (_) { return ''; }
    }

    function documentAction(product, field, action, shortLabel, longLabel) {
        const url = normalizeDocumentUrl(product?.[field]);
        const unavailable = tr('products.mobile.document_unavailable', 'Document non disponible');
        const title = url ? longLabel : `${longLabel} — ${unavailable}`;
        return `<button type="button" class="btn btn-ghost products-mobile-card__document" data-product-action="${action}" aria-label="${esc(title)}" title="${esc(title)}" ${url ? '' : 'disabled aria-disabled="true"'}>${esc(shortLabel)}</button>`;
    }

    function openProductDocument(product, field, label) {
        const url = normalizeDocumentUrl(product?.[field]);
        if (!url) {
            global.Toast?.show?.(tr('products.mobile.document_unavailable', 'Document non disponible'), 'warning');
            announce(`${label} : ${tr('products.mobile.document_unavailable', 'document non disponible')}.`, 'assertive');
            return false;
        }

        // Une ancre native déclenchée pendant le geste utilisateur reste fiable dans
        // Chrome mobile, les navigateurs intégrés et une PWA installée. L’ancienne
        // utilisation de window.open(..., "noopener,noreferrer") pouvait renvoyer null
        // ou être neutralisée sans que l’interface puisse distinguer un blocage.
        const link = document.createElement('a');
        link.href = url;
        link.target = '_blank';
        link.rel = 'noopener noreferrer external';
        link.referrerPolicy = 'no-referrer';
        link.className = 'ms-sr-only';
        link.setAttribute('aria-hidden', 'true');
        document.body.appendChild(link);
        try {
            link.click();
            announce(`${label} : ${tr('products.mobile.document_opening', 'ouverture du document')}.`);
            return true;
        } catch (_) {
            // Repli garanti : le document remplace l’onglet courant et le bouton Retour
            // du navigateur permet de revenir à MageStock.
            global.location.assign(url);
            return true;
        } finally {
            link.remove();
        }
    }

    function announce(message, priority) {
        if (UI()?.announce) UI().announce(message, priority || 'polite');
    }

    function number(value) {
        const parsed = Number.parseFloat(value);
        return Number.isFinite(parsed) ? parsed : 0;
    }

    function formatQuantity(value) {
        const qty = number(value);
        return Number.isInteger(qty) ? String(qty) : qty.toLocaleString('fr-FR', { maximumFractionDigits: 2 });
    }

    function selectedText(id, fallback) {
        const select = document.getElementById(id);
        if (!select || !select.value) return fallback || 'Non renseigné';
        return select.options?.[select.selectedIndex]?.textContent?.trim() || select.value;
    }

    function getProductStock(product, warehouseId) {
        const rows = (Page?.stock || []).filter((row) => String(row.productId) === String(product.id));
        const scoped = warehouseId ? rows.filter((row) => String(row.warehouseId) === String(warehouseId)) : rows;
        const quantity = scoped.reduce((sum, row) => sum + number(row.quantity), 0);
        const minCandidates = scoped.map((row) => Number.parseFloat(row.minStock)).filter(Number.isFinite);
        const minStock = minCandidates.length ? Math.min(...minCandidates) : null;
        let status = 'success';
        let label = 'Disponible';
        if (quantity <= 0) {
            status = 'danger';
            label = 'Rupture';
        } else if (minStock != null && quantity <= minStock) {
            status = 'danger';
            label = 'Critique';
        } else if (minStock != null && quantity <= minStock * 1.5) {
            status = 'warning';
            label = 'Stock bas';
        }
        return { rows, scoped, quantity, minStock, status, label };
    }

    function getWarehouseLabel(product, warehouseId, stockInfo) {
        const warehouses = Page?.whs || [];
        if (warehouseId) {
            return warehouses.find((warehouse) => String(warehouse.id) === String(warehouseId))?.name || 'Magasin';
        }
        const ids = new Set();
        (stockInfo?.rows || []).forEach((row) => ids.add(String(row.warehouseId)));
        if (!ids.size) {
            const allowed = Page?._getProductWarehouses?.(product) || [];
            allowed.forEach((id) => ids.add(String(id)));
        }
        const names = [...ids].map((id) => warehouses.find((warehouse) => String(warehouse.id) === id)?.name).filter(Boolean);
        if (!names.length) return 'Aucun magasin';
        if (names.length <= 2) return names.join(' · ');
        return names.slice(0, 2).join(' · ') + ' +' + (names.length - 2);
    }

    function getLocationLabel(warehouseId, stockInfo) {
        if (!warehouseId) return '';
        const row = (stockInfo?.scoped || [])[0];
        if (!row) return '';
        const locationId = row.locationId != null ? String(row.locationId) : '';
        const registered = locationId && Page?._locationsById?.[locationId];
        if (registered) return [registered.code, registered.label].filter(Boolean).join(' · ');
        return row.locationLabel || [row.picking, row.location, row.rack, row.shelf].filter(Boolean).join(' · ');
    }

    function ensureMobileContainers() {
        const root = pageRoot();
        if (!root) return null;
        let list = document.getElementById('prodMobileCards');
        if (!list) {
            list = document.createElement('section');
            list.id = 'prodMobileCards';
            list.className = 'products-mobile-cards ms-entity-list';
            list.setAttribute('aria-label', 'Catalogue des produits');
            list.setAttribute('aria-live', 'polite');
            const grid = document.getElementById('prodGrid');
            grid?.parentNode?.insertBefore(list, grid);
        }
        let more = document.getElementById('prodMobileMore');
        if (!more) {
            more = document.createElement('div');
            more.id = 'prodMobileMore';
            more.className = 'products-mobile-more';
            list.insertAdjacentElement('afterend', more);
        }
        return { root, list, more };
    }

    function buildCard(product, warehouseId) {
        const stockInfo = getProductStock(product, warehouseId);
        const baseUnit = Page?._getBaseUnit?.(product) || product.unitType || 'piece';
        const unitLabel = baseUnit === 'piece' ? 'u' : baseUnit;
        const image = Page?._productImageSrc?.(product) || '';
        const warehouse = getWarehouseLabel(product, warehouseId, stockInfo);
        const location = getLocationLabel(warehouseId, stockInfo);
        const cost = typeof global.getBaseCost === 'function' ? global.getBaseCost(product) : number(product.cump || product.cost);
        const isAdmin = global.SM_ROLE === 'admin';
        const variant = Page?._getVariantAttributeText?.(product) || product.variantLabel || product.size || '';

        const article = document.createElement('article');
        article.className = 'ms-entity-card products-mobile-card';
        article.dataset.state = stockInfo.status;
        article.dataset.productId = String(product.id);
        article.innerHTML = `
            <header class="products-mobile-card__head">
                <button type="button" class="products-mobile-card__media" data-product-action="${image ? 'zoom' : 'open'}" aria-label="${image ? 'Agrandir l’image de ' : 'Ouvrir '}${esc(product.name || 'Produit')}">
                    ${image ? `<img src="${esc(image)}" alt="" loading="lazy" onerror="this.parentElement.classList.add('is-empty');this.remove()">` : '<span aria-hidden="true">📦</span>'}
                </button>
                <div class="products-mobile-card__identity">
                    <span class="ms-entity-card__eyebrow">${esc(product.sku || 'Sans SKU')}</span>
                    <h3 class="ms-entity-card__title">${esc(product.name || 'Produit sans nom')}</h3>
                    ${variant ? `<p class="products-mobile-card__variant">${esc(variant)}</p>` : ''}
                    <div class="products-mobile-card__chips">
                        ${product.category ? `<span>${esc(product.category)}</span>` : ''}
                        ${product.brand ? `<span>${esc(product.brand)}</span>` : ''}
                    </div>
                </div>
                <span class="products-mobile-card__status products-mobile-card__status--${stockInfo.status}">${stockInfo.label}</span>
            </header>
            <dl class="ms-entity-card__fields products-mobile-card__fields">
                <div class="ms-entity-card__field"><dt>Stock</dt><dd>${formatQuantity(stockInfo.quantity)} ${esc(unitLabel)}</dd></div>
                <div class="ms-entity-card__field"><dt>Prix</dt><dd>${cost > 0 && global.Locale ? global.Locale.formatMoney(cost) : '—'}</dd></div>
                <div class="ms-entity-card__field products-mobile-card__field--wide"><dt>Magasin</dt><dd>${esc(warehouse)}</dd></div>
                ${location ? `<div class="ms-entity-card__field products-mobile-card__field--wide"><dt>Emplacement</dt><dd>${esc(location)}</dd></div>` : ''}
            </dl>
            <footer class="ms-entity-card__actions products-mobile-card__actions">
                <div class="products-mobile-card__primary-actions">
                    <button type="button" class="btn btn-primary" data-product-action="open">${isAdmin ? 'Modifier' : 'Consulter'}</button>
                    ${isAdmin ? '<button type="button" class="btn btn-secondary" data-product-action="movement">Mouvement</button>' : ''}
                </div>
                <div class="products-mobile-card__resource-actions" aria-label="${esc(tr('products.mobile.resources', 'Documents et localisation'))}">
                    ${documentAction(product, 'datasheetUrl', 'datasheet', 'FT', tr('products.mobile.datasheet', 'Fiche technique'))}
                    <button type="button" class="btn btn-ghost" data-product-action="plan" aria-label="${esc(tr('products.mobile.plan', 'Voir le produit sur un plan'))}">${esc(tr('products.mobile.plan_short', 'Plan'))}</button>
                    ${documentAction(product, 'msdsUrl', 'safety-sheet', 'FS', tr('products.mobile.safety_sheet', 'Fiche de sécurité'))}
                </div>
            </footer>`;
        return article;
    }

    function renderMobileCards(forceReset) {
        const containers = ensureMobileContainers();
        if (!containers || !Page) return;
        const shown = Array.isArray(Page._shown) ? Page._shown : [];
        const signature = shown.map((product) => product.id).join(',') + '|' + (Page._currentWhFilter || '');
        if (forceReset || signature !== state.signature) {
            state.signature = signature;
            state.renderCount = MOBILE_BATCH;
        }

        const products = shown.slice(0, state.renderCount);
        containers.list.innerHTML = '';
        if (!products.length) {
            containers.list.innerHTML = '<div class="products-mobile-empty"><strong>Aucun produit trouvé</strong><span>Modifiez la recherche ou les filtres.</span></div>';
            containers.more.innerHTML = '';
        } else {
            const fragment = document.createDocumentFragment();
            products.forEach((product) => fragment.appendChild(buildCard(product, Page._currentWhFilter || '')));
            containers.list.appendChild(fragment);
            const remaining = Math.max(0, shown.length - products.length);
            containers.more.innerHTML = remaining
                ? `<button type="button" class="btn btn-secondary" id="prodMobileLoadMore">Afficher ${Math.min(MOBILE_BATCH, remaining)} produit(s) de plus</button><span>${products.length} sur ${shown.length}</span>`
                : `<span>${shown.length} produit(s) affiché(s)</span>`;
            document.getElementById('prodMobileLoadMore')?.addEventListener('click', () => {
                state.renderCount += MOBILE_BATCH;
                renderMobileCards(false);
                announce(`${Math.min(state.renderCount, shown.length)} produits affichés sur ${shown.length}.`);
            });
        }
        toggleResponsiveContainers(true);
        bindCardActions();
    }

    function toggleResponsiveContainers(mobile) {
        const grid = document.getElementById('prodGrid');
        const table = document.getElementById('prodTableWrap');
        const pager = document.getElementById('prodPager');
        const cards = document.getElementById('prodMobileCards');
        const more = document.getElementById('prodMobileMore');
        const infiniteLoader = grid?.parentElement?.querySelector('.infinite-scroll-loader, .infinite-scroll-end');
        if (mobile) {
            if (grid) grid.hidden = true;
            if (table) table.hidden = true;
            if (pager) pager.hidden = true;
            if (cards) cards.hidden = false;
            if (more) more.hidden = false;
            if (infiniteLoader) infiniteLoader.hidden = true;
        } else {
            if (cards) cards.hidden = true;
            if (more) more.hidden = true;
            if (pager) pager.hidden = false;
            if (grid) grid.hidden = false;
            if (table) table.hidden = false;
            if (infiniteLoader) infiniteLoader.hidden = false;
        }
    }

    function bindCardActions() {
        const list = document.getElementById('prodMobileCards');
        if (!list || list.dataset.productsMobileBound === '1') return;
        list.dataset.productsMobileBound = '1';
        list.addEventListener('click', (event) => {
            const action = event.target.closest('[data-product-action]');
            const card = event.target.closest('[data-product-id]');
            if (!action || !card) return;
            const productId = card.dataset.productId;
            const product = (Page?._shown || []).find((item) => String(item.id) === String(productId));
            event.preventDefault();
            event.stopPropagation();
            switch (action.dataset.productAction) {
                case 'open': Page?.openModal?.(productId); break;
                case 'movement': Page?.quickAction?.(productId); break;
                case 'plan': Page?.openProductPlan?.(productId, event); break;
                case 'datasheet':
                    openProductDocument(product, 'datasheetUrl', tr('products.mobile.datasheet', 'Fiche technique'));
                    break;
                case 'safety-sheet':
                    openProductDocument(product, 'msdsUrl', tr('products.mobile.safety_sheet', 'Fiche de sécurité'));
                    break;
                case 'zoom': {
                    const src = Page?._productImageSrc?.(product) || '';
                    if (src) Page?._openZoom?.(src, product?.name || 'Image produit');
                    break;
                }
            }
        });
    }

    function openFilterDrawer(trigger) {
        if (!isAdaptive()) return;
        const wh = document.getElementById('prodWhFilter');
        const cat = document.getElementById('prodCatFilter');
        const brand = document.getElementById('prodBrandFilter');
        if (!wh || !cat || !brand || !UI()?.drawer) return;
        const content = document.createElement('form');
        content.className = 'products-mobile-filter-form';
        content.innerHTML = `
            <label for="pmfWarehouse">Magasin</label>
            <select class="form-select" id="pmfWarehouse">${wh.innerHTML}</select>
            <label for="pmfCategory">Catégorie</label>
            <select class="form-select" id="pmfCategory">${cat.innerHTML}</select>
            <label for="pmfBrand">Marque</label>
            <select class="form-select" id="pmfBrand">${brand.innerHTML}</select>`;
        content.querySelector('#pmfWarehouse').value = wh.value;
        content.querySelector('#pmfCategory').value = cat.value;
        content.querySelector('#pmfBrand').value = brand.value;
        content.querySelector('#pmfWarehouse').addEventListener('change', () => {
            // Les catégories et marques dépendent du magasin. On évite de conserver
            // un filtre devenu incohérent avant le rechargement serveur du catalogue.
            content.querySelector('#pmfCategory').value = '';
            content.querySelector('#pmfBrand').value = '';
        });
        UI().drawer.open({
            title: 'Filtrer les produits',
            description: 'Affinez le catalogue sans perdre votre position dans la page.',
            content,
            trigger,
            actions: [
                {
                    label: 'Réinitialiser',
                    className: 'btn btn-ghost',
                    close: false,
                    onClick: ({ body }) => {
                        body.querySelector('#pmfWarehouse').value = '';
                        body.querySelector('#pmfCategory').value = '';
                        body.querySelector('#pmfBrand').value = '';
                    }
                },
                {
                    label: 'Appliquer',
                    className: 'btn btn-primary',
                    onClick: ({ body }) => {
                        wh.value = body.querySelector('#pmfWarehouse').value;
                        cat.value = body.querySelector('#pmfCategory').value;
                        brand.value = body.querySelector('#pmfBrand').value;
                        state.signature = '';
                        Page.load();
                    }
                }
            ]
        });
    }

    function bindFilters() {
        const button = document.getElementById('prodFilterToggle');
        if (!button || button.dataset.productsMobileFilterBound === '1') return;
        button.dataset.productsMobileFilterBound = '1';
        button.addEventListener('click', (event) => {
            if (!isAdaptive()) return;
            event.preventDefault();
            event.stopPropagation();
            event.stopImmediatePropagation();
            openFilterDrawer(button);
        }, true);
    }

    function openProductScanner(trigger) {
        if (!global.MageStockScannerConsumer?.open) {
            announce('Le scanner commun est indisponible.', 'assertive');
            global.toast?.('Scanner indisponible.', 'error');
            return null;
        }
        if (state.scannerConsumer && !state.scannerConsumer.closed) return state.scannerConsumer;
        state.scannerConsumer = global.MageStockScannerConsumer.open({
            title: 'Rechercher un produit',
            description: 'Scannez un code-barres, un EAN, un SKU ou un QR MageStock.',
            context: 'product-search',
            owner: 'products-search',
            mode: 'auto',
            trigger,
            resolverOptions: () => ({ products: Page?._allProds || Page?._shown || [] }),
            onResolved: async (result, handle) => {
                await handle.stop('product-search-resolved');
                state.scannerConsumer = null;
                handle.close('resolved');
                const product = result?.product;
                if (!product) return false;
                const search = document.getElementById('prodSearch');
                if (search) search.value = product.sku || product.barcode || product.name || '';
                announce(`${product.name || 'Produit'} trouvé.`);
                global.setTimeout(() => Page.openModal?.(product.id), 160);
                return false;
            },
            onNotFound: result => {
                global.toast?.(`Code non reconnu${result?.raw ? ` : ${result.raw}` : ''}`, 'error');
            },
            onClose: () => { state.scannerConsumer = null; },
            onError: error => console.error('[Products scanner]', error)
        });
        return state.scannerConsumer;
    }

    function ensureProductScanButton() {
        const row = pageRoot()?.querySelector('.prod-search-row');
        if (!row || document.getElementById('prodScannerSearch')) return;
        const button = document.createElement('button');
        button.type = 'button';
        button.id = 'prodScannerSearch';
        button.className = 'btn btn-secondary btn-sm products-scanner-search';
        button.innerHTML = '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path><circle cx="12" cy="13" r="4"></circle></svg><span>Scanner</span>';
        button.addEventListener('click', event => openProductScanner(event.currentTarget));
        row.appendChild(button);
    }

    function createMobileActionBar() {
        const root = pageRoot();
        const filters = root?.querySelector('.prod-filters-bar');
        if (!root || !filters || document.getElementById('prodMobileActions')) return;
        const isAdmin = global.SM_ROLE === 'admin';
        const bar = document.createElement('div');
        bar.id = 'prodMobileActions';
        bar.className = 'products-mobile-actions';
        bar.innerHTML = isAdmin
            ? '<button type="button" class="btn btn-primary" data-product-mobile-action="new">Nouveau produit</button><button type="button" class="btn btn-secondary" data-product-mobile-action="scan">Scanner</button><button type="button" class="btn btn-secondary" data-product-mobile-action="stock">Entrée de stock</button><button type="button" class="btn btn-ghost" data-product-mobile-action="more" aria-label="Autres actions">Plus</button>'
            : '<button type="button" class="btn btn-primary" data-product-mobile-action="scan">Scanner un produit</button><span class="products-mobile-actions__hint">Touchez un produit pour consulter sa fiche.</span>';
        filters.insertAdjacentElement('afterend', bar);
        bar.addEventListener('click', (event) => {
            const target = event.target.closest('[data-product-mobile-action]');
            if (!target) return;
            const action = target.dataset.productMobileAction;
            if (action === 'new') Page.openModal();
            if (action === 'scan') openProductScanner(target);
            if (action === 'stock') global.openMovementModal?.('entry');
            if (action === 'more') openProductActions(target);
        });
    }

    function openProductActions(trigger) {
        if (!UI()?.sheet) return;
        const content = document.createElement('div');
        content.className = 'products-mobile-action-list';
        content.innerHTML = `
            <button type="button" class="products-mobile-action-row" data-action="import"><strong>Importer un fichier CSV</strong><span>Ajouter ou mettre à jour plusieurs produits.</span></button>
            <button type="button" class="products-mobile-action-row" data-action="desktop-list"><strong>Vue de gestion avancée</strong><span>La sélection multiple reste disponible sur ordinateur.</span></button>`;
        const handle = UI().sheet.open({ title: 'Actions Produits', content, trigger });
        content.addEventListener('click', (event) => {
            const button = event.target.closest('[data-action]');
            if (!button) return;
            if (button.dataset.action === 'import') {
                handle.close('action');
                Page.openImportModal();
            } else {
                announce('La sélection multiple est conservée dans la vue ordinateur.');
            }
        });
    }

    function getSection(titleId) {
        return document.getElementById(titleId)?.closest('section') || null;
    }

    function moveNodes(target, nodes) {
        nodes.filter(Boolean).forEach((node) => target.appendChild(node));
    }

    function createStep(numberValue, title, description) {
        const section = document.createElement('section');
        section.className = 'ms-flow__panel products-create-step';
        section.dataset.msFlowStep = String(numberValue);
        section.innerHTML = `<div class="products-create-step__intro"><span>Étape ${numberValue} sur 5</span><h3 data-ms-flow-title>${esc(title)}</h3><p>${esc(description)}</p></div>`;
        return section;
    }

    function createOptionalGroup(title, description, nodes, open) {
        const details = document.createElement('details');
        details.className = 'products-create-optional';
        if (open) details.open = true;
        details.innerHTML = `<summary><span><strong>${esc(title)}</strong><small>${esc(description || '')}</small></span><span aria-hidden="true">⌄</span></summary><div class="products-create-optional__body"></div>`;
        moveNodes(details.querySelector('.products-create-optional__body'), nodes);
        return details;
    }

    function compactIdentityPanel(identity) {
        if (!identity) return;
        const start = identity.querySelector('label[for="pmEanInput"]');
        const end = identity.querySelector('#pmDesc');
        if (!start || !end) return;
        const nodes = [];
        let current = start;
        while (current) {
            const next = current.nextSibling;
            nodes.push(current);
            if (current === end) break;
            current = next;
        }
        const group = createOptionalGroup(
            tr('products.mobile.create.identity_more', 'Informations complémentaires'),
            tr('products.mobile.create.identity_more_help', 'EAN supplémentaires et description.'),
            nodes,
            false
        );
        identity.appendChild(group);
    }

    function revealControlInCreateBody(control) {
        const body = document.getElementById('modalBody');
        if (!body || !control || !body.contains(control)) return;
        global.requestAnimationFrame(() => {
            const bodyRect = body.getBoundingClientRect();
            const rect = control.getBoundingClientRect();
            const topLimit = bodyRect.top + 12;
            const bottomLimit = bodyRect.bottom - 14;
            if (rect.top < topLimit) body.scrollTop += rect.top - topLimit;
            else if (rect.bottom > bottomLimit) body.scrollTop += rect.bottom - bottomLimit;
        });
    }

    function configureCreateControlHints(step) {
        if (!step) return;
        const controls = Array.from(step.querySelectorAll('input:not([type="hidden"]):not([type="file"]):not([disabled]), textarea:not([disabled]), select:not([disabled])'))
            .filter((control) => control.offsetParent !== null);
        controls.forEach((control, index) => {
            if (control.tagName === 'TEXTAREA' || control.tagName === 'SELECT') return;
            control.setAttribute('enterkeyhint', index === controls.length - 1 ? 'done' : 'next');
        });
    }

    function validateField(id, message) {
        const field = document.getElementById(id);
        if (!field) return true;
        const value = field.type === 'checkbox' ? field.checked : String(field.value || '').trim();
        if (value) return true;
        field.closest('details')?.setAttribute('open', '');
        field.setCustomValidity(message);
        field.reportValidity?.();
        field.focus?.({ preventScroll: true });
        global.setTimeout(() => revealControlInCreateBody(field), 30);
        global.setTimeout(() => field.setCustomValidity(''), 50);
        announce(message, 'assertive');
        return false;
    }

    function validateCreateStep(stepIndex) {
        if (stepIndex === 0) return validateField('pmName', 'Renseignez le nom du produit.');
        if (stepIndex === 1) return validateField('pmWarehouse', 'Choisissez le magasin du produit.');
        if (stepIndex === 2) {
            const cost = document.getElementById('pmCost');
            if (cost && number(cost.value) < 0) {
                cost.focus();
                announce('Le prix d’achat ne peut pas être négatif.', 'assertive');
                return false;
            }
        }
        if (stepIndex === 3) {
            const initQty = number(document.getElementById('pmInitQty')?.value);
            if (initQty > 0 && document.getElementById('pmTrackLot')?.checked && !String(document.getElementById('pmInitLot')?.value || '').trim()) {
                return validateField('pmInitLot', 'Renseignez le lot du stock initial.');
            }
            if (initQty > 0 && document.getElementById('pmTrackExp')?.checked && !String(document.getElementById('pmInitExp')?.value || '').trim()) {
                return validateField('pmInitExp', 'Renseignez la date de péremption du stock initial.');
            }
        }
        return true;
    }

    function createPurchasePanel(supplierHost) {
        const panel = document.createElement('section');
        panel.className = 'pm-panel products-purchase-panel';
        panel.setAttribute('aria-labelledby', 'pmPurchaseOffersTitle');
        panel.innerHTML = `
            <div class="products-panel-title-row">
                <div><h4 id="pmPurchaseOffersTitle">🏷️ Offres fournisseurs</h4><p>Optionnel : prépare les références, prix et conditionnements utilisés dans les commandes.</p></div>
                <button type="button" class="btn btn-secondary" id="pmAddSupplierOffer">Ajouter une offre</button>
            </div>`;
        if (supplierHost) {
            supplierHost.style.display = 'block';
            supplierHost.classList.add('products-supplier-offers');
            panel.appendChild(supplierHost);
        }
        panel.querySelector('#pmAddSupplierOffer')?.addEventListener('click', () => Page?._appendEmptySupplierLink?.());
        return panel;
    }

    function collectReview() {
        const productName = String(document.getElementById('pmName')?.value || '').trim() || 'Produit sans nom';
        const sku = String(document.getElementById('pmSKU')?.value || '').trim() || 'Généré automatiquement';
        const warehouse = selectedText('pmWarehouse');
        const category = selectedText('pmCat', 'Aucune catégorie');
        const location = selectedText('pmLocationId', '') || [
            document.getElementById('pmPicking')?.value,
            document.getElementById('pmLocation')?.value,
            document.getElementById('pmRack')?.value,
            document.getElementById('pmShelfLoc')?.value
        ].filter(Boolean).join(' · ') || 'Non renseigné';
        const supplier = selectedText('pmSupp', 'Aucun fournisseur');
        const brandSelect = document.getElementById('pmBrand');
        const brand = brandSelect?.value === '__other__' ? document.getElementById('pmBrandOther')?.value : brandSelect?.value;
        const baseUnit = selectedText('pmBaseUnit', 'pièce');
        const cost = number(document.getElementById('pmCost')?.value);
        const initQty = number(document.getElementById('pmInitQty')?.value);
        const trace = [];
        if (document.getElementById('pmTrackLot')?.checked) trace.push('lots');
        if (document.getElementById('pmTrackExp')?.checked) trace.push('péremption');
        const variants = [];
        if (document.getElementById('pmIsVariantParent')?.checked) variants.push('parent de déclinaisons');
        const sizeType = selectedText('pmSizeType', 'Aucune taille');
        if (document.getElementById('pmSizeType')?.value && document.getElementById('pmSizeType').value !== 'none') variants.push(sizeType);
        const eans = Page?._sanitizeEanList?.(document.getElementById('pmEansHidden')?.value || '[]') || [];
        const image = document.querySelector('#pmImgPreview img')?.src || '';
        return { productName, sku, warehouse, category, location, supplier, brand: brand || 'Aucune marque', baseUnit, cost, initQty, trace, variants, eans, image };
    }

    function updateReview() {
        const host = document.getElementById('productsCreateReview');
        if (!host) return;
        const review = collectReview();
        host.innerHTML = `
            <article class="products-review-card">
                <header>
                    <div class="products-review-card__image">${review.image ? `<img src="${esc(review.image)}" alt="">` : '<span aria-hidden="true">📦</span>'}</div>
                    <div><span>Produit à créer</span><h4>${esc(review.productName)}</h4><p>${esc(review.sku)}</p></div>
                </header>
                <dl>
                    <div><dt>Magasin</dt><dd>${esc(review.warehouse)}</dd></div>
                    <div><dt>Catégorie</dt><dd>${esc(review.category)}</dd></div>
                    <div><dt>Emplacement</dt><dd>${esc(review.location)}</dd></div>
                    <div><dt>Fournisseur</dt><dd>${esc(review.supplier)}</dd></div>
                    <div><dt>Marque</dt><dd>${esc(review.brand)}</dd></div>
                    <div><dt>Unité de base</dt><dd>${esc(review.baseUnit)}</dd></div>
                    <div><dt>Prix d’achat</dt><dd>${review.cost > 0 && global.Locale ? global.Locale.formatMoney(review.cost) : 'Non renseigné'}</dd></div>
                    <div><dt>Stock initial</dt><dd>${review.initQty > 0 ? formatQuantity(review.initQty) + ' ' + esc(review.baseUnit) : 'Aucun'}</dd></div>
                    <div><dt>Traçabilité</dt><dd>${review.trace.length ? esc(review.trace.join(' et ')) : 'Aucune'}</dd></div>
                    <div><dt>Variantes</dt><dd>${review.variants.length ? esc(review.variants.join(' · ')) : 'Produit standard'}</dd></div>
                    <div><dt>Codes supplémentaires</dt><dd>${review.eans.length ? review.eans.length + ' EAN' : 'Aucun'}</dd></div>
                </dl>
            </article>
            <p class="products-review-note">Le stock ne sera créé que si une quantité initiale supérieure à zéro est renseignée. Toutes les autres entrées resteront gérées par les mouvements.</p>`;
    }

    function serializeDraft(step) {
        const values = {};
        document.querySelectorAll('#modalBody [id^="pm"] input, #modalBody [id^="pm"] select, #modalBody [id^="pm"] textarea').forEach((control) => {
            if (!control.id || control.type === 'file' || control.type === 'button' || control.type === 'submit') return;
            if (control.type === 'radio' && !control.checked) return;
            if (control.type === 'checkbox') values[control.id] = !!control.checked;
            else if (control.multiple) values[control.id] = Array.from(control.selectedOptions).map((option) => option.value);
            else values[control.id] = control.value;
        });
        return {
            version: 1,
            step: Number(step || 1),
            values,
            supplierLinks: Page?._collectSupplierLinks?.() || [],
            imageUrl: Page?._imgUploadUrl || '',
            savedAt: new Date().toISOString()
        };
    }

    function saveDraft(controller) {
        if (state.activeMode !== 'create') return;
        global.clearTimeout(state.draftTimer);
        state.draftTimer = global.setTimeout(() => {
            try {
                sessionStorage.setItem(CREATE_DRAFT_KEY, JSON.stringify(serializeDraft((controller?.current || 0) + 1)));
            } catch (_) {}
        }, 180);
    }

    function readDraft() {
        try {
            const raw = sessionStorage.getItem(CREATE_DRAFT_KEY);
            return raw ? JSON.parse(raw) : null;
        } catch (_) { return null; }
    }

    function clearDraft() {
        try { sessionStorage.removeItem(CREATE_DRAFT_KEY); } catch (_) {}
    }

    async function applyDraft(draft) {
        if (!draft?.values) return false;
        const values = draft.values;
        const warehouseValue = values.pmWarehouse;
        const categoryValue = values.pmCat;
        const locationValue = values.pmLocationId;
        const supplierValue = values.pmSupp;
        const brandValue = values.pmBrand;
        const brandOtherValue = values.pmBrandOther;
        const baseUnitValue = values.pmBaseUnit;
        const sizeTypeValue = values.pmSizeType;
        const sizeNormValue = values.pmSizeNorm;

        Object.entries(values).forEach(([id, value]) => {
            if (['pmWarehouse', 'pmCat', 'pmLocationId', 'pmSupp', 'pmBrand', 'pmBrandOther', 'pmBaseUnit', 'pmSizeType', 'pmSizeNorm'].includes(id)) return;
            const control = document.getElementById(id);
            if (!control) return;
            if (control.type === 'checkbox') control.checked = !!value;
            else if (control.multiple && Array.isArray(value)) Array.from(control.options).forEach((option) => { option.selected = value.map(String).includes(String(option.value)); });
            else control.value = value == null ? '' : String(value);
            control.dispatchEvent(new Event('input', { bubbles: true }));
        });

        if (warehouseValue) {
            const warehouse = document.getElementById('pmWarehouse');
            warehouse.value = String(warehouseValue);
            await Promise.resolve(warehouse.onchange?.());
            if (categoryValue != null) document.getElementById('pmCat').value = String(categoryValue);
            if (locationValue != null) document.getElementById('pmLocationId').value = String(locationValue);
        }
        if (supplierValue != null) {
            const supplier = document.getElementById('pmSupp');
            supplier.value = String(supplierValue);
            await Promise.resolve(supplier.onchange?.());
            const brand = document.getElementById('pmBrand');
            if (brand && brandValue != null) {
                brand.value = String(brandValue);
                brand.dispatchEvent(new Event('change', { bubbles: true }));
            }
            const other = document.getElementById('pmBrandOther');
            if (other && brandOtherValue != null) other.value = String(brandOtherValue);
        }
        if (baseUnitValue != null) {
            const baseUnit = document.getElementById('pmBaseUnit');
            baseUnit.value = String(baseUnitValue);
            baseUnit.onchange?.();
            if (values.pmCondInput != null) {
                const input = document.getElementById('pmCondInput');
                if (input) { input.value = values.pmCondInput; input.dispatchEvent(new Event('input', { bubbles: true })); }
            }
        }
        if (sizeTypeValue != null) {
            const size = document.getElementById('pmSizeType');
            size.value = String(sizeTypeValue);
            size.onchange?.();
        }
        if (sizeNormValue != null) {
            const norm = document.getElementById('pmSizeNorm');
            norm.value = String(sizeNormValue);
            norm.onchange?.();
        }
        if (Array.isArray(draft.supplierLinks) && draft.supplierLinks.length) {
            Page._renderSupplierLinksEditor({ supplierLinks: draft.supplierLinks, supplier: supplierValue || '' });
        }
        if (draft.imageUrl) {
            Page._imgUploadUrl = draft.imageUrl;
            const url = document.getElementById('pmImgUrl');
            if (url) url.value = draft.imageUrl;
        }
        global._pmEanRefresh?.();
        document.getElementById('pmTrackLot')?.dispatchEvent(new Event('change', { bubbles: true }));
        document.getElementById('pmTrackExp')?.dispatchEvent(new Event('change', { bubbles: true }));
        return true;
    }

    function updateCreateHeader(controller) {
        const labels = [
            tr('products.mobile.create.step_identity', 'Identité'),
            tr('products.mobile.create.step_classification', 'Classement'),
            tr('products.mobile.create.step_purchase', 'Achats'),
            tr('products.mobile.create.step_traceability', 'Traçabilité'),
            tr('products.mobile.create.step_review', 'Vérification')
        ];
        const label = document.querySelector('[data-products-flow-step-label]');
        if (label) label.textContent = labels[controller.current] || labels[0];
        configureCreateControlHints(controller.steps[controller.current]);
    }

    function updateCreateFooter(controller) {
        const footer = document.getElementById('modalFooter');
        if (!footer) return;
        const first = controller.current === 0;
        const last = controller.current === controller.steps.length - 1;
        footer.classList.toggle('is-first-step', first);
        footer.classList.toggle('is-last-step', last);
        footer.querySelector('[data-products-flow-back]').hidden = first;
        footer.querySelector('[data-products-flow-next]').hidden = last;
        footer.querySelector('[data-products-flow-create]').hidden = !last;
        if (last) updateReview();
        updateCreateHeader(controller);
        UI()?.sticky?.refresh?.();
    }

    function configureCreateFooter(controller) {
        const footer = document.getElementById('modalFooter');
        if (!footer) return;
        footer.className = 'modal-footer products-flow-footer';
        footer.setAttribute('data-ms-sticky-actions', '');
        footer.innerHTML = `
            <button type="button" class="btn btn-secondary" data-products-flow-back>Retour</button>
            <button type="button" class="btn btn-primary" data-products-flow-next>Suivant</button>
            <button type="button" class="btn btn-primary" data-products-flow-create>Créer le produit</button>`;
        footer.querySelector('[data-products-flow-back]').addEventListener('click', async () => {
            document.getElementById('modalBox')?.dispatchEvent(new CustomEvent('products:flow-before-change'));
            await controller.goTo(controller.current - 1, { validate: false, focus: false });
        });
        footer.querySelector('[data-products-flow-next]').addEventListener('click', async () => {
            document.getElementById('modalBox')?.dispatchEvent(new CustomEvent('products:flow-before-change'));
            await controller.goTo(controller.current + 1, { validate: true, focus: false });
        });
        footer.querySelector('[data-products-flow-create]').addEventListener('click', async (event) => {
            if (!validateCreateStep(3)) return;
            const button = event.currentTarget;
            button.disabled = true;
            button.textContent = 'Création…';
            const success = await Page.saveProduct(null);
            if (success) clearDraft();
            else {
                button.disabled = false;
                button.textContent = 'Créer le produit';
            }
        });
        UI()?.sticky?.register?.(footer);
        updateCreateFooter(controller);
    }

    function installCreateKeyboardSupport(modal, body, controller) {
        const viewport = global.visualViewport;
        let raf = 0;
        let baselineHeight = Math.max(viewport?.height || 0, global.innerHeight || 0);
        const sync = () => {
            global.cancelAnimationFrame(raf);
            raf = global.requestAnimationFrame(() => {
                const visualHeight = Math.max(320, Math.round(viewport?.height || global.innerHeight || 0));
                const offset = Math.max(0, Math.round(viewport?.offsetTop || 0));
                const active = document.activeElement;
                const editing = !!active && body.contains(active) && /^(INPUT|TEXTAREA|SELECT)$/.test(active.tagName);
                if (!editing) baselineHeight = Math.max(baselineHeight, visualHeight, global.innerHeight || 0);
                const keyboardVisible = editing && visualHeight <= baselineHeight * 0.86 && baselineHeight - visualHeight >= 110;
                modal.style.setProperty('--products-keyboard-height', `${visualHeight}px`);
                modal.style.setProperty('--products-keyboard-offset', `${offset}px`);
                modal.classList.toggle('is-keyboard-visible', keyboardVisible);
                if (editing) revealControlInCreateBody(active);
            });
        };
        const reveal = (event) => {
            const field = event.target.closest('input, select, textarea, [contenteditable="true"]');
            if (!field || !body.contains(field)) return;
            global.setTimeout(() => { sync(); revealControlInCreateBody(field); }, 40);
            global.setTimeout(() => { sync(); revealControlInCreateBody(field); }, 180);
        };
        const handleEnter = (event) => {
            if (event.key !== 'Enter' || event.shiftKey || event.ctrlKey || event.altKey || event.metaKey) return;
            const field = event.target.closest('input');
            if (!field || !body.contains(field) || ['submit','button','checkbox','radio','file'].includes(field.type)) return;
            const step = controller.steps[controller.current];
            const controls = Array.from(step.querySelectorAll('input:not([type="hidden"]):not([type="file"]):not([disabled]), textarea:not([disabled]), select:not([disabled])'))
                .filter((control) => control.offsetParent !== null);
            const index = controls.indexOf(field);
            const next = controls[index + 1];
            if (!next) return;
            event.preventDefault();
            next.focus({ preventScroll: true });
            revealControlInCreateBody(next);
        };
        const blurForStep = () => {
            const active = document.activeElement;
            if (active && body.contains(active) && /^(INPUT|TEXTAREA|SELECT)$/.test(active.tagName)) active.blur();
            body.scrollTop = 0;
        };
        const viewportEvent = () => sync();
        body.addEventListener('focusin', reveal, true);
        body.addEventListener('keydown', handleEnter, true);
        viewport?.addEventListener?.('resize', viewportEvent, { passive: true });
        viewport?.addEventListener?.('scroll', viewportEvent, { passive: true });
        global.addEventListener('orientationchange', viewportEvent, { passive: true });
        modal.addEventListener('products:flow-before-change', blurForStep);
        sync();
        return () => {
            body.removeEventListener('focusin', reveal, true);
            body.removeEventListener('keydown', handleEnter, true);
            viewport?.removeEventListener?.('resize', viewportEvent);
            viewport?.removeEventListener?.('scroll', viewportEvent);
            global.removeEventListener('orientationchange', viewportEvent);
            modal.removeEventListener('products:flow-before-change', blurForStep);
            modal.style.removeProperty('--products-keyboard-height');
            modal.style.removeProperty('--products-keyboard-offset');
            modal.classList.remove('is-keyboard-visible');
        };
    }

    async function enhanceCreateModal(product) {
        const body = document.getElementById('modalBody');
        const modal = document.getElementById('modalBox');
        if (!body || !modal) return;
        const identity = getSection('pmIdentityTitle');
        const image = getSection('pmImageTitle');
        const classification = getSection('pmClassTitle');
        const storage = getSection('pmStorageTitle');
        const packaging = getSection('pmPackTitle');
        const price = getSection('pmPriceTitle');
        const variants = getSection('pmVariantTitle');
        const sizes = getSection('pmSizeSectionTitle');
        const trace = getSection('pmTraceTitle');
        const threshold = getSection('pmThresholdTitle');
        const documents = getSection('pmDocsTitle');
        const initialStock = getSection('pmInitTitle');
        const supplierHost = document.getElementById('pmSupplierLinksList');

        const flow = document.createElement('div');
        flow.id = 'productsCreateFlow';
        flow.className = 'products-create-flow';
        flow.dataset.msFlow = '';
        flow.innerHTML = `
            <header class="ms-flow__header products-create-flow__header" data-modal-no-autofocus="1">
                <div class="products-create-flow__summary">
                    <strong class="products-create-flow__product-title">Nouveau produit</strong>
                    <span class="products-create-flow__current">Étape <strong data-ms-flow-current>1</strong>/<strong data-ms-flow-total>5</strong> · <b data-products-flow-step-label>Identité</b></span>
                </div>
                <div class="ms-flow__progress" data-ms-flow-progress role="progressbar" aria-valuemin="1" aria-valuemax="5"></div>
                <details class="products-create-flow__steps-toggle">
                    <summary>${esc(tr('products.mobile.create.show_steps', 'Voir les 5 étapes'))}</summary>
                    <ol class="ms-flow__steps" data-ms-flow-indicators aria-label="Étapes de création">
                        <li><button type="button" class="ms-flow__indicator" data-product-step-go="1" data-ms-flow-indicator="1">Identité</button></li>
                        <li><button type="button" class="ms-flow__indicator" data-product-step-go="2" data-ms-flow-indicator="2">Classement</button></li>
                        <li><button type="button" class="ms-flow__indicator" data-product-step-go="3" data-ms-flow-indicator="3">Achats</button></li>
                        <li><button type="button" class="ms-flow__indicator" data-product-step-go="4" data-ms-flow-indicator="4">Traçabilité</button></li>
                        <li><button type="button" class="ms-flow__indicator" data-product-step-go="5" data-ms-flow-indicator="5">Vérification</button></li>
                    </ol>
                </details>
            </header>`;

        const steps = [
            createStep(1, 'Identité du produit', 'Nom, référence, codes, description et image.'),
            createStep(2, 'Classement et emplacement', 'Choisissez le magasin avant la catégorie et le rangement.'),
            createStep(3, 'Conditionnements et achats', 'Définissez l’unité, le prix et les offres fournisseurs.'),
            createStep(4, 'Traçabilité et variantes', 'Configurez seulement les options nécessaires à ce produit.'),
            createStep(5, 'Vérification', 'Contrôlez les informations avant de créer la fiche.')
        ];
        compactIdentityPanel(identity);
        moveNodes(steps[0], [identity, createOptionalGroup(
            tr('products.mobile.create.image', 'Image du produit'),
            tr('products.mobile.create.image_help', 'Photo ou visuel optionnel.'),
            [image],
            false
        )]);
        moveNodes(steps[1], [classification, createOptionalGroup(
            tr('products.mobile.create.storage', 'Emplacement précis'),
            tr('products.mobile.create.storage_help', 'Rangement enregistré ou saisie manuelle.'),
            [storage],
            false
        )]);
        moveNodes(steps[2], [packaging, createOptionalGroup(
            tr('products.mobile.create.purchase_more', 'Prix et offres fournisseurs'),
            tr('products.mobile.create.purchase_more_help', 'Informations d’achat optionnelles.'),
            [price, createPurchasePanel(supplierHost)],
            false
        )]);
        moveNodes(steps[3], [
            createOptionalGroup(tr('products.mobile.create.trace', 'Traçabilité et seuils'), tr('products.mobile.create.trace_help', 'Lots, péremption et stock de sécurité.'), [trace, threshold], false),
            createOptionalGroup(tr('products.mobile.create.variants', 'Variantes et tailles'), tr('products.mobile.create.variants_help', 'Uniquement pour les produits concernés.'), [variants, sizes], false),
            createOptionalGroup(tr('products.mobile.create.documents', 'Documents'), tr('products.mobile.create.documents_help', 'Fiche technique et fiche de sécurité.'), [documents], false),
            createOptionalGroup(tr('products.mobile.create.initial_stock', 'Stock initial'), tr('products.mobile.create.initial_stock_help', 'Laissez vide si l’entrée sera faite plus tard.'), [initialStock], false)
        ]);
        const review = document.createElement('div');
        review.id = 'productsCreateReview';
        steps[4].appendChild(review);
        steps.forEach((step) => flow.appendChild(step));

        body.innerHTML = '';
        body.appendChild(flow);
        modal.classList.add('products-mobile-modal', 'products-mobile-modal--create');
        document.getElementById('modalTitle').textContent = 'Nouveau produit';
        document.getElementById('modalSub').textContent = 'Création guidée en cinq étapes';

        const draft = readDraft();
        if (draft) {
            const notice = document.createElement('div');
            notice.className = 'products-draft-notice';
            notice.innerHTML = '<div><strong>Brouillon retrouvé</strong><span>Votre préparation précédente a été restaurée.</span></div><button type="button" class="btn btn-ghost">Effacer</button>';
            flow.querySelector('.products-create-flow__header').appendChild(notice);
            notice.querySelector('button').addEventListener('click', () => {
                clearDraft();
                global.Modal.close();
                global.setTimeout(() => Page.openModal(), 80);
            });
        }

        const controller = global.MobileFlow.create(flow, {
            restore: false,
            startStep: draft ? Math.max(0, Math.min(4, Number(draft.step || 1) - 1)) : 0,
            validateStep: ({ stepIndex }) => validateCreateStep(stepIndex),
            onChange: ({ controller: active }) => {
                body.scrollTop = 0;
                flow.querySelector('.products-create-flow__steps-toggle')?.removeAttribute('open');
                updateCreateFooter(active);
                saveDraft(active);
            }
        });
        state.activeController = controller;
        flow.querySelectorAll('[data-product-step-go]').forEach((button) => {
            button.addEventListener('click', async () => {
                const target = Number(button.dataset.productStepGo || 1) - 1;
                if (target > controller.current) return;
                document.getElementById('modalBox')?.dispatchEvent(new CustomEvent('products:flow-before-change'));
                await controller.goTo(target, { validate: false, focus: false });
            });
        });
        configureCreateFooter(controller);
        body.addEventListener('input', () => saveDraft(controller), true);
        body.addEventListener('change', () => saveDraft(controller), true);
        if (draft) await applyDraft(draft);
        updateReview();
        controller.update(false);
        updateCreateFooter(controller);
        UI()?.enhance?.(modal);
        state.keyboardCleanup?.();
        state.keyboardCleanup = installCreateKeyboardSupport(modal, body, controller);
        global.setTimeout(() => {
            const active = document.activeElement;
            if (active && body.contains(active) && /^(INPUT|TEXTAREA|SELECT)$/.test(active.tagName)) active.blur();
            modal.querySelector('.modal-close')?.focus?.({ preventScroll: true });
        }, 30);
    }

    function createEditGroup(id, title, description, nodes, open) {
        const section = document.createElement('section');
        section.className = 'products-edit-section';
        const panelId = id + '-panel';
        section.innerHTML = `
            <button type="button" class="products-edit-section__toggle" data-ms-disclosure aria-controls="${panelId}" aria-expanded="${open ? 'true' : 'false'}">
                <span><strong>${esc(title)}</strong><small>${esc(description)}</small></span><span aria-hidden="true">⌄</span>
            </button>
            <div class="products-edit-section__panel" id="${panelId}" ${open ? '' : 'hidden'}></div>`;
        moveNodes(section.querySelector('.products-edit-section__panel'), nodes);
        return section;
    }

    function openEditActions(productId, trigger) {
        const content = document.createElement('div');
        content.className = 'products-mobile-action-list';
        content.innerHTML = `
            <button type="button" class="products-mobile-action-row" data-edit-action="plan"><strong>Localiser sur le plan</strong><span>Afficher l’emplacement physique du produit.</span></button>
            <button type="button" class="products-mobile-action-row" data-edit-action="variants"><strong>Créer des déclinaisons</strong><span>Dupliquer le produit selon une famille ou des tailles.</span></button>
            <button type="button" class="products-mobile-action-row" data-edit-action="duplicate"><strong>Dupliquer la fiche</strong><span>Créer une nouvelle fiche à partir de ce produit.</span></button>
            <button type="button" class="products-mobile-action-row products-mobile-action-row--danger" data-edit-action="delete"><strong>Supprimer le produit</strong><span>Action irréversible après confirmation.</span></button>`;
        const handle = UI().sheet.open({ title: 'Actions du produit', content, trigger });
        content.addEventListener('click', (event) => {
            const button = event.target.closest('[data-edit-action]');
            if (!button) return;
            handle.close('action');
            const action = button.dataset.editAction;
            if (action === 'plan') Page.openProductPlan(productId);
            if (action === 'variants') Page.openVariantDuplicateModal(productId);
            if (action === 'duplicate') Page.duplicateProduct(productId);
            if (action === 'delete') Page.delProduct(productId);
        });
    }

    function configureEditFooter(productId) {
        const footer = document.getElementById('modalFooter');
        if (!footer) return;
        footer.className = 'modal-footer products-edit-footer';
        footer.setAttribute('data-ms-sticky-actions', '');
        footer.innerHTML = `
            <button type="button" class="btn btn-ghost" data-products-edit-close>Fermer</button>
            <button type="button" class="btn btn-secondary" data-products-edit-more>Plus</button>
            <button type="button" class="btn btn-primary" data-products-edit-save>Sauvegarder</button>`;
        footer.querySelector('[data-products-edit-close]').addEventListener('click', () => global.Modal.close());
        footer.querySelector('[data-products-edit-more]').addEventListener('click', (event) => openEditActions(productId, event.currentTarget));
        footer.querySelector('[data-products-edit-save]').addEventListener('click', async (event) => {
            const button = event.currentTarget;
            button.disabled = true;
            button.textContent = 'Sauvegarde…';
            const success = await Page.saveProduct(productId);
            if (!success) {
                button.disabled = false;
                button.textContent = 'Sauvegarder';
            }
        });
        UI()?.sticky?.register?.(footer);
    }

    async function enhanceEditModal(productId, product) {
        const body = document.getElementById('modalBody');
        const modal = document.getElementById('modalBox');
        if (!body || !modal) return;
        const stock = body.querySelector('.pm-stock-summary');
        const identity = getSection('pmIdentityTitle');
        const image = getSection('pmImageTitle');
        const classification = getSection('pmClassTitle');
        const storage = getSection('pmStorageTitle');
        const packaging = getSection('pmPackTitle');
        const price = getSection('pmPriceTitle');
        const variants = getSection('pmVariantTitle');
        const sizes = getSection('pmSizeSectionTitle');
        const trace = getSection('pmTraceTitle');
        const threshold = getSection('pmThresholdTitle');
        const documents = getSection('pmDocsTitle');
        const supplierHost = document.getElementById('pmSupplierLinksList');

        const shell = document.createElement('div');
        shell.className = 'products-edit-page ms-detail-page';
        shell.setAttribute('data-ms-detail-page', '');
        shell.innerHTML = `
            <header class="products-edit-page__header">
                <div><span>Fiche produit</span><h2>${esc(product?.name || 'Produit')}</h2><p>${esc(product?.sku || 'Sans SKU')} · Modifiez uniquement la rubrique nécessaire.</p></div>
                <span class="products-edit-page__badge">${esc(selectedText('pmWarehouse', 'Magasin'))}</span>
            </header>
            <nav class="products-edit-page__jump" aria-label="Rubriques de la fiche">
                <button type="button" data-jump="products-edit-general-panel">Général</button>
                <button type="button" data-jump="products-edit-stock-panel">Stock</button>
                <button type="button" data-jump="products-edit-purchase-panel">Achats</button>
                <button type="button" data-jump="products-edit-trace-panel">Traçabilité</button>
            </nav>`;

        const groups = [
            createEditGroup('products-edit-general', 'Général', 'Identité, codes, description et image.', [identity, image], true),
            createEditGroup('products-edit-stock', 'Classement et emplacement', 'Stock actuel, magasin, catégorie et rangement.', [stock, classification, storage], false),
            createEditGroup('products-edit-purchase', 'Conditionnements et achats', 'Unité, prix et offres fournisseurs.', [packaging, price, createPurchasePanel(supplierHost)], false),
            createEditGroup('products-edit-trace', 'Traçabilité et variantes', 'Lots, péremption, seuils, documents et déclinaisons.', [variants, sizes, trace, threshold, documents], false)
        ];
        groups.forEach((group) => shell.appendChild(group));
        body.innerHTML = '';
        body.appendChild(shell);
        modal.classList.add('products-mobile-modal', 'products-mobile-modal--edit');
        document.getElementById('modalTitle').textContent = 'Modifier le produit';
        document.getElementById('modalSub').textContent = 'Fiche organisée par rubriques';
        configureEditFooter(productId);
        UI()?.enhance?.(modal);
        shell.querySelectorAll('[data-jump]').forEach((button) => {
            button.addEventListener('click', () => {
                const target = document.getElementById(button.dataset.jump);
                const disclosure = target?.previousElementSibling;
                if (disclosure && disclosure.getAttribute('aria-expanded') !== 'true') disclosure.click();
                target?.scrollIntoView({ block: 'start', behavior: global.matchMedia?.('(prefers-reduced-motion: reduce)').matches ? 'auto' : 'smooth' });
                disclosure?.focus?.({ preventScroll: true });
            });
        });
    }

    async function enhanceModal(context) {
        const modal = document.getElementById('modalBox');
        const footer = document.getElementById('modalFooter');
        if (!modal) return;
        modal.classList.remove('products-mobile-modal', 'products-mobile-modal--create', 'products-mobile-modal--edit');
        footer?.removeAttribute('data-ms-sticky-actions');
        state.keyboardCleanup?.();
        state.keyboardCleanup = null;
        state.activeController?.destroy?.();
        state.activeController = null;
        state.activeMode = '';
        state.activeProductId = context?.pid || null;
        if (!isAdaptive() || global.SM_ROLE !== 'admin') return;
        state.activeMode = context?.isEdit ? 'edit' : 'create';
        if (context?.isEdit) await enhanceEditModal(context.pid, context.product || {});
        else await enhanceCreateModal(context?.product || {});
    }

    function patchProductPage() {
        if (state.patched || !Page) return;
        state.patched = true;
        const originalRenderPaged = Page.renderPaged.bind(Page);
        Page.renderPaged = function productsResponsiveRenderPaged() {
            if (isAdaptive()) {
                renderMobileCards(false);
                return;
            }
            toggleResponsiveContainers(false);
            return originalRenderPaged();
        };
        const originalInfinite = Page._initInfiniteScroll.bind(Page);
        Page._initInfiniteScroll = function productsResponsiveInfiniteScroll() {
            if (isAdaptive()) return;
            return originalInfinite();
        };
    }

    function init() {
        if (state.initialized || global.SM_PAGE !== 'products' || !Page) return;
        state.initialized = true;
        document.documentElement.dataset.productsMobileFlow = VERSION;
        patchProductPage();
        ensureMobileContainers();
        createMobileActionBar();
        ensureProductScanButton();
        bindFilters();
        global.addEventListener('magestock:devicechange', () => {
            state.scannerConsumer?.close?.('device-change');
            state.scannerConsumer = null;
            state.signature = '';
            if (isAdaptive()) renderMobileCards(true);
            else {
                toggleResponsiveContainers(false);
                Page._applyView?.(Page._currentView || 'grid');
            }
        });
        document.addEventListener('magestock:mobilefoundationready', bindFilters, { once: true });
    }

    global.MageStockProductsMobile = {
        version: VERSION,
        init,
        enhanceModal,
        renderCards: () => renderMobileCards(true),
        openScanner: openProductScanner,
        clearDraft
    };

    if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', init, { once: true });
    else init();
})(window, document);
