/**
 * Fichier  : assets/js/pages/orders-mobile.js
 * Auteur   : KREMER Régis
 * Version  : 2.6.90
 * Date     : 2026-07-20
 * Description : Extension téléphone/tablette des commandes — assistant réel, brouillons serveur,
 *               cartes de lignes, réception orientée scanner et revue avant application au stock.
 *
 * Changelog :
 *   v2.6.80 (2026-07-17) — Première version ORDERS MOBILE FLOW4.
 *   v2.6.90 (2026-07-20) — SCANNER CONSUMERS MIGRATION3 : réception reliée au scanner commun.
 */
(function ordersMobileFlow(global, document) {
    'use strict';

    if (typeof OrdersPage === 'undefined') return;

    const original = {
        init: OrdersPage.init,
        showList: OrdersPage.showList,
        showForm: OrdersPage.showForm,
        showReceive: OrdersPage.showReceive
    };

    const STATUS_META = {
        draft: { label: 'Brouillon', state: 'neutral', icon: '📝' },
        sent: { label: 'Envoyé', state: 'info', icon: '📤' },
        partial: { label: 'Partiel', state: 'warning', icon: '📦' },
        received: { label: 'Reçu', state: 'success', icon: '✅' },
        cancelled: { label: 'Annulé', state: 'danger', icon: '⛔' }
    };
    const VALIDATION_META = {
        pending: { label: 'À valider', state: 'neutral' },
        approved: { label: 'Validé', state: 'success' },
        refused: { label: 'Refusé', state: 'danger' }
    };
    const UNIT_OPTIONS = [
        ['piece', 'Pièce'], ['carton', 'Carton'], ['palette', 'Palette'], ['bidon', 'Bidon'],
        ['fut', 'Fût'], ['aerosol', 'Aérosol'], ['sac', 'Sac'], ['seau', 'Seau'],
        ['rouleau', 'Rouleau'], ['bobine', 'Bobine'], ['tube', 'Tube'], ['boite', 'Boîte'],
        ['paquet', 'Paquet'], ['ml', 'Mètre linéaire'], ['m2', 'm²'], ['m3', 'm³'],
        ['litre', 'Litre'], ['kg', 'Kilogramme']
    ];

    const escapeHtml = (value) => String(value ?? '').replace(/[&<>"']/g, (char) => ({
        '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;'
    }[char]));
    const safeNumber = (value, fallback = 0) => {
        const parsed = Number(value);
        return Number.isFinite(parsed) ? parsed : fallback;
    };
    const debounce = (fn, delay) => {
        let timer = 0;
        return (...args) => {
            global.clearTimeout(timer);
            timer = global.setTimeout(() => fn(...args), delay);
        };
    };
    const uid = (prefix = 'ord') => `${prefix}-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
    const formatMoney = (value) => global.Locale?.formatMoney ? global.Locale.formatMoney(safeNumber(value)) : `${safeNumber(value).toFixed(2)} €`;
    const formatDate = (value) => {
        if (!value) return '—';
        if (global.Locale?.formatDateTime) return global.Locale.formatDateTime(value);
        try { return new Intl.DateTimeFormat('fr-FR', { dateStyle: 'medium', timeStyle: 'short' }).format(new Date(value)); }
        catch (_) { return String(value); }
    };

    OrdersPage._mobileFilters = { status: '', supplier: '', query: '' };
    OrdersPage._mobileOrderState = null;
    OrdersPage._mobileFlow = null;
    OrdersPage._mobileDraftTimer = 0;
    OrdersPage._mobileDraftSaving = null;
    OrdersPage._mobileDraftQueued = false;
    OrdersPage._mobilePanel = null;
    OrdersPage._receiveState = null;
    OrdersPage._receiveReviewPanel = null;
    OrdersPage._receiveScannerConsumer = null;
    OrdersPage._ordersMobileBound = false;

    OrdersPage.isOrdersTerrainMode = function isOrdersTerrainMode() {
        const profile = document.documentElement?.dataset?.msDevice;
        if (profile) return profile !== 'desktop';
        return global.matchMedia ? global.matchMedia('(max-width: 1199px)').matches : global.innerWidth < 1200;
    };

    OrdersPage._supplierById = function supplierById(id) {
        return (this.suppliers || []).find((supplier) => String(supplier.id) === String(id));
    };
    OrdersPage._warehouseById = function warehouseById(id) {
        return (this.warehouses || []).find((warehouse) => String(warehouse.id) === String(id));
    };
    OrdersPage._mobileProductById = function mobileProductById(id) {
        return (this.products || []).find((product) => String(product.id) === String(id));
    };
    OrdersPage._mobileProductCode = function mobileProductCode(product) {
        if (!product) return [];
        const codes = [product.barcode, product.sku];
        (Array.isArray(product.eans) ? product.eans : []).forEach((ean) => {
            codes.push(typeof ean === 'object' ? (ean.code || ean.value || '') : ean);
        });
        return codes.filter(Boolean).map(String);
    };
    OrdersPage._mobileImage = function mobileImage(product) {
        if (!product?.image) return '';
        return global.Api?.imageUrl ? global.Api.imageUrl(product.image) : product.image;
    };
    OrdersPage._orderDestinationIds = function orderDestinationIds(order) {
        const ids = new Set();
        if (order?.draftWarehouseId) ids.add(String(order.draftWarehouseId));
        (order?.lines || []).forEach((line) => {
            if (line.warehouseId) ids.add(String(line.warehouseId));
        });
        return [...ids];
    };
    OrdersPage._orderDestinationLabel = function orderDestinationLabel(order) {
        const names = this._orderDestinationIds(order)
            .map((id) => this._warehouseById(id)?.name)
            .filter(Boolean);
        if (!names.length) return 'Destination à renseigner';
        if (names.length === 1) return names[0];
        return `${names[0]} + ${names.length - 1} autre${names.length > 2 ? 's' : ''}`;
    };
    OrdersPage._orderTotal = function orderTotal(order) {
        return (order?.lines || []).reduce((sum, line) => sum + safeNumber(line.quantity) * safeNumber(line.unitCost), 0);
    };
    OrdersPage._draftComplete = function draftComplete(order) {
        return !!order?.supplierId && this._orderDestinationIds(order).length > 0 && (order?.lines || []).length > 0;
    };
    OrdersPage._draftStepLabel = function draftStepLabel(order) {
        const step = Math.max(1, Math.min(4, Number(order?.draftStep || ((order?.lines || []).length ? 4 : 1))));
        return `Étape ${step} sur 4`;
    };

    OrdersPage.init = async function initOrdersMobile() {
        await original.init.call(this);
        if (this._ordersMobileBound) return;
        this._ordersMobileBound = true;
        document.addEventListener('magestock:devicechange', () => {
            this._receiveScannerConsumer?.close?.('device-change');
            this._receiveScannerConsumer = null;
            if (this._view === 'form') this.showForm(this._mobileOrderState?.id || this.editId || null);
            else if (this._view === 'receive') this.showReceive(this.receiveId);
            else this.showList();
        });
        document.addEventListener('visibilitychange', () => {
            this._persistMobileDraftLocal();
            this._persistReceiveDraftLocal();
        });
        global.addEventListener('beforeunload', () => {
            this._persistMobileDraftLocal();
            this._persistReceiveDraftLocal();
        });
    };

    OrdersPage._cleanupOrdersMobile = function cleanupOrdersMobile() {
        global.clearTimeout(this._mobileDraftTimer);
        this._mobileFlow?.destroy?.();
        this._mobileFlow = null;
        this._mobilePanel?.close?.('navigation');
        this._mobilePanel = null;
        this._receiveReviewPanel?.close?.('navigation');
        this._receiveReviewPanel = null;
        this._receiveScannerConsumer?.close?.('navigation');
        this._receiveScannerConsumer = null;
    };

    // ---------------------------------------------------------------------
    // Mobile list and resumable drafts
    // ---------------------------------------------------------------------
    OrdersPage.showList = function showOrdersListResponsive() {
        if (!this.isOrdersTerrainMode()) {
            this._cleanupOrdersMobile();
            return original.showList.call(this);
        }
        this._view = 'list';
        this.editId = null;
        this.receiveId = null;
        this._cleanupOrdersMobile();
        const root = document.getElementById('ordersRoot');
        if (!root) return;
        root.innerHTML = this._mobileListHTML();
        this._bindMobileList();
        this._renderMobileOrderCards();
    };

    OrdersPage._mobileListHTML = function mobileListHTML() {
        const drafts = (this.orders || []).filter((order) => order.status === 'draft');
        const pendingReceive = (this.orders || []).filter((order) => ['sent', 'partial'].includes(order.status) && (order.validationStatus || 'pending') === 'approved');
        const pendingValidation = (this.orders || []).filter((order) => order.status === 'sent' && (order.validationStatus || 'pending') === 'pending');
        return `
            <section class="ord-mobile-list" aria-labelledby="ordMobileTitle">
                <header class="ord-mobile-hero">
                    <div>
                        <span class="ord-mobile-eyebrow">Achats et réceptions</span>
                        <h2 id="ordMobileTitle">Bons de commande</h2>
                        <p>Créez, reprenez et réceptionnez les commandes avec un parcours adapté au terrain.</p>
                    </div>
                    <button type="button" class="btn btn-primary ord-mobile-new" id="btnOrderMobileNew">Nouveau BC</button>
                </header>
                <div class="ord-mobile-kpis" aria-label="Synthèse commandes">
                    <div><strong>${drafts.length}</strong><span>À reprendre</span></div>
                    <div><strong>${pendingValidation.length}</strong><span>À valider</span></div>
                    <div><strong>${pendingReceive.length}</strong><span>À recevoir</span></div>
                    <div><strong>${(this.orders || []).filter((order) => order.status === 'received').length}</strong><span>Reçues</span></div>
                </div>
                <div class="ord-mobile-tools">
                    <label class="ord-mobile-search">
                        <span class="sr-only">Rechercher une commande</span>
                        <input type="search" id="ordMobileSearch" class="form-input" placeholder="N° BC, référence, fournisseur…" value="${escapeHtml(this._mobileFilters.query)}">
                    </label>
                    <button type="button" class="btn btn-secondary" id="btnOrderMobileFilters">Filtres <span id="ordMobileFilterCount" class="ord-filter-count" hidden>0</span></button>
                </div>
                <section id="ordMobileDraftSection" class="ord-mobile-section" aria-labelledby="ordMobileDraftTitle" hidden>
                    <div class="ord-mobile-section__head"><div><span class="ord-mobile-eyebrow">Brouillons serveur</span><h3 id="ordMobileDraftTitle">Commandes à reprendre</h3></div></div>
                    <div id="ordMobileDrafts" class="ord-mobile-grid"></div>
                </section>
                <section class="ord-mobile-section" aria-labelledby="ordMobileAllTitle">
                    <div class="ord-mobile-section__head"><div><span class="ord-mobile-eyebrow">Suivi</span><h3 id="ordMobileAllTitle">Commandes</h3></div><span id="ordMobileResultCount" class="ord-mobile-count"></span></div>
                    <div id="ordMobileCards" class="ord-mobile-grid"></div>
                    <div id="ordMobileEmpty" class="ord-mobile-empty" hidden>Aucune commande ne correspond aux filtres.</div>
                </section>
            </section>`;
    };

    OrdersPage._bindMobileList = function bindMobileList() {
        document.getElementById('btnOrderMobileNew')?.addEventListener('click', () => this.showForm());
        const search = document.getElementById('ordMobileSearch');
        search?.addEventListener('input', debounce(() => {
            this._mobileFilters.query = search.value.trim();
            this._renderMobileOrderCards();
        }, 180));
        document.getElementById('btnOrderMobileFilters')?.addEventListener('click', (event) => this.openOrderMobileFilters(event.currentTarget));
        document.getElementById('ordersRoot')?.addEventListener('click', (event) => {
            const action = event.target.closest('[data-order-mobile-action]');
            if (!action) return;
            const id = action.dataset.orderId;
            const order = (this.orders || []).find((item) => String(item.id) === String(id));
            if (!order) return;
            const type = action.dataset.orderMobileAction;
            if (type === 'resume') this.showForm(order.id);
            else if (type === 'receive') this.showReceive(order.id);
            else if (type === 'detail') this.viewDetail(order.id);
            else if (type === 'menu') this.openOrderActionSheet(order, action);
        });
        this._refreshOrderMobileFilterCount();
    };

    OrdersPage._filteredMobileOrders = function filteredMobileOrders() {
        const query = this._mobileFilters.query.toLocaleLowerCase('fr');
        return (this.orders || []).filter((order) => {
            if (this._mobileFilters.status && order.status !== this._mobileFilters.status) return false;
            if (this._mobileFilters.supplier && String(order.supplierId) !== String(this._mobileFilters.supplier)) return false;
            if (!query) return true;
            const supplier = this._supplierById(order.supplierId);
            const haystack = [order.orderNumber, order.ref, order.note, supplier?.name, this._orderDestinationLabel(order)]
                .filter(Boolean).join(' ').toLocaleLowerCase('fr');
            return haystack.includes(query);
        });
    };

    OrdersPage._renderMobileOrderCards = function renderMobileOrderCards() {
        const filtered = this._filteredMobileOrders();
        const drafts = filtered.filter((order) => order.status === 'draft');
        const regular = filtered.filter((order) => order.status !== 'draft');
        const draftSection = document.getElementById('ordMobileDraftSection');
        if (draftSection) draftSection.hidden = drafts.length === 0;
        this._renderMobileCardsInto(document.getElementById('ordMobileDrafts'), drafts, true);
        this._renderMobileCardsInto(document.getElementById('ordMobileCards'), regular, false);
        const count = document.getElementById('ordMobileResultCount');
        if (count) count.textContent = `${regular.length} commande${regular.length > 1 ? 's' : ''}`;
        const empty = document.getElementById('ordMobileEmpty');
        if (empty) empty.hidden = regular.length > 0 || drafts.length > 0;
    };

    OrdersPage._renderMobileCardsInto = function renderMobileCardsInto(container, orders, draftMode) {
        if (!container) return;
        container.innerHTML = (orders || []).map((order) => this._mobileOrderCardHTML(order, draftMode)).join('');
    };

    OrdersPage._mobileOrderCardHTML = function mobileOrderCardHTML(order, draftMode) {
        const supplier = this._supplierById(order.supplierId);
        const status = STATUS_META[order.status] || { label: order.status || '—', state: 'neutral', icon: '•' };
        const validation = VALIDATION_META[order.validationStatus || 'pending'] || VALIDATION_META.pending;
        const lineCount = (order.lines || []).length;
        const total = this._orderTotal(order);
        const ordered = (order.lines || []).reduce((sum, line) => sum + safeNumber(line.quantity), 0);
        const received = (order.lines || []).reduce((sum, line) => sum + safeNumber(line.quantityReceived), 0);
        const progress = ordered > 0 ? Math.min(100, Math.round(received / ordered * 100)) : 0;
        const canReceive = ['sent', 'partial'].includes(order.status) && (order.validationStatus || 'pending') === 'approved';
        const updated = order.draftUpdatedAt || order.updatedAt || order.createdAt;
        return `
            <article class="ord-mobile-card" data-state="${escapeHtml(status.state)}">
                <header class="ord-mobile-card__head">
                    <div>
                        <span class="ord-mobile-card__number">${escapeHtml(order.orderNumber || 'Brouillon')}</span>
                        <h4>${escapeHtml(supplier?.name || (draftMode ? 'Fournisseur à choisir' : 'Fournisseur inconnu'))}</h4>
                        <p>${escapeHtml(this._orderDestinationLabel(order))}${order.ref ? ` · ${escapeHtml(order.ref)}` : ''}</p>
                    </div>
                    <span class="ord-mobile-badge" data-state="${escapeHtml(status.state)}">${status.icon} ${escapeHtml(status.label)}</span>
                </header>
                ${draftMode ? `
                    <div class="ord-mobile-draft-state">
                        <span>${escapeHtml(this._draftStepLabel(order))}</span>
                        <span>${this._draftComplete(order) ? 'Prêt à envoyer' : 'À compléter'}</span>
                        <span>Sauvegardé ${escapeHtml(formatDate(updated))}</span>
                    </div>` : `
                    <div class="ord-mobile-progress" aria-label="Réception ${progress}%"><span style="width:${progress}%"></span></div>`}
                <dl class="ord-mobile-card__facts">
                    <div><dt>Lignes</dt><dd>${lineCount}</dd></div>
                    <div><dt>Total estimé</dt><dd>${escapeHtml(formatMoney(total))}</dd></div>
                    <div><dt>Validation</dt><dd data-state="${escapeHtml(validation.state)}">${escapeHtml(validation.label)}</dd></div>
                    ${draftMode ? `<div><dt>Créé par</dt><dd>${escapeHtml(order.userName || '—')}</dd></div>` : `<div><dt>Réception</dt><dd>${received} / ${ordered}</dd></div>`}
                </dl>
                <footer class="ord-mobile-card__actions">
                    ${draftMode ? `<button type="button" class="btn btn-primary" data-order-mobile-action="resume" data-order-id="${escapeHtml(order.id)}">Reprendre</button>` : ''}
                    ${canReceive ? `<button type="button" class="btn btn-success" data-order-mobile-action="receive" data-order-id="${escapeHtml(order.id)}">Réceptionner</button>` : ''}
                    <button type="button" class="btn btn-secondary" data-order-mobile-action="detail" data-order-id="${escapeHtml(order.id)}">Détails</button>
                    <button type="button" class="btn btn-ghost ord-mobile-menu" aria-label="Autres actions pour ${escapeHtml(order.orderNumber || 'la commande')}" data-order-mobile-action="menu" data-order-id="${escapeHtml(order.id)}">⋯</button>
                </footer>
            </article>`;
    };

    OrdersPage.openOrderMobileFilters = function openOrderMobileFilters(trigger) {
        if (!global.FilterDrawer) return;
        const content = document.createElement('form');
        content.className = 'ord-filter-drawer';
        content.innerHTML = `
            <div class="ord-mobile-field"><label for="ordDrawerStatus">Statut</label><select id="ordDrawerStatus" class="form-select">
                <option value="">Tous les statuts</option><option value="draft">Brouillon</option><option value="sent">Envoyé</option><option value="partial">Partiel</option><option value="received">Reçu</option><option value="cancelled">Annulé</option>
            </select></div>
            <div class="ord-mobile-field"><label for="ordDrawerSupplier">Fournisseur</label><select id="ordDrawerSupplier" class="form-select"><option value="">Tous les fournisseurs</option>${(this.suppliers || []).map((supplier) => `<option value="${escapeHtml(supplier.id)}">${escapeHtml(supplier.name)}</option>`).join('')}</select></div>`;
        content.querySelector('#ordDrawerStatus').value = this._mobileFilters.status;
        content.querySelector('#ordDrawerSupplier').value = this._mobileFilters.supplier;
        global.FilterDrawer.open({
            title: 'Filtrer les commandes',
            description: 'Les filtres s’appliquent aux brouillons et aux commandes suivies.',
            content,
            trigger,
            actions: [
                { label: 'Réinitialiser', className: 'btn btn-secondary', close: false, onClick: () => {
                    content.reset();
                    this._mobileFilters.status = '';
                    this._mobileFilters.supplier = '';
                    this._renderMobileOrderCards();
                    this._refreshOrderMobileFilterCount();
                    return false;
                } },
                { label: 'Appliquer', className: 'btn btn-primary', onClick: () => {
                    this._mobileFilters.status = content.querySelector('#ordDrawerStatus').value;
                    this._mobileFilters.supplier = content.querySelector('#ordDrawerSupplier').value;
                    this._renderMobileOrderCards();
                    this._refreshOrderMobileFilterCount();
                } }
            ]
        });
    };

    OrdersPage._refreshOrderMobileFilterCount = function refreshOrderMobileFilterCount() {
        const total = Number(!!this._mobileFilters.status) + Number(!!this._mobileFilters.supplier);
        const badge = document.getElementById('ordMobileFilterCount');
        if (!badge) return;
        badge.textContent = String(total);
        badge.hidden = total === 0;
    };

    OrdersPage.openOrderActionSheet = function openOrderActionSheet(order, trigger) {
        if (!global.ActionSheet) return;
        const actions = [];
        if (order.status === 'draft' && this._draftComplete(order)) {
            actions.push({ label: 'Envoyer pour validation', className: 'btn btn-primary', onClick: () => this.markSent(order.id) });
        }
        if (order.status === 'sent' && (order.validationStatus || 'pending') === 'pending' && global.SM_ROLE === 'admin') {
            actions.push({ label: 'Valider la commande', className: 'btn btn-success', onClick: () => this.approve(order.id) });
            actions.push({ label: 'Refuser la commande', className: 'btn btn-warning', onClick: () => this.refuse(order.id) });
        }
        if (order.status === 'sent' && (order.validationStatus || 'pending') === 'refused') {
            actions.push({ label: 'Re-soumettre', className: 'btn btn-primary', onClick: () => this.resubmit(order.id) });
        }
        if (order.status === 'received' && !order.storedAt && global.SM_ROLE === 'admin') {
            actions.push({ label: 'Marquer comme rangée', className: 'btn btn-success', onClick: () => this.store(order.id) });
        }
        if (['draft', 'sent'].includes(order.status) && global.SM_ROLE === 'admin') {
            actions.push({ label: 'Annuler la commande', className: 'btn btn-warning', onClick: () => this.cancelOrder(order.id) });
        }
        if (['draft', 'cancelled'].includes(order.status)) {
            actions.push({ label: 'Supprimer définitivement', className: 'btn btn-danger', onClick: () => this.deleteOrder(order.id) });
        }
        if (!actions.length) actions.push({ label: 'Aucune autre action disponible', className: 'btn btn-secondary', disabled: true });
        global.ActionSheet.open({
            title: order.orderNumber || 'Commande',
            description: this._supplierById(order.supplierId)?.name || 'Actions de la commande',
            content: '<p class="ord-action-sheet-note">Les actions proposées dépendent du statut et de la validation de la commande.</p>',
            trigger,
            actions
        });
    };

    // ---------------------------------------------------------------------
    // Four-step order wizard with server-side draft
    // ---------------------------------------------------------------------
    OrdersPage._draftLocalKey = function draftLocalKey(id) {
        return `magestock:orders:mobile-draft:${id || 'new'}`;
    };
    OrdersPage._stateFromOrder = function stateFromOrder(order) {
        const local = this._readDraftLocal(order?.id || 'new');
        const warehouseId = order?.draftWarehouseId || this._orderDestinationIds(order || {})[0] || '';
        const base = {
            id: order?.id || null,
            orderNumber: order?.orderNumber || '',
            supplierId: order?.supplierId || '',
            warehouseId: warehouseId || '',
            ref: order?.ref || '',
            note: order?.note || '',
            step: Math.max(1, Math.min(4, Number(order?.draftStep || 1))),
            revision: Math.max(1, Number(order?.draftRevision || 1)),
            lines: (order?.lines || []).map((line, index) => ({
                clientId: `server-${index}-${line.productId}`,
                productId: String(line.productId),
                warehouseId: String(line.warehouseId || warehouseId || ''),
                quantity: safeNumber(line.quantity, 1),
                unitCost: safeNumber(line.unitCost),
                unit: line.unit || 'piece',
                supplierSku: line.supplierSku || '',
                note: line.note || ''
            })),
            dirty: false,
            conflict: false,
            saveStatus: order ? 'Sauvegardé sur le serveur' : 'Préparation locale'
        };
        if (!local || (order && String(local.id || '') !== String(order.id))) return base;
        const serverTime = new Date(order?.draftUpdatedAt || order?.updatedAt || order?.createdAt || 0).getTime();
        const localTime = new Date(local.savedAt || 0).getTime();
        if (localTime <= serverTime) return base;
        return Object.assign(base, local.state || {}, { id: order?.id || local.state?.id || null, revision: base.revision, dirty: true, saveStatus: 'Saisie locale restaurée' });
    };
    OrdersPage._readDraftLocal = function readDraftLocal(id) {
        try {
            const raw = global.localStorage?.getItem(this._draftLocalKey(id));
            return raw ? JSON.parse(raw) : null;
        } catch (_) { return null; }
    };
    OrdersPage._persistMobileDraftLocal = function persistMobileDraftLocal() {
        const state = this._mobileOrderState;
        if (!state) return;
        const payload = {
            id: state.id,
            savedAt: new Date().toISOString(),
            state: {
                id: state.id, orderNumber: state.orderNumber, supplierId: state.supplierId,
                warehouseId: state.warehouseId, ref: state.ref, note: state.note,
                step: state.step, lines: state.lines
            }
        };
        try { global.localStorage?.setItem(this._draftLocalKey(state.id || 'new'), JSON.stringify(payload)); } catch (_) {}
    };
    OrdersPage._clearDraftLocal = function clearDraftLocal(id) {
        try {
            global.localStorage?.removeItem(this._draftLocalKey(id || 'new'));
            if (id) global.localStorage?.removeItem(this._draftLocalKey('new'));
        } catch (_) {}
    };

    OrdersPage.showForm = function showOrderFormResponsive(orderId = null) {
        if (!this.isOrdersTerrainMode()) return original.showForm.call(this, orderId);
        this._cleanupOrdersMobile();
        this._view = 'form';
        this.editId = orderId;
        const order = orderId ? (this.orders || []).find((item) => String(item.id) === String(orderId)) : null;
        if (order && order.status !== 'draft') {
            toast('Seuls les brouillons peuvent être modifiés.', 'error');
            return this.showList();
        }
        this._mobileOrderState = this._stateFromOrder(order);
        const root = document.getElementById('ordersRoot');
        if (!root) return;
        root.innerHTML = this._mobileOrderFlowHTML(this._mobileOrderState);
        this._bindMobileOrderFlow();
    };

    OrdersPage._mobileOrderFlowHTML = function mobileOrderFlowHTML(state) {
        const supplierOptions = (this.suppliers || []).map((supplier) => `<option value="${escapeHtml(supplier.id)}" ${String(state.supplierId) === String(supplier.id) ? 'selected' : ''}>${escapeHtml(supplier.name)}</option>`).join('');
        const warehouseOptions = (this.warehouses || []).map((warehouse) => `<option value="${escapeHtml(warehouse.id)}" ${String(state.warehouseId) === String(warehouse.id) ? 'selected' : ''}>${escapeHtml(warehouse.name)}</option>`).join('');
        return `
            <section class="ord-flow-page" aria-labelledby="ordFlowTitle">
                <header class="ord-flow-head">
                    <button type="button" class="btn btn-ghost" id="btnOrdFlowClose" aria-label="Retour aux commandes">←</button>
                    <div><span class="ord-mobile-eyebrow">${state.orderNumber ? escapeHtml(state.orderNumber) : 'Nouveau brouillon'}</span><h2 id="ordFlowTitle">Créer un bon de commande</h2><p id="ordDraftStatus">${escapeHtml(state.saveStatus)}</p></div>
                    <button type="button" class="btn btn-secondary" id="btnOrdSaveDraft">Enregistrer</button>
                </header>
                <div class="ord-flow-progress" data-ms-flow-progress><span></span></div>
                <nav class="ord-flow-steps" aria-label="Étapes de création">
                    <button type="button" data-ms-flow-indicator="1" data-ms-flow-go="1" data-ms-flow-validate="false"><span>1</span>Fournisseur</button>
                    <button type="button" data-ms-flow-indicator="2" data-ms-flow-go="2"><span>2</span>Destination</button>
                    <button type="button" data-ms-flow-indicator="3" data-ms-flow-go="3"><span>3</span>Produits</button>
                    <button type="button" data-ms-flow-indicator="4" data-ms-flow-go="4"><span>4</span>Vérification</button>
                </nav>
                <div id="ordMobileFlow" class="ord-flow" data-ms-flow data-ms-flow-start="${state.step}">
                    <section class="ms-flow__panel ord-flow-panel" data-ms-flow-step="1">
                        <span class="ord-mobile-eyebrow">Étape 1 sur 4</span><h3 data-ms-flow-title>Choisir le fournisseur</h3>
                        <p>Sélectionnez le fournisseur concerné. Le brouillon sera créé sur le serveur avant de poursuivre.</p>
                        <div class="ord-mobile-field"><label for="ordFlowSupplier">Fournisseur *</label><select id="ordFlowSupplier" class="form-select" required><option value="">— Choisir —</option>${supplierOptions}</select></div>
                        <button type="button" class="btn btn-secondary ord-inline-action" id="btnOrdNewSupplier">Créer un fournisseur</button>
                    </section>
                    <section class="ms-flow__panel ord-flow-panel" data-ms-flow-step="2">
                        <span class="ord-mobile-eyebrow">Étape 2 sur 4</span><h3 data-ms-flow-title>Définir la destination</h3>
                        <p>Le magasin sélectionné est appliqué aux nouvelles lignes de commande.</p>
                        <div class="ord-mobile-field"><label for="ordFlowWarehouse">Magasin de destination *</label><select id="ordFlowWarehouse" class="form-select" required><option value="">— Choisir —</option>${warehouseOptions}</select></div>
                        <div class="ord-mobile-field"><label for="ordFlowRef">Référence interne</label><input type="text" id="ordFlowRef" class="form-input" value="${escapeHtml(state.ref)}" placeholder="Ex. CMD-2026-001"></div>
                        <div class="ord-mobile-field"><label for="ordFlowNote">Instructions / note</label><textarea id="ordFlowNote" class="form-input" rows="4" placeholder="Délais, urgence, conditions…">${escapeHtml(state.note)}</textarea></div>
                    </section>
                    <section class="ms-flow__panel ord-flow-panel ord-products-step" data-ms-flow-step="3">
                        <span class="ord-mobile-eyebrow">Étape 3 sur 4</span><h3 data-ms-flow-title>Ajouter les produits</h3>
                        <p>Les lignes sont enregistrées dans le brouillon serveur à chaque modification.</p>
                        <div class="ord-products-actions">
                            <button type="button" class="btn btn-primary" id="btnOrdAddProduct">Ajouter un produit</button>
                            <button type="button" class="btn btn-secondary" id="btnOrdCreateProduct">Nouveau produit</button>
                        </div>
                        <div id="ordFlowLines" class="ord-line-grid"></div>
                        <div id="ordFlowLinesEmpty" class="ord-mobile-empty">Aucun produit ajouté.</div>
                    </section>
                    <section class="ms-flow__panel ord-flow-panel" data-ms-flow-step="4">
                        <span class="ord-mobile-eyebrow">Étape 4 sur 4</span><h3 data-ms-flow-title>Vérifier la commande</h3>
                        <p>Contrôlez le fournisseur, la destination, les produits et le montant avant l’envoi.</p>
                        <div id="ordFlowReview" class="ord-flow-review"></div>
                    </section>
                </div>
                <footer class="ord-flow-actions" data-ms-sticky-actions>
                    <button type="button" class="btn btn-secondary" id="btnOrdFlowBack">Retour</button>
                    <span class="ord-flow-step-label">Étape <strong data-ms-flow-current>${state.step}</strong> / <span data-ms-flow-total>4</span></span>
                    <button type="button" class="btn btn-primary" id="btnOrdFlowNext">Suivant</button>
                </footer>
            </section>`;
    };

    OrdersPage._bindMobileOrderFlow = function bindMobileOrderFlow() {
        const flowRoot = document.getElementById('ordMobileFlow');
        if (!flowRoot || !global.MobileFlow) return;
        this._mobileFlow = global.MobileFlow.create(flowRoot, {
            restore: false,
            startStep: Math.max(0, (this._mobileOrderState?.step || 1) - 1),
            validateStep: ({ stepIndex }) => this._validateMobileOrderStep(stepIndex),
            onChange: ({ index }) => {
                if (!this._mobileOrderState) return;
                this._mobileOrderState.step = index + 1;
                this._updateMobileOrderFlowActions();
                if (index === 2) this._renderMobileOrderLines();
                if (index === 3) this._renderMobileOrderReview();
                this._scheduleMobileDraftSave(250);
            }
        });
        this._updateMobileOrderFlowActions();
        this._renderMobileOrderLines();
        if (this._mobileFlow.current === 3) this._renderMobileOrderReview();

        const supplier = document.getElementById('ordFlowSupplier');
        const warehouse = document.getElementById('ordFlowWarehouse');
        const ref = document.getElementById('ordFlowRef');
        const note = document.getElementById('ordFlowNote');
        supplier?.addEventListener('change', () => { this._mobileOrderState.supplierId = supplier.value; this._markDraftDirty(); });
        warehouse?.addEventListener('change', () => {
            const previous = String(this._mobileOrderState.warehouseId || '');
            const next = String(warehouse.value || '');
            this._mobileOrderState.warehouseId = next;
            this._mobileOrderState.lines.forEach((line) => {
                if (!line.warehouseId || String(line.warehouseId) === previous) line.warehouseId = next;
            });
            this._markDraftDirty();
        });
        ref?.addEventListener('input', () => { this._mobileOrderState.ref = ref.value; this._markDraftDirty(); });
        note?.addEventListener('input', () => { this._mobileOrderState.note = note.value; this._markDraftDirty(); });

        document.getElementById('btnOrdFlowClose')?.addEventListener('click', async () => {
            if (this._mobileOrderState?.dirty) await this._saveMobileDraft({ silent: true });
            this.showList();
        });
        document.getElementById('btnOrdSaveDraft')?.addEventListener('click', () => this._saveMobileDraft({ explicit: true }));
        document.getElementById('btnOrdFlowBack')?.addEventListener('click', () => {
            if (this._mobileFlow.current === 0) this.showList();
            else this._mobileFlow.back();
        });
        document.getElementById('btnOrdFlowNext')?.addEventListener('click', async () => {
            if (this._mobileFlow.current < 3) await this._mobileFlow.next();
            else await this._sendMobileOrder();
        });
        document.getElementById('btnOrdNewSupplier')?.addEventListener('click', (event) => this._openMobileSupplierCreator(event.currentTarget));
        document.getElementById('btnOrdAddProduct')?.addEventListener('click', (event) => this._openMobileProductPicker(event.currentTarget));
        document.getElementById('btnOrdCreateProduct')?.addEventListener('click', (event) => this._openMobileProductCreator(event.currentTarget));
        document.getElementById('ordFlowLines')?.addEventListener('click', (event) => {
            const button = event.target.closest('[data-order-line-action]');
            if (!button) return;
            const line = this._mobileOrderState.lines.find((item) => item.clientId === button.dataset.lineId);
            if (!line) return;
            if (button.dataset.orderLineAction === 'edit') this._openMobileLineEditor(this._mobileProductById(line.productId), line, button);
            if (button.dataset.orderLineAction === 'remove') {
                this._mobileOrderState.lines = this._mobileOrderState.lines.filter((item) => item.clientId !== line.clientId);
                this._markDraftDirty();
                this._renderMobileOrderLines();
            }
        });
    };

    OrdersPage._syncMobileOrderInputs = function syncMobileOrderInputs() {
        const state = this._mobileOrderState;
        if (!state) return;
        const supplier = document.getElementById('ordFlowSupplier');
        const warehouse = document.getElementById('ordFlowWarehouse');
        const ref = document.getElementById('ordFlowRef');
        const note = document.getElementById('ordFlowNote');
        if (supplier) state.supplierId = supplier.value;
        if (warehouse) state.warehouseId = warehouse.value;
        if (ref) state.ref = ref.value.trim();
        if (note) state.note = note.value.trim();
    };

    OrdersPage._validateMobileOrderStep = async function validateMobileOrderStep(stepIndex) {
        this._syncMobileOrderInputs();
        const state = this._mobileOrderState;
        if (!state) return false;
        if (stepIndex === 0) {
            if (!state.supplierId) { toast('Choisissez un fournisseur.', 'error'); return false; }
            return this._saveMobileDraft({ silent: true, ensure: true, step: 1 });
        }
        if (stepIndex === 1) {
            if (!state.warehouseId) { toast('Choisissez le magasin de destination.', 'error'); return false; }
            return this._saveMobileDraft({ silent: true, ensure: true, step: 2 });
        }
        if (stepIndex === 2) {
            if (!state.lines.length) { toast('Ajoutez au moins un produit.', 'error'); return false; }
            const invalid = state.lines.find((line) => !line.productId || !line.warehouseId || !(safeNumber(line.quantity) > 0));
            if (invalid) { toast('Une ligne de commande est incomplète.', 'error'); return false; }
            return this._saveMobileDraft({ silent: true, ensure: true, step: 3 });
        }
        return true;
    };

    OrdersPage._markDraftDirty = function markDraftDirty() {
        if (!this._mobileOrderState) return;
        this._mobileOrderState.dirty = true;
        this._mobileOrderState.saveStatus = 'Modifications non enregistrées';
        this._updateDraftStatus();
        this._persistMobileDraftLocal();
        this._scheduleMobileDraftSave();
    };
    OrdersPage._scheduleMobileDraftSave = function scheduleMobileDraftSave(delay = 850) {
        global.clearTimeout(this._mobileDraftTimer);
        this._mobileDraftTimer = global.setTimeout(() => {
            if (this._mobileOrderState?.id && this._mobileOrderState?.dirty) this._saveMobileDraft({ silent: true });
        }, delay);
    };
    OrdersPage._updateDraftStatus = function updateDraftStatus(text) {
        if (text && this._mobileOrderState) this._mobileOrderState.saveStatus = text;
        const node = document.getElementById('ordDraftStatus');
        if (node && this._mobileOrderState) node.textContent = this._mobileOrderState.saveStatus;
    };
    OrdersPage._mobileDraftPayload = function mobileDraftPayload() {
        const state = this._mobileOrderState;
        return {
            supplierId: state.supplierId || null,
            draftWarehouseId: state.warehouseId || null,
            ref: state.ref || '',
            note: state.note || '',
            draftStep: state.step || 1,
            draftRevision: state.revision,
            lines: state.lines.map((line) => ({
                productId: Number(line.productId),
                warehouseId: Number(line.warehouseId || state.warehouseId),
                quantity: safeNumber(line.quantity),
                unitCost: safeNumber(line.unitCost),
                unit: line.unit || 'piece',
                supplierSku: line.supplierSku || '',
                note: line.note || ''
            }))
        };
    };

    OrdersPage._saveMobileDraft = async function saveMobileDraft(options = {}) {
        const state = this._mobileOrderState;
        if (!state) return false;
        this._syncMobileOrderInputs();
        if (options.step) state.step = Math.max(state.step, options.step);
        this._persistMobileDraftLocal();
        if (this._mobileDraftSaving) {
            this._mobileDraftQueued = true;
            await this._mobileDraftSaving;
            if (this._mobileDraftQueued) {
                this._mobileDraftQueued = false;
                return this._saveMobileDraft(options);
            }
            return true;
        }
        if (!state.supplierId && !state.id) {
            if (options.explicit) toast('Choisissez d’abord un fournisseur.', 'warning');
            return !options.ensure;
        }
        const payload = this._mobileDraftPayload();
        this._updateDraftStatus('Sauvegarde…');
        this._mobileDraftSaving = (async () => {
            try {
                let saved;
                if (!state.id) {
                    saved = await Api.orderCreate(Object.assign({}, payload, { draftMode: true }));
                    const oldKey = this._draftLocalKey('new');
                    state.id = saved.id;
                    state.orderNumber = saved.orderNumber || state.orderNumber;
                    try { global.localStorage?.removeItem(oldKey); } catch (_) {}
                } else {
                    saved = await Api.orderUpdate(Object.assign({ id: state.id, action: 'update' }, payload));
                }
                state.revision = Number(saved.draftRevision || state.revision + 1);
                state.step = Number(saved.draftStep || state.step);
                state.dirty = false;
                state.conflict = false;
                state.saveStatus = `Sauvegardé ${formatDate(saved.draftUpdatedAt || saved.updatedAt || new Date().toISOString())}`;
                this.editId = state.id;
                this._persistMobileDraftLocal();
                this._updateDraftStatus();
                const eyebrow = document.querySelector('.ord-flow-head .ord-mobile-eyebrow');
                if (eyebrow && state.orderNumber) eyebrow.textContent = state.orderNumber;
                if (options.explicit) toast('Brouillon enregistré sur le serveur.', 'success');
                return true;
            } catch (error) {
                state.saveStatus = error.status === 409 ? 'Conflit avec une version plus récente' : 'Sauvegarde serveur impossible';
                state.conflict = error.status === 409;
                this._updateDraftStatus();
                this._persistMobileDraftLocal();
                if (!options.silent || options.ensure) toast(error.message || 'Impossible d’enregistrer le brouillon.', 'error');
                if (error.status === 409) global.MageStockMobileUI?.announce?.('Le brouillon a été modifié depuis un autre appareil.', 'assertive');
                return false;
            } finally {
                this._mobileDraftSaving = null;
            }
        })();
        return this._mobileDraftSaving;
    };

    OrdersPage._updateMobileOrderFlowActions = function updateMobileOrderFlowActions() {
        if (!this._mobileFlow) return;
        const back = document.getElementById('btnOrdFlowBack');
        const next = document.getElementById('btnOrdFlowNext');
        if (back) back.textContent = this._mobileFlow.current === 0 ? 'Quitter' : 'Retour';
        if (next) {
            next.textContent = this._mobileFlow.current === 3 ? 'Envoyer pour validation' : 'Suivant';
            next.className = this._mobileFlow.current === 3 ? 'btn btn-success' : 'btn btn-primary';
        }
    };

    OrdersPage._renderMobileOrderLines = function renderMobileOrderLines() {
        const container = document.getElementById('ordFlowLines');
        const empty = document.getElementById('ordFlowLinesEmpty');
        const state = this._mobileOrderState;
        if (!container || !state) return;
        container.innerHTML = state.lines.map((line) => {
            const product = this._mobileProductById(line.productId);
            const warehouse = this._warehouseById(line.warehouseId || state.warehouseId);
            const image = this._mobileImage(product);
            return `
                <article class="ord-line-card">
                    <div class="ord-line-card__identity">
                        ${image ? `<img src="${escapeHtml(image)}" alt="">` : '<span class="ord-line-card__placeholder">📦</span>'}
                        <div><h4>${escapeHtml(product?.name || 'Produit inconnu')}</h4><p>${escapeHtml(product?.sku || 'Sans SKU')} · ${escapeHtml(warehouse?.name || 'Destination à préciser')}</p></div>
                    </div>
                    <dl><div><dt>Quantité</dt><dd>${safeNumber(line.quantity)} ${escapeHtml(line.unit || 'piece')}</dd></div><div><dt>Prix unitaire</dt><dd>${escapeHtml(formatMoney(line.unitCost))}</dd></div><div><dt>Total</dt><dd>${escapeHtml(formatMoney(safeNumber(line.quantity) * safeNumber(line.unitCost)))}</dd></div></dl>
                    ${line.supplierSku ? `<p class="ord-line-card__supplier">Réf. fournisseur : <code>${escapeHtml(line.supplierSku)}</code></p>` : ''}
                    <footer><button type="button" class="btn btn-secondary" data-order-line-action="edit" data-line-id="${escapeHtml(line.clientId)}">Modifier</button><button type="button" class="btn btn-ghost" data-order-line-action="remove" data-line-id="${escapeHtml(line.clientId)}">Retirer</button></footer>
                </article>`;
        }).join('');
        if (empty) empty.hidden = state.lines.length > 0;
    };

    OrdersPage._renderMobileOrderReview = function renderMobileOrderReview() {
        const container = document.getElementById('ordFlowReview');
        const state = this._mobileOrderState;
        if (!container || !state) return;
        const supplier = this._supplierById(state.supplierId);
        const warehouse = this._warehouseById(state.warehouseId);
        const total = state.lines.reduce((sum, line) => sum + safeNumber(line.quantity) * safeNumber(line.unitCost), 0);
        const warnings = [];
        state.lines.forEach((line) => {
            const product = this._mobileProductById(line.productId);
            if (!(safeNumber(line.unitCost) > 0)) warnings.push(`Prix non renseigné : ${product?.name || 'produit'}`);
            if (!line.supplierSku) warnings.push(`Référence fournisseur absente : ${product?.name || 'produit'}`);
        });
        container.innerHTML = `
            <dl class="ord-review-summary">
                <div><dt>Fournisseur</dt><dd>${escapeHtml(supplier?.name || '—')}</dd></div>
                <div><dt>Destination</dt><dd>${escapeHtml(warehouse?.name || '—')}</dd></div>
                <div><dt>Référence</dt><dd>${escapeHtml(state.ref || '—')}</dd></div>
                <div><dt>Produits</dt><dd>${state.lines.length}</dd></div>
                <div><dt>Total estimé</dt><dd>${escapeHtml(formatMoney(total))}</dd></div>
            </dl>
            ${state.note ? `<div class="ord-review-note"><strong>Instructions</strong><p>${escapeHtml(state.note)}</p></div>` : ''}
            <div class="ord-review-lines">${state.lines.map((line) => {
                const product = this._mobileProductById(line.productId);
                return `<div><span>${escapeHtml(product?.name || 'Produit')}</span><strong>${safeNumber(line.quantity)} ${escapeHtml(line.unit || '')} · ${escapeHtml(formatMoney(safeNumber(line.quantity) * safeNumber(line.unitCost)))}</strong></div>`;
            }).join('')}</div>
            ${warnings.length ? `<div class="ord-review-warning"><strong>Points à vérifier</strong><ul>${warnings.map((warning) => `<li>${escapeHtml(warning)}</li>`).join('')}</ul></div>` : '<div class="ord-review-ok">La commande est complète et prête à être envoyée.</div>'}`;
    };

    OrdersPage._sendMobileOrder = async function sendMobileOrder() {
        const state = this._mobileOrderState;
        if (!state || !state.supplierId || !state.warehouseId || !state.lines.length) {
            toast('La commande est incomplète.', 'error');
            return false;
        }
        const saved = await this._saveMobileDraft({ silent: false, ensure: true, step: 4 });
        if (!saved || !state.id) return false;
        if (!global.confirm('Envoyer ce bon de commande pour validation ? Il ne sera plus modifiable.')) return false;
        try {
            await Api.orderStatus(state.id, 'sent');
            this._clearDraftLocal(state.id);
            toast('Bon de commande envoyé pour validation.', 'success');
            await this.loadData();
            this._mobileOrderState = null;
            this.showList();
            return true;
        } catch (error) {
            toast(error.message || 'Impossible d’envoyer la commande.', 'error');
            return false;
        }
    };

    OrdersPage._openMobileSupplierCreator = function openMobileSupplierCreator(trigger) {
        if (!global.ActionSheet) return;
        const form = document.createElement('form');
        form.className = 'ord-sheet-form';
        form.innerHTML = `
            <div class="ord-mobile-field"><label for="ordNewSupplierName">Nom *</label><input id="ordNewSupplierName" class="form-input" required autocomplete="organization"></div>
            <div class="ord-mobile-field"><label for="ordNewSupplierContact">Contact</label><input id="ordNewSupplierContact" class="form-input" autocomplete="name"></div>
            <div class="ord-mobile-field"><label for="ordNewSupplierEmail">Email</label><input id="ordNewSupplierEmail" type="email" class="form-input" autocomplete="email"></div>
            <div class="ord-mobile-field"><label for="ordNewSupplierPhone">Téléphone</label><input id="ordNewSupplierPhone" type="tel" class="form-input" autocomplete="tel"></div>`;
        global.ActionSheet.open({
            title: 'Nouveau fournisseur', description: 'Création rapide depuis le bon de commande.', content: form, trigger,
            actions: [
                { label: 'Annuler', className: 'btn btn-secondary' },
                { label: 'Créer', className: 'btn btn-primary', close: false, onClick: async ({ close }) => {
                    if (!form.reportValidity()) return false;
                    try {
                        const supplier = await Api.supplierAdd({
                            name: form.querySelector('#ordNewSupplierName').value.trim(),
                            contact: form.querySelector('#ordNewSupplierContact').value.trim(),
                            email: form.querySelector('#ordNewSupplierEmail').value.trim(),
                            phone: form.querySelector('#ordNewSupplierPhone').value.trim()
                        });
                        this.suppliers.push(supplier);
                        const select = document.getElementById('ordFlowSupplier');
                        if (select) {
                            const option = document.createElement('option');
                            option.value = supplier.id;
                            option.textContent = supplier.name;
                            select.appendChild(option);
                            select.value = String(supplier.id);
                            this._mobileOrderState.supplierId = String(supplier.id);
                            this._markDraftDirty();
                        }
                        toast('Fournisseur créé.', 'success');
                        close('created');
                    } catch (error) { toast(error.message || 'Création impossible.', 'error'); }
                    return false;
                } }
            ]
        });
    };

    OrdersPage._productAllowedForWarehouse = function productAllowedForWarehouse(product, warehouseId) {
        if (!warehouseId) return true;
        if (global.StockCascade?.productInWarehouse) return global.StockCascade.productInWarehouse(product, warehouseId, { includeUnrestricted: true });
        const allowed = Array.isArray(product.allowedWarehouses) ? product.allowedWarehouses.map(String) : [];
        return !allowed.length || allowed.includes(String(warehouseId));
    };

    OrdersPage._openMobileProductPicker = function openMobileProductPicker(trigger) {
        if (!global.ActionSheet) return;
        const shell = document.createElement('div');
        shell.className = 'ord-product-picker';
        shell.innerHTML = `<label class="ord-mobile-search"><span class="sr-only">Rechercher un produit</span><input type="search" class="form-input" id="ordProductPickerSearch" placeholder="Nom, SKU, code-barres…"></label><div id="ordProductPickerResults" class="ord-product-picker__grid"></div>`;
        const render = () => {
            const query = shell.querySelector('#ordProductPickerSearch').value.trim().toLocaleLowerCase('fr');
            const products = (this.products || []).filter((product) => {
                if (!this._productAllowedForWarehouse(product, this._mobileOrderState?.warehouseId)) return false;
                if (!query) return true;
                return [product.name, product.sku, product.barcode, product.category].filter(Boolean).join(' ').toLocaleLowerCase('fr').includes(query);
            }).slice(0, 80);
            shell.querySelector('#ordProductPickerResults').innerHTML = products.map((product) => {
                const image = this._mobileImage(product);
                return `<button type="button" class="ord-product-choice" data-product-id="${escapeHtml(product.id)}">${image ? `<img src="${escapeHtml(image)}" alt="">` : '<span>📦</span>'}<div><strong>${escapeHtml(product.name)}</strong><small>${escapeHtml(product.sku || 'Sans SKU')} · ${escapeHtml(product.category || 'Sans catégorie')}</small></div></button>`;
            }).join('') || '<div class="ord-mobile-empty">Aucun produit trouvé.</div>';
        };
        const panel = global.ActionSheet.open({ title: 'Ajouter un produit', description: this._warehouseById(this._mobileOrderState?.warehouseId)?.name || 'Catalogue produits', content: shell, trigger });
        shell.querySelector('#ordProductPickerSearch')?.addEventListener('input', debounce(render, 100));
        shell.addEventListener('click', (event) => {
            const choice = event.target.closest('[data-product-id]');
            if (!choice) return;
            const product = this._mobileProductById(choice.dataset.productId);
            panel.close('selected');
            global.setTimeout(() => this._openMobileLineEditor(product, null, trigger), 210);
        });
        render();
    };

    OrdersPage._unitOptionsHTML = function unitOptionsHTML(selected) {
        const values = new Map(UNIT_OPTIONS);
        if (selected && !values.has(selected)) values.set(selected, selected);
        return [...values.entries()].map(([value, label]) => `<option value="${escapeHtml(value)}" ${String(value) === String(selected) ? 'selected' : ''}>${escapeHtml(label)}</option>`).join('');
    };

    OrdersPage._openMobileLineEditor = function openMobileLineEditor(product, line, trigger) {
        if (!product || !global.ActionSheet) return;
        const offer = typeof this._pickSupplierOffer === 'function'
            ? this._pickSupplierOffer(product, this._mobileOrderState?.supplierId, this._mobileOrderState?.warehouseId)
            : null;
        const draft = line || {
            clientId: uid('line'), productId: String(product.id), warehouseId: String(this._mobileOrderState?.warehouseId || ''),
            quantity: safeNumber(offer?.minOrderQty, 1) || 1,
            unitCost: safeNumber(offer?.purchasePrice ?? product.cost ?? product.cump),
            unit: offer?.packagingLabel || product.packBaseUnit || product.unitType || 'piece',
            supplierSku: offer?.supplierSku || product.sku || '', note: ''
        };
        const form = document.createElement('form');
        form.className = 'ord-sheet-form';
        form.innerHTML = `
            <div class="ord-sheet-product"><strong>${escapeHtml(product.name)}</strong><span>${escapeHtml(product.sku || 'Sans SKU')}</span></div>
            <div class="ord-sheet-grid">
                <div class="ord-mobile-field"><label for="ordLineQty">Quantité *</label><input id="ordLineQty" type="number" min="0.01" step="0.01" inputmode="decimal" class="form-input" required value="${escapeHtml(draft.quantity)}"></div>
                <div class="ord-mobile-field"><label for="ordLineUnit">Unité</label><select id="ordLineUnit" class="form-select">${this._unitOptionsHTML(draft.unit)}</select></div>
                <div class="ord-mobile-field"><label for="ordLineCost">Prix unitaire (${escapeHtml(global.Locale?.currencyCode?.() || 'EUR')})</label><input id="ordLineCost" type="number" min="0" step="0.0001" inputmode="decimal" class="form-input" value="${escapeHtml(draft.unitCost)}"></div>
                <div class="ord-mobile-field"><label for="ordLineSupplierSku">Référence fournisseur</label><input id="ordLineSupplierSku" class="form-input" value="${escapeHtml(draft.supplierSku)}"></div>
            </div>
            <div class="ord-mobile-field"><label for="ordLineNote">Note de ligne</label><input id="ordLineNote" class="form-input" value="${escapeHtml(draft.note || '')}"></div>`;
        global.ActionSheet.open({
            title: line ? 'Modifier la ligne' : 'Ajouter à la commande',
            description: this._warehouseById(draft.warehouseId)?.name || 'Destination de la commande',
            content: form, trigger,
            actions: [
                { label: 'Annuler', className: 'btn btn-secondary' },
                { label: line ? 'Enregistrer' : 'Ajouter', className: 'btn btn-primary', close: false, onClick: ({ close }) => {
                    if (!form.reportValidity()) return false;
                    const updated = {
                        clientId: draft.clientId,
                        productId: String(product.id),
                        warehouseId: String(this._mobileOrderState.warehouseId),
                        quantity: safeNumber(form.querySelector('#ordLineQty').value),
                        unitCost: safeNumber(form.querySelector('#ordLineCost').value),
                        unit: form.querySelector('#ordLineUnit').value || 'piece',
                        supplierSku: form.querySelector('#ordLineSupplierSku').value.trim(),
                        note: form.querySelector('#ordLineNote').value.trim()
                    };
                    const existingIndex = this._mobileOrderState.lines.findIndex((item) => item.clientId === updated.clientId);
                    if (existingIndex >= 0) this._mobileOrderState.lines.splice(existingIndex, 1, updated);
                    else this._mobileOrderState.lines.push(updated);
                    this._markDraftDirty();
                    this._renderMobileOrderLines();
                    close('saved');
                    return false;
                } }
            ]
        });
    };

    OrdersPage._openMobileProductCreator = function openMobileProductCreator(trigger) {
        if (!global.ActionSheet) return;
        const form = document.createElement('form');
        form.className = 'ord-sheet-form';
        form.innerHTML = `
            <div class="ord-mobile-field"><label for="ordNewProductName">Nom *</label><input id="ordNewProductName" class="form-input" required></div>
            <div class="ord-sheet-grid"><div class="ord-mobile-field"><label for="ordNewProductSku">SKU</label><input id="ordNewProductSku" class="form-input"></div><div class="ord-mobile-field"><label for="ordNewProductCategory">Catégorie</label><input id="ordNewProductCategory" class="form-input" list="ordNewProductCategories"></div></div>
            <datalist id="ordNewProductCategories">${(this.categories || []).map((category) => `<option value="${escapeHtml(typeof category === 'object' ? (category.name || category.label || '') : category)}"></option>`).join('')}</datalist>
            <div class="ord-sheet-grid"><div class="ord-mobile-field"><label for="ordNewProductUnit">Unité</label><select id="ordNewProductUnit" class="form-select">${this._unitOptionsHTML('piece')}</select></div><div class="ord-mobile-field"><label for="ordNewProductCost">Prix d’achat</label><input id="ordNewProductCost" type="number" min="0" step="0.0001" inputmode="decimal" class="form-input" value="0"></div></div>
            <div class="ord-mobile-field"><label for="ordNewProductSupplierSku">Référence fournisseur</label><input id="ordNewProductSupplierSku" class="form-input"></div>`;
        global.ActionSheet.open({
            title: 'Nouveau produit', description: 'Le produit est créé immédiatement dans le catalogue.', content: form, trigger,
            actions: [
                { label: 'Annuler', className: 'btn btn-secondary' },
                { label: 'Créer et ajouter', className: 'btn btn-primary', close: false, onClick: async ({ close }) => {
                    if (!form.reportValidity()) return false;
                    const unit = form.querySelector('#ordNewProductUnit').value || 'piece';
                    const cost = safeNumber(form.querySelector('#ordNewProductCost').value);
                    const supplierSku = form.querySelector('#ordNewProductSupplierSku').value.trim();
                    try {
                        const product = await Api.prodAdd({
                            name: form.querySelector('#ordNewProductName').value.trim(),
                            sku: form.querySelector('#ordNewProductSku').value.trim(),
                            category: form.querySelector('#ordNewProductCategory').value.trim(),
                            unitType: unit, packBaseUnit: unit, cost, cump: cost, costByUnit: [],
                            supplier: this._mobileOrderState.supplierId,
                            supplierLinks: this._mobileOrderState.supplierId ? [{
                                supplierId: this._mobileOrderState.supplierId, supplierSku, purchasePrice: cost,
                                packagingLabel: unit, packagingQty: 1, minOrderQty: 1, leadTimeDays: 0,
                                discountRate: 0, preferred: true,
                                allowedWarehouses: this._mobileOrderState.warehouseId ? [String(this._mobileOrderState.warehouseId)] : []
                            }] : [],
                            allowedWarehouses: this._mobileOrderState.warehouseId ? [String(this._mobileOrderState.warehouseId)] : [],
                            trackLot: false, trackExpiry: false, image: '', description: ''
                        });
                        this.products.push(product);
                        close('created');
                        global.setTimeout(() => this._openMobileLineEditor(product, {
                            clientId: uid('line'), productId: String(product.id), warehouseId: String(this._mobileOrderState.warehouseId),
                            quantity: 1, unitCost: cost, unit, supplierSku, note: ''
                        }, trigger), 210);
                    } catch (error) { toast(error.message || 'Création du produit impossible.', 'error'); }
                    return false;
                } }
            ]
        });
    };

    // ---------------------------------------------------------------------
    // Mobile receiving queue, scanner and mandatory review
    // ---------------------------------------------------------------------
    OrdersPage._receiveLocalKey = function receiveLocalKey(orderId) { return `magestock:orders:receive:${orderId}`; };
    OrdersPage._receiveFingerprint = function receiveFingerprint(order) {
        return JSON.stringify((order?.lines || []).map((line) => [line.productId, line.warehouseId, safeNumber(line.quantity), safeNumber(line.quantityReceived)]));
    };
    OrdersPage._persistReceiveDraftLocal = function persistReceiveDraftLocal() {
        const state = this._receiveState;
        if (!state?.orderId) return;
        try { global.localStorage?.setItem(this._receiveLocalKey(state.orderId), JSON.stringify({ savedAt: new Date().toISOString(), fingerprint: state.fingerprint, entries: state.entries })); } catch (_) {}
    };
    OrdersPage._loadReceiveDraftLocal = function loadReceiveDraftLocal(order) {
        try {
            const raw = global.localStorage?.getItem(this._receiveLocalKey(order.id));
            if (!raw) return {};
            const draft = JSON.parse(raw);
            if (draft.fingerprint !== this._receiveFingerprint(order)) return {};
            const entries = draft.entries || {};
            Object.keys(entries).forEach((key) => {
                const line = order.lines?.[Number(key)];
                const remaining = line ? Math.max(0, safeNumber(line.quantity) - safeNumber(line.quantityReceived)) : 0;
                if (!line || !(safeNumber(entries[key].quantityReceived) > 0) || safeNumber(entries[key].quantityReceived) > remaining) delete entries[key];
            });
            return entries;
        } catch (_) { return {}; }
    };
    OrdersPage._clearReceiveDraftLocal = function clearReceiveDraftLocal(orderId) {
        try { global.localStorage?.removeItem(this._receiveLocalKey(orderId)); } catch (_) {}
    };

    OrdersPage.showReceive = function showReceiveResponsive(orderId) {
        if (!this.isOrdersTerrainMode()) return original.showReceive.call(this, orderId);
        this._cleanupOrdersMobile();
        this._view = 'receive';
        this.receiveId = orderId;
        const order = (this.orders || []).find((item) => String(item.id) === String(orderId));
        if (!order) return this.showList();
        if ((order.validationStatus || 'pending') !== 'approved') { toast('La commande doit être validée avant réception.', 'error'); return this.showList(); }
        if (!['sent', 'partial'].includes(order.status)) { toast('Cette commande ne peut pas être réceptionnée.', 'error'); return this.showList(); }
        this._receiveState = {
            orderId: order.id,
            fingerprint: this._receiveFingerprint(order),
            entries: this._loadReceiveDraftLocal(order),
            query: '', filter: 'remaining'
        };
        const root = document.getElementById('ordersRoot');
        if (!root) return;
        root.innerHTML = this._mobileReceiveHTML(order);
        this._bindMobileReceive(order);
        this._renderReceiveQueue(order);
    };

    OrdersPage._mobileReceiveHTML = function mobileReceiveHTML(order) {
        const supplier = this._supplierById(order.supplierId);
        const totalLines = (order.lines || []).length;
        const completeLines = (order.lines || []).filter((line) => safeNumber(line.quantityReceived) >= safeNumber(line.quantity)).length;
        return `
            <section class="ord-receive-page" aria-labelledby="ordReceiveTitle">
                <header class="ord-flow-head ord-receive-head">
                    <button type="button" class="btn btn-ghost" id="btnOrdReceiveClose" aria-label="Retour aux commandes">←</button>
                    <div><span class="ord-mobile-eyebrow">${escapeHtml(order.orderNumber || '')}</span><h2 id="ordReceiveTitle">Réception terrain</h2><p>${escapeHtml(supplier?.name || 'Fournisseur')} · ${completeLines}/${totalLines} lignes complètes</p></div>
                    <button type="button" class="btn btn-primary" id="btnOrdReceiveScan">Scanner</button>
                </header>
                <div class="ord-receive-summary"><div><strong id="ordReceivePreparedCount">${Object.keys(this._receiveState.entries).length}</strong><span>Préparées</span></div><div><strong>${totalLines - completeLines}</strong><span>À terminer</span></div><div><strong>${completeLines}</strong><span>Complètes</span></div></div>
                <div class="ord-receive-tools">
                    <label class="ord-mobile-search"><span class="sr-only">Rechercher un produit de la commande</span><input type="search" id="ordReceiveSearch" class="form-input" placeholder="Produit, SKU ou code-barres…"></label>
                    <select id="ordReceiveFilter" class="form-select" aria-label="Filtrer les lignes"><option value="remaining">À recevoir</option><option value="prepared">Préparées</option><option value="all">Toutes</option></select>
                </div>
                <div id="ordReceiveQueue" class="ord-receive-grid"></div>
                <div id="ordReceiveEmpty" class="ord-mobile-empty" hidden>Aucune ligne ne correspond à la recherche.</div>
                <footer class="ord-receive-actions" data-ms-sticky-actions><button type="button" class="btn btn-secondary" id="btnOrdReceiveManualScan">Saisir un code</button><button type="button" class="btn btn-success" id="btnOrdReceiveReview">Revoir la réception</button></footer>
            </section>`;
    };

    OrdersPage._bindMobileReceive = function bindMobileReceive(order) {
        document.getElementById('btnOrdReceiveClose')?.addEventListener('click', () => { this._persistReceiveDraftLocal(); this.showList(); });
        document.getElementById('btnOrdReceiveScan')?.addEventListener('click', (event) => this._openReceiveScanner(order, event.currentTarget));
        document.getElementById('btnOrdReceiveManualScan')?.addEventListener('click', (event) => this._openReceiveCodeEntry(order, event.currentTarget));
        document.getElementById('btnOrdReceiveReview')?.addEventListener('click', () => this._reviewMobileReception(order));
        const search = document.getElementById('ordReceiveSearch');
        search?.addEventListener('input', debounce(() => { this._receiveState.query = search.value.trim(); this._renderReceiveQueue(order); }, 120));
        document.getElementById('ordReceiveFilter')?.addEventListener('change', (event) => { this._receiveState.filter = event.target.value; this._renderReceiveQueue(order); });
        document.getElementById('ordReceiveQueue')?.addEventListener('click', (event) => {
            const button = event.target.closest('[data-receive-line]');
            if (!button) return;
            const lineIndex = Number(button.dataset.receiveLine);
            this._openReceiveLineEditor(order, lineIndex, button);
        });
        this._updateReceivePreparedCount();
    };

    OrdersPage._receiveLineMatches = function receiveLineMatches(order, line, lineIndex) {
        const product = this._mobileProductById(line.productId);
        const query = (this._receiveState.query || '').toLocaleLowerCase('fr');
        const prepared = !!this._receiveState.entries[lineIndex];
        const remaining = Math.max(0, safeNumber(line.quantity) - safeNumber(line.quantityReceived));
        if (this._receiveState.filter === 'remaining' && remaining <= 0) return false;
        if (this._receiveState.filter === 'prepared' && !prepared) return false;
        if (!query) return true;
        return [product?.name, product?.sku, product?.barcode, line.supplierSku].filter(Boolean).join(' ').toLocaleLowerCase('fr').includes(query);
    };

    OrdersPage._renderReceiveQueue = function renderReceiveQueue(order) {
        const queue = document.getElementById('ordReceiveQueue');
        const empty = document.getElementById('ordReceiveEmpty');
        if (!queue) return;
        const rows = (order.lines || []).map((line, lineIndex) => ({ line, lineIndex })).filter(({ line, lineIndex }) => this._receiveLineMatches(order, line, lineIndex));
        queue.innerHTML = rows.map(({ line, lineIndex }) => {
            const product = this._mobileProductById(line.productId);
            const warehouse = this._warehouseById(line.warehouseId);
            const image = this._mobileImage(product);
            const remaining = Math.max(0, safeNumber(line.quantity) - safeNumber(line.quantityReceived));
            const prepared = this._receiveState.entries[lineIndex];
            return `
                <article class="ord-receive-card ${prepared ? 'is-prepared' : ''} ${remaining <= 0 ? 'is-complete' : ''}">
                    <header><div class="ord-line-card__identity">${image ? `<img src="${escapeHtml(image)}" alt="">` : '<span class="ord-line-card__placeholder">📦</span>'}<div><h3>${escapeHtml(product?.name || 'Produit inconnu')}</h3><p>${escapeHtml(product?.sku || 'Sans SKU')} · ${escapeHtml(warehouse?.name || 'Magasin')}</p></div></div>${prepared ? '<span class="ord-mobile-badge" data-state="info">Préparée</span>' : remaining <= 0 ? '<span class="ord-mobile-badge" data-state="success">Complète</span>' : ''}</header>
                    <dl><div><dt>Commandé</dt><dd>${safeNumber(line.quantity)} ${escapeHtml(line.unit || '')}</dd></div><div><dt>Déjà reçu</dt><dd>${safeNumber(line.quantityReceived)}</dd></div><div><dt>Reste</dt><dd>${remaining}</dd></div>${prepared ? `<div><dt>Préparé</dt><dd>${safeNumber(prepared.quantityReceived)}</dd></div>` : ''}</dl>
                    <button type="button" class="btn ${prepared ? 'btn-secondary' : 'btn-primary'}" data-receive-line="${lineIndex}" ${remaining <= 0 ? 'disabled' : ''}>${prepared ? 'Modifier la préparation' : 'Préparer la réception'}</button>
                </article>`;
        }).join('');
        if (empty) empty.hidden = rows.length > 0;
    };

    OrdersPage._openReceiveLineEditor = function openReceiveLineEditor(order, lineIndex, trigger) {
        if (!global.ActionSheet) return;
        const line = order.lines?.[lineIndex];
        if (!line) return;
        const product = this._mobileProductById(line.productId);
        const remaining = Math.max(0, safeNumber(line.quantity) - safeNumber(line.quantityReceived));
        const prepared = this._receiveState.entries[lineIndex] || {
            lineIndex, quantityReceived: remaining, lotNumber: '', expiryDate: '', unitCost: safeNumber(line.unitCost)
        };
        const form = document.createElement('form');
        form.className = 'ord-sheet-form';
        form.innerHTML = `
            <div class="ord-sheet-product"><strong>${escapeHtml(product?.name || 'Produit')}</strong><span>Reste à recevoir : ${remaining} ${escapeHtml(line.unit || '')}</span></div>
            <div class="ord-mobile-field"><label for="ordReceiveQty">Quantité reçue *</label><input id="ordReceiveQty" type="number" min="0.01" max="${remaining}" step="0.01" inputmode="decimal" class="form-input" required value="${escapeHtml(prepared.quantityReceived)}"></div>
            <div class="ord-sheet-grid"><div class="ord-mobile-field"><label for="ordReceiveLot">N° de lot</label><input id="ordReceiveLot" class="form-input" value="${escapeHtml(prepared.lotNumber || '')}"></div><div class="ord-mobile-field"><label for="ordReceiveExpiry">Date limite</label><input id="ordReceiveExpiry" type="date" class="form-input" value="${escapeHtml(prepared.expiryDate || '')}"></div></div>
            <div class="ord-mobile-field"><label for="ordReceiveCost">Prix réel unitaire (${escapeHtml(global.Locale?.currencyCode?.() || 'EUR')})</label><input id="ordReceiveCost" type="number" min="0" step="0.0001" inputmode="decimal" class="form-input" value="${escapeHtml(prepared.unitCost)}"></div>`;
        const actions = [];
        if (this._receiveState.entries[lineIndex]) actions.push({ label: 'Retirer', className: 'btn btn-danger', close: false, onClick: ({ close }) => {
            delete this._receiveState.entries[lineIndex];
            this._persistReceiveDraftLocal(); this._renderReceiveQueue(order); this._updateReceivePreparedCount(); close('removed'); return false;
        } });
        actions.push({ label: 'Annuler', className: 'btn btn-secondary' });
        actions.push({ label: 'Préparer', className: 'btn btn-primary', close: false, onClick: ({ close }) => {
            if (!form.reportValidity()) return false;
            const quantity = safeNumber(form.querySelector('#ordReceiveQty').value);
            if (!(quantity > 0) || quantity > remaining) { toast('La quantité dépasse le reliquat.', 'error'); return false; }
            this._receiveState.entries[lineIndex] = {
                lineIndex, quantityReceived: quantity,
                lotNumber: form.querySelector('#ordReceiveLot').value.trim(),
                expiryDate: form.querySelector('#ordReceiveExpiry').value,
                unitCost: safeNumber(form.querySelector('#ordReceiveCost').value)
            };
            this._persistReceiveDraftLocal();
            this._renderReceiveQueue(order);
            this._updateReceivePreparedCount();
            close('prepared');
            return false;
        } });
        global.ActionSheet.open({ title: 'Préparer la ligne', description: product?.name || 'Produit', content: form, trigger, actions });
    };

    OrdersPage._updateReceivePreparedCount = function updateReceivePreparedCount() {
        const total = Object.keys(this._receiveState?.entries || {}).length;
        const node = document.getElementById('ordReceivePreparedCount');
        if (node) node.textContent = String(total);
        const button = document.getElementById('btnOrdReceiveReview');
        if (button) {
            button.disabled = total === 0;
            button.textContent = total ? `Revoir la réception (${total})` : 'Revoir la réception';
        }
    };

    OrdersPage._findReceiveLinesByCode = function findReceiveLinesByCode(order, code) {
        const normalized = String(code || '').trim();
        if (!normalized) return [];
        const productIds = new Set((this.products || []).filter((product) => this._mobileProductCode(product).includes(normalized)).map((product) => String(product.id)));
        return (order.lines || []).map((line, lineIndex) => ({ line, lineIndex })).filter(({ line }) => productIds.has(String(line.productId)) && safeNumber(line.quantityReceived) < safeNumber(line.quantity));
    };


    OrdersPage._findReceiveLinesByProduct = function findReceiveLinesByProduct(order, productId) {
        return (order.lines || []).map((line, lineIndex) => ({ line, lineIndex })).filter(({ line }) =>
            String(line.productId) === String(productId) && safeNumber(line.quantityReceived) < safeNumber(line.quantity)
        );
    };

    OrdersPage._handleReceiveProduct = function handleReceiveProduct(order, product, trigger) {
        const matches = this._findReceiveLinesByProduct(order, product?.id);
        if (!matches.length) {
            toast(`${product?.name || 'Ce produit'} n’a plus de quantité à réceptionner sur cette commande.`, 'error');
            return false;
        }
        if (matches.length === 1) {
            this._openReceiveLineEditor(order, matches[0].lineIndex, trigger);
            return true;
        }
        if (!global.ActionSheet) return false;
        const content = document.createElement('div');
        content.className = 'ord-code-matches';
        content.innerHTML = matches.map(({ line, lineIndex }) => {
            const warehouse = this._warehouseById(line.warehouseId);
            return `<button type="button" class="ord-product-choice" data-match-line="${lineIndex}"><span>📦</span><div><strong>${escapeHtml(product?.name || 'Produit')}</strong><small>${escapeHtml(warehouse?.name || 'Magasin')} · reste ${Math.max(0, safeNumber(line.quantity) - safeNumber(line.quantityReceived))}</small></div></button>`;
        }).join('');
        const panel = global.ActionSheet.open({ title: 'Choisir la ligne', description: 'Le même produit figure plusieurs fois sur la commande.', content, trigger });
        content.addEventListener('click', event => {
            const choice = event.target.closest('[data-match-line]');
            if (!choice) return;
            panel.close('selected');
            global.setTimeout(() => this._openReceiveLineEditor(order, Number(choice.dataset.matchLine), trigger), 210);
        });
        return true;
    };

    OrdersPage._handleReceiveCode = function handleReceiveCode(order, code, trigger) {
        const normalized = String(code || '').trim();
        if (!normalized) return false;
        const product = (this.products || []).find(item => this._mobileProductCode(item).includes(normalized));
        if (!product) {
            toast('Ce code ne correspond à aucun produit restant sur la commande.', 'error');
            return false;
        }
        return this._handleReceiveProduct(order, product, trigger);
    };

    OrdersPage._openReceiveCodeEntry = function openReceiveCodeEntry(order, trigger) {
        if (!global.ActionSheet) return;
        const form = document.createElement('form');
        form.className = 'ord-sheet-form';
        form.innerHTML = '<div class="ord-mobile-field"><label for="ordReceiveCode">Code-barres, EAN ou SKU</label><input id="ordReceiveCode" class="form-input" inputmode="text" autocomplete="off" autocapitalize="off" spellcheck="false" required autofocus></div>';
        global.ActionSheet.open({ title: 'Saisir un code', description: 'Rechercher une ligne de la commande.', content: form, trigger, actions: [
            { label: 'Annuler', className: 'btn btn-secondary' },
            { label: 'Rechercher', className: 'btn btn-primary', close: false, onClick: ({ close }) => {
                if (!form.reportValidity()) return false;
                const found = this._handleReceiveCode(order, form.querySelector('#ordReceiveCode').value, trigger);
                if (found) close('found');
                return false;
            } }
        ] });
    };

    OrdersPage._openReceiveScanner = function openReceiveScanner(order, trigger) {
        if (!global.MageStockScannerConsumer?.open) return this._openReceiveCodeEntry(order, trigger);
        if (this._receiveScannerConsumer && !this._receiveScannerConsumer.closed) return this._receiveScannerConsumer;

        const remainingLines = (order.lines || []).map((line, lineIndex) => ({ ...line, lineIndex }))
            .filter(line => safeNumber(line.quantityReceived) < safeNumber(line.quantity));
        const allowedProductIds = [...new Set(remainingLines.map(line => String(line.productId)))];

        this._receiveScannerConsumer = global.MageStockScannerConsumer.open({
            title: 'Scanner la réception',
            description: order.orderNumber || 'Bon de commande',
            context: 'order-receiving',
            owner: `order-receiving-${order.id || 'current'}`,
            mode: 'auto',
            trigger,
            resolverOptions: () => ({
                products: this.products || [],
                lines: remainingLines,
                allowedProductIds
            }),
            onResolved: async (result, handle) => {
                await handle.stop('order-receiving-resolved');
                this._receiveScannerConsumer = null;
                handle.close('resolved');
                global.setTimeout(() => this._handleReceiveProduct(order, result.product, trigger), 210);
                return false;
            },
            onOutOfScope: result => {
                toast(`${result?.product?.name || 'Ce produit'} ne fait pas partie des lignes restant à réceptionner.`, 'error');
            },
            onNotFound: result => {
                toast(`Code non reconnu${result?.raw ? ` : ${result.raw}` : ''}`, 'error');
            },
            onClose: () => { this._receiveScannerConsumer = null; },
            onError: error => console.error('[Orders receive scanner]', error)
        });
        return this._receiveScannerConsumer;
    };

    OrdersPage._receivePayloadLines = function receivePayloadLines() {
        return Object.values(this._receiveState?.entries || {}).map((entry) => ({
            lineIndex: Number(entry.lineIndex), quantityReceived: safeNumber(entry.quantityReceived),
            lotNumber: entry.lotNumber || '', expiryDate: entry.expiryDate || '', unitCost: safeNumber(entry.unitCost)
        })).sort((a, b) => a.lineIndex - b.lineIndex);
    };

    OrdersPage._reviewMobileReception = async function reviewMobileReception(order) {
        const lines = this._receivePayloadLines();
        if (!lines.length) { toast('Préparez au moins une ligne.', 'error'); return; }
        const button = document.getElementById('btnOrdReceiveReview');
        if (button) button.disabled = true;
        try {
            const review = await Api.orderReceiveReview({ id: order.id, lines });
            this._openReceiveReview(order, review, lines, button);
        } catch (error) {
            toast(error.message || 'La revue de réception a échoué.', 'error');
            if (button) button.disabled = false;
        }
    };

    OrdersPage._openReceiveReview = function openReceiveReview(order, review, lines, trigger) {
        if (!global.MobileDetailPage) return;
        const content = document.createElement('div');
        content.className = 'ord-receive-review';
        content.innerHTML = `
            <div class="ord-review-status" data-state="${review.willClose ? 'success' : 'warning'}"><strong>${review.willClose ? 'Réception complète' : 'Réception partielle'}</strong><span>${review.willClose ? 'La commande sera clôturée après confirmation.' : 'Un reliquat restera à réceptionner.'}</span></div>
            <dl class="ord-review-summary"><div><dt>Lignes préparées</dt><dd>${review.lineCount}</dd></div><div><dt>Quantité reçue</dt><dd>${safeNumber(review.totalQuantity)}</dd></div><div><dt>Valeur réceptionnée</dt><dd>${escapeHtml(formatMoney(review.totalValue))}</dd></div><div><dt>Statut après validation</dt><dd>${review.willClose ? 'Reçu' : 'Partiel'}</dd></div></dl>
            ${(review.warnings || []).length ? `<div class="ord-review-warning"><strong>Points à vérifier</strong><ul>${review.warnings.map((warning) => `<li>${escapeHtml(warning)}</li>`).join('')}</ul></div>` : ''}
            <div class="ord-review-receive-lines">${(review.lines || []).map((line) => `<article><div><strong>${escapeHtml(line.productName)}</strong><span>${escapeHtml(line.warehouseName)} · ${escapeHtml(line.sku || '')}</span></div><dl><div><dt>Cette réception</dt><dd>${safeNumber(line.receivedNow)} ${escapeHtml(line.unit)}</dd></div><div><dt>Après validation</dt><dd>${safeNumber(line.receivedAfter)} / ${safeNumber(line.ordered)}</dd></div><div><dt>Reste</dt><dd>${safeNumber(line.remainingAfter)}</dd></div><div><dt>Valeur</dt><dd>${escapeHtml(formatMoney(line.lineValue))}</dd></div></dl>${line.lotNumber || line.expiryDate ? `<p>${line.lotNumber ? `Lot ${escapeHtml(line.lotNumber)}` : ''}${line.lotNumber && line.expiryDate ? ' · ' : ''}${line.expiryDate ? `Date ${escapeHtml(line.expiryDate)}` : ''}</p>` : ''}</article>`).join('')}</div>`;
        this._receiveReviewPanel = global.MobileDetailPage.open({
            title: 'Revue avant application', description: order.orderNumber || 'Réception', content, trigger,
            className: 'ord-receive-review-page',
            onClose: () => { this._receiveReviewPanel = null; this._updateReceivePreparedCount(); },
            actions: [
                { label: 'Modifier', className: 'btn btn-secondary' },
                { label: review.willClose ? 'Appliquer et clôturer' : 'Appliquer la réception', className: 'btn btn-success', close: false, onClick: async ({ close, panel }) => {
                    const confirmButton = panel.querySelector('.btn-success');
                    if (confirmButton) confirmButton.disabled = true;
                    try {
                        await Api.orderReceive({ id: order.id, lines, reviewToken: review.reviewToken });
                        this._clearReceiveDraftLocal(order.id);
                        toast(review.willClose ? 'Réception appliquée et commande clôturée.' : 'Réception partielle appliquée.', 'success');
                        close('applied');
                        await this.loadData();
                        this._receiveState = null;
                        this.showList();
                    } catch (error) {
                        toast(error.message || 'Impossible d’appliquer la réception.', 'error');
                        if (confirmButton) confirmButton.disabled = false;
                        if (error.status === 409) {
                            close('stale');
                            await this.loadData();
                            this.showReceive(order.id);
                        }
                    }
                    return false;
                } }
            ]
        });
    };

})(window, document);
