/**
 * Fichier  : assets/js/pages/movements-terrain.js
 * Auteur   : KREMER Régis
 * Version  : 2.6.90
 * Date     : 2026-07-20
 * Description : Extension mobile/tablette des Mouvements — assistant conditionnel, scanner, cartes et filtres.
 *
 * Changelog :
 *   v2.6.79 (2026-07-17) — Première version MOVEMENTS TERRAIN FLOW3.
 *   v2.6.90 (2026-07-20) — SCANNER CONSUMERS MIGRATION3 : suppression du cycle caméra terrain local.
 */

(function movementsTerrainFlow(global, document) {
    'use strict';

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

    const original = {
        init: MvPage.init,
        renderTable: MvPage.renderTable,
        initInfiniteScroll: MvPage._initInfiniteScroll,
        openMvModal: MvPage.openMvModal,
        updateDeliverySelectionBar: MvPage.updateDeliverySelectionBar,
        currentRenderedDeliveryRows: MvPage._currentRenderedDeliveryRows,
        openCameraModal: MvPage._openCameraModal,
        startCameraScan: MvPage.startCameraScan,
        handleBarcodeScan: MvPage.handleBarcodeScan,
        onProdChange: MvPage._onProdChange
    };

    const TYPE_META = {
        entry: {
            label: 'Entrée de stock', short: 'Entrée', badge: 'ENTRÉE', icon: '📥', state: 'success',
            description: 'Réceptionner un produit avec fournisseur, coût et référence éventuelle.'
        },
        add: {
            label: 'Ajout de stock', short: 'Ajout', badge: 'AJOUT', icon: '➕', state: 'success',
            description: 'Remettre ou ajouter une quantité dans un magasin.'
        },
        remove: {
            label: 'Sortie de stock', short: 'Sortie', badge: 'SORTIE', icon: '➖', state: 'danger',
            description: 'Sortir un produit pour un chantier, une livraison ou une consommation.'
        },
        transfer: {
            label: 'Transfert de stock', short: 'Transfert', badge: 'TRANSFERT', icon: '↔', state: 'info',
            description: 'Déplacer une quantité entre deux magasins.'
        },
        inventory: {
            label: 'Mouvement d’inventaire', short: 'Inventaire', badge: 'INVENTAIRE', icon: '📋', state: 'warning',
            description: 'Correction générée par la clôture d’un inventaire.'
        }
    };

    const escapeHtml = (value) => MvPage._escapeHtml ? MvPage._escapeHtml(value) : String(value ?? '')
        .replace(/[&<>"']/g, (char) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }[char]));

    const safeId = (value) => String(value ?? 'item').replace(/[^a-zA-Z0-9_-]/g, '_');

    const debounce = (fn, delay) => {
        let timer = 0;
        return (...args) => {
            global.clearTimeout(timer);
            timer = global.setTimeout(() => fn(...args), delay);
        };
    };

    MvPage._mobileLimit = 40;
    MvPage._mobileChunk = 40;
    MvPage._terrainPanel = null;
    MvPage._terrainFlowController = null;
    MvPage._terrainBound = false;
    MvPage._movementSubmitting = false;

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

    MvPage.init = async function initTerrainMovements() {
        await original.init.call(this);
        this._defaultDateFrom = document.getElementById('mvDateFrom')?.value || '';
        this._defaultDateTo = document.getElementById('mvDateTo')?.value || '';
        this._initTerrainExperience();
        this.renderTable();
    };

    MvPage._initInfiniteScroll = function initInfiniteScrollTerrainAware() {
        if (this.isTerrainMode()) return;
        return original.initInfiniteScroll.call(this);
    };

    MvPage._initTerrainExperience = function initTerrainExperience() {
        if (this._terrainBound) return;
        this._terrainBound = true;

        const hiddenSearch = document.getElementById('mvSearch');
        const mobileSearch = document.getElementById('mvMobileSearch');
        const filtersButton = document.getElementById('btnMvMobileFilters');
        const createButton = document.getElementById('btnMvMobileCreate');
        const scanButton = document.getElementById('btnMvMobileScan');
        const list = document.getElementById('mvMobileList');

        if (mobileSearch && hiddenSearch) {
            mobileSearch.value = hiddenSearch.value || '';
            const search = debounce(() => {
                hiddenSearch.value = mobileSearch.value;
                this._mobileLimit = this._mobileChunk;
                this.load();
            }, 220);
            mobileSearch.addEventListener('input', search);
            mobileSearch.addEventListener('search', search);
        }

        if (filtersButton) filtersButton.addEventListener('click', () => this.openMobileFilters(filtersButton));
        if (createButton) createButton.addEventListener('click', () => this.openOperationSheet({ trigger: createButton }));
        if (scanButton) scanButton.addEventListener('click', () => this.openOperationSheet({ trigger: scanButton, scanIntent: true }));

        if (list) {
            list.addEventListener('click', (event) => {
                const toggle = event.target.closest('[data-mv-card-toggle]');
                if (toggle) {
                    const target = document.getElementById(toggle.dataset.mvCardToggle || '');
                    if (target) {
                        const open = toggle.getAttribute('aria-expanded') === 'true';
                        toggle.setAttribute('aria-expanded', open ? 'false' : 'true');
                        target.hidden = open;
                        toggle.textContent = open ? 'Détails' : 'Réduire';
                    }
                    return;
                }

                const more = event.target.closest('[data-mv-load-more]');
                if (more) {
                    this._mobileLimit += this._mobileChunk;
                    this.renderMobileCards();
                    return;
                }

                const documentLink = event.target.closest('[data-mv-document-url]');
                if (documentLink) {
                    event.preventDefault();
                    global.open(documentLink.dataset.mvDocumentUrl, '_blank', 'noopener');
                }
            });
        }

        document.addEventListener('magestock:devicechange', () => {
            this._mobileLimit = this._mobileChunk;
            this.renderTable();
        });

        this._refreshMobileFilterCount();
    };

    MvPage._populateProductFilter = function populateProductFilter(warehouseId, selectedValue) {
        const select = document.getElementById('mvProdFilter');
        if (!select) return;
        const wid = String(warehouseId || '');
        let products = this.prods || [];
        if (wid) {
            const stockIds = new Set((this.stock || [])
                .filter((row) => String(row.warehouseId) === wid)
                .map((row) => String(row.productId)));
            const movementIds = new Set((this._all || [])
                .filter((movement) => String(movement.warehouseId) === wid)
                .map((movement) => String(movement.productId)));
            const filtered = products.filter((product) => stockIds.has(String(product.id)) || movementIds.has(String(product.id)));
            if (filtered.length) products = filtered;
        }
        select.innerHTML = '<option value="">Tous les produits</option>' + products.map((product) => {
            const sku = product.sku ? ` — ${escapeHtml(product.sku)}` : '';
            return `<option value="${escapeHtml(product.id)}">${escapeHtml(product.name)}${sku}</option>`;
        }).join('');
        select.value = selectedValue && [...select.options].some((option) => String(option.value) === String(selectedValue))
            ? String(selectedValue)
            : '';
    };

    MvPage.openMobileFilters = function openMobileFilters(trigger) {
        if (!global.FilterDrawer) return;

        const current = {
            warehouse: document.getElementById('mvWhFilter')?.value || '',
            product: document.getElementById('mvProdFilter')?.value || '',
            type: document.getElementById('mvTypeFilter')?.value || '',
            dateFrom: document.getElementById('mvDateFrom')?.value || '',
            dateTo: document.getElementById('mvDateTo')?.value || '',
            operator: document.getElementById('mvOpFilter')?.value || '',
            site: document.getElementById('mvSiteFilter')?.value || '',
            delivery: document.getElementById('mvDeliveryFilter')?.value || '',
            supplier: document.getElementById('mvSupplierFilter')?.value || ''
        };

        const content = document.createElement('form');
        content.className = 'mv-filter-drawer';
        content.noValidate = true;
        content.innerHTML = `
            <p class="mv-filter-drawer__hint">Les produits proposés suivent le magasin sélectionné. Les dates restent limitées aux trente derniers jours tant qu’elles ne sont pas modifiées.</p>
            <div class="mv-filter-drawer__grid">
                <div class="mv-terrain-field">
                    <label for="mvDrawerWarehouse">Magasin</label>
                    <select id="mvDrawerWarehouse" class="form-select">
                        <option value="">Tous les magasins</option>
                        ${(this.whs || []).map((warehouse) => `<option value="${escapeHtml(warehouse.id)}">${escapeHtml(warehouse.name)}</option>`).join('')}
                    </select>
                </div>
                <div class="mv-terrain-field">
                    <label for="mvDrawerProduct">Produit</label>
                    <select id="mvDrawerProduct" class="form-select"></select>
                </div>
                <div class="mv-terrain-field">
                    <label for="mvDrawerType">Type</label>
                    <select id="mvDrawerType" class="form-select">
                        <option value="">Tous les types</option>
                        <option value="entry">Entrée</option>
                        <option value="add">Ajout</option>
                        <option value="remove">Sortie</option>
                        <option value="transfer">Transfert</option>
                        <option value="inventory">Inventaire</option>
                    </select>
                </div>
                <div class="mv-terrain-field">
                    <label for="mvDrawerOperator">Opérateur</label>
                    <input type="text" id="mvDrawerOperator" class="form-input" placeholder="Nom de l’opérateur">
                </div>
                <div class="mv-terrain-field">
                    <label for="mvDrawerDateFrom">Date de début</label>
                    <input type="date" id="mvDrawerDateFrom" class="form-input">
                </div>
                <div class="mv-terrain-field">
                    <label for="mvDrawerDateTo">Date de fin</label>
                    <input type="date" id="mvDrawerDateTo" class="form-input">
                </div>
                <div class="mv-terrain-field">
                    <label for="mvDrawerSite">Chantier / site</label>
                    <input type="text" id="mvDrawerSite" class="form-input" placeholder="Nom du site">
                </div>
                <div class="mv-terrain-field">
                    <label for="mvDrawerDelivery">BL / référence</label>
                    <input type="text" id="mvDrawerDelivery" class="form-input" placeholder="Numéro de document">
                </div>
                <div class="mv-terrain-field mv-terrain-field--wide">
                    <label for="mvDrawerSupplier">Fournisseur</label>
                    <input type="text" id="mvDrawerSupplier" class="form-input" placeholder="Nom du fournisseur">
                </div>
            </div>`;

        const warehouseSelect = content.querySelector('#mvDrawerWarehouse');
        const productSelect = content.querySelector('#mvDrawerProduct');

        const populateProducts = (warehouseId, selected) => {
            const wid = String(warehouseId || '');
            let products = this.prods || [];
            if (wid) {
                const ids = new Set([
                    ...(this.stock || []).filter((row) => String(row.warehouseId) === wid).map((row) => String(row.productId)),
                    ...(this._all || []).filter((movement) => String(movement.warehouseId) === wid).map((movement) => String(movement.productId))
                ]);
                const filtered = products.filter((product) => ids.has(String(product.id)));
                if (filtered.length) products = filtered;
            }
            productSelect.innerHTML = '<option value="">Tous les produits</option>' + products.map((product) => {
                const sku = product.sku ? ` — ${escapeHtml(product.sku)}` : '';
                return `<option value="${escapeHtml(product.id)}">${escapeHtml(product.name)}${sku}</option>`;
            }).join('');
            productSelect.value = selected && [...productSelect.options].some((option) => String(option.value) === String(selected))
                ? String(selected)
                : '';
        };

        warehouseSelect.value = current.warehouse;
        populateProducts(current.warehouse, current.product);
        content.querySelector('#mvDrawerType').value = current.type;
        content.querySelector('#mvDrawerDateFrom').value = current.dateFrom;
        content.querySelector('#mvDrawerDateTo').value = current.dateTo;
        content.querySelector('#mvDrawerOperator').value = current.operator;
        content.querySelector('#mvDrawerSite').value = current.site;
        content.querySelector('#mvDrawerDelivery').value = current.delivery;
        content.querySelector('#mvDrawerSupplier').value = current.supplier;
        warehouseSelect.addEventListener('change', () => populateProducts(warehouseSelect.value, ''));

        global.FilterDrawer.open({
            title: 'Filtrer les mouvements',
            description: 'Affinez l’historique sans perdre votre position dans la page.',
            trigger,
            content,
            actions: [
                {
                    label: 'Réinitialiser',
                    className: 'btn btn-secondary',
                    close: false,
                    onClick: () => {
                        warehouseSelect.value = '';
                        populateProducts('', '');
                        content.querySelector('#mvDrawerType').value = '';
                        content.querySelector('#mvDrawerDateFrom').value = this._defaultDateFrom || '';
                        content.querySelector('#mvDrawerDateTo').value = this._defaultDateTo || '';
                        content.querySelector('#mvDrawerOperator').value = '';
                        content.querySelector('#mvDrawerSite').value = '';
                        content.querySelector('#mvDrawerDelivery').value = '';
                        content.querySelector('#mvDrawerSupplier').value = '';
                        global.MageStockMobileUI?.announce?.('Filtres réinitialisés.');
                        return false;
                    }
                },
                {
                    label: 'Appliquer',
                    className: 'btn btn-primary',
                    onClick: () => {
                        const warehouse = warehouseSelect.value;
                        const product = productSelect.value;
                        document.getElementById('mvWhFilter').value = warehouse;
                        this._populateProductFilter(warehouse, product);
                        document.getElementById('mvTypeFilter').value = content.querySelector('#mvDrawerType').value;
                        document.getElementById('mvDateFrom').value = content.querySelector('#mvDrawerDateFrom').value;
                        document.getElementById('mvDateTo').value = content.querySelector('#mvDrawerDateTo').value;
                        document.getElementById('mvOpFilter').value = content.querySelector('#mvDrawerOperator').value;
                        document.getElementById('mvSiteFilter').value = content.querySelector('#mvDrawerSite').value;
                        document.getElementById('mvDeliveryFilter').value = content.querySelector('#mvDrawerDelivery').value;
                        document.getElementById('mvSupplierFilter').value = content.querySelector('#mvDrawerSupplier').value;
                        this._mobileLimit = this._mobileChunk;
                        this._refreshMobileFilterCount();
                        this.load();
                    }
                }
            ]
        });
    };

    MvPage._activeMobileFilterCount = function activeMobileFilterCount() {
        const values = [
            document.getElementById('mvWhFilter')?.value,
            document.getElementById('mvProdFilter')?.value,
            document.getElementById('mvTypeFilter')?.value,
            document.getElementById('mvOpFilter')?.value,
            document.getElementById('mvSiteFilter')?.value,
            document.getElementById('mvDeliveryFilter')?.value,
            document.getElementById('mvSupplierFilter')?.value
        ];
        let count = values.filter((value) => String(value || '').trim() !== '').length;
        const from = document.getElementById('mvDateFrom')?.value || '';
        const to = document.getElementById('mvDateTo')?.value || '';
        if (from && from !== (this._defaultDateFrom || from)) count += 1;
        if (to && to !== (this._defaultDateTo || to)) count += 1;
        return count;
    };

    MvPage._refreshMobileFilterCount = function refreshMobileFilterCount() {
        const count = this._activeMobileFilterCount();
        const badge = document.getElementById('mvMobileFilterCount');
        const label = document.getElementById('mvMobileFilterLabel');
        if (badge) {
            badge.hidden = count === 0;
            badge.textContent = String(count);
        }
        if (label) label.textContent = count ? `Filtres (${count})` : 'Filtres';
    };

    MvPage.renderTable = function renderResponsiveMovements() {
        if (this.isTerrainMode()) {
            this.renderMobileCards();
            const count = (this._filteredRaw || []).length;
            const desktopCount = document.getElementById('mvCount');
            if (desktopCount) desktopCount.textContent = `${count} mouvement${count > 1 ? 's' : ''}`;
            this.updateDeliverySelectionBar();
            return;
        }
        return original.renderTable.call(this);
    };

    MvPage._dateKey = function movementDateKey(value) {
        const date = new Date(value);
        if (Number.isNaN(date.getTime())) return 'unknown';
        const year = date.getFullYear();
        const month = String(date.getMonth() + 1).padStart(2, '0');
        const day = String(date.getDate()).padStart(2, '0');
        return `${year}-${month}-${day}`;
    };

    MvPage._dateHeading = function movementDateHeading(key) {
        if (key === 'unknown') return 'Date inconnue';
        const date = new Date(`${key}T12:00:00`);
        const today = new Date();
        const yesterday = new Date(today);
        yesterday.setDate(today.getDate() - 1);
        const todayKey = this._dateKey(today);
        const yesterdayKey = this._dateKey(yesterday);
        if (key === todayKey) return 'Aujourd’hui';
        if (key === yesterdayKey) return 'Hier';
        return global.Locale?.formatDate
            ? global.Locale.formatDate(date, { weekday: 'long', day: '2-digit', month: 'long', year: 'numeric' })
            : date.toLocaleDateString('fr-FR', { weekday: 'long', day: '2-digit', month: 'long', year: 'numeric' });
    };

    MvPage._movementDocument = function movementDocument(movement) {
        const bcMatch = String(movement.reason || '').match(/\bBC-\d{8}-[A-Z0-9]+\b/);
        const bcId = bcMatch ? bcMatch[0] : '';
        if (bcId) return { label: bcId, url: `orders?open=${encodeURIComponent(bcId)}` };
        if (['remove', 'transfer'].includes(movement.type)) {
            return { label: 'Bon de livraison', url: Api.url(`api/delivery_note.php?movementId=${encodeURIComponent(movement.id)}`) };
        }
        return null;
    };

    MvPage._renderMobileMovementCard = function renderMobileMovementCard(movement) {
        const product = (this.prods || []).find((item) => String(item.id) === String(movement.productId));
        const warehouse = (this.whs || []).find((item) => String(item.id) === String(movement.warehouseId));
        const meta = TYPE_META[movement.type] || { badge: String(movement.type || 'MOUVEMENT').toUpperCase(), state: 'info' };
        const date = new Date(movement.date);
        const time = Number.isNaN(date.getTime()) ? '—' : (global.Locale?.formatTime ? global.Locale.formatTime(date) : date.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }));
        const quantity = Number(movement.quantity || 0);
        const sign = movement.type === 'remove' ? '−' : '+';
        const quantityClass = movement.type === 'remove' ? 'text-danger' : 'text-success';
        const unit = movement.unit || product?.packBaseUnit || product?.unitType || 'unité';
        const currentStock = this._getCurrentStock(movement.productId, movement.warehouseId);
        const reason = [movement.reason, movement.site].filter(Boolean).join(' · ') || '—';
        const detailId = `mvCardDetails_${safeId(movement.id)}`;
        const documentInfo = this._movementDocument(movement);
        const selected = this._selectedDeliveryMoves?.has(String(movement.id || ''));
        const selectable = this._isDeliverySelectable(movement);
        const note = movement.note || movement.notes || '—';
        const operator = movement.operator || movement.user || '—';
        const lot = movement.lotNumber || '—';
        const expiry = movement.expiryDate ? (global.Locale?.formatDate ? global.Locale.formatDate(movement.expiryDate) : movement.expiryDate) : '—';

        const deliveryAction = selectable ? `
            <label class="mv-delivery-select${selected ? ' is-checked' : ''}" title="Ajouter au bon de livraison">
                <input type="checkbox" class="mv-delivery-check" data-id="${escapeHtml(movement.id)}" aria-label="Ajouter ${escapeHtml(product?.name || 'ce mouvement')} au bon de livraison"${selected ? ' checked' : ''}>
                <span class="mv-delivery-mark" aria-hidden="true"></span><span class="mv-delivery-action-label">BL groupé</span>
            </label>` : '';

        return `
            <article class="mv-native-card${selected ? ' is-delivery-selected' : ''}" data-movement-card="${escapeHtml(movement.id)}" data-type="${escapeHtml(movement.type || '')}">
                <header class="mv-native-card__head">
                    <div class="mv-native-card__identity">
                        <div class="mv-native-card__eyebrow">
                            <span class="badge badge-${escapeHtml(meta.state === 'danger' ? 'danger' : meta.state === 'warning' ? 'warning' : meta.state === 'info' ? 'info' : 'success')}">${escapeHtml(meta.badge)}</span>
                            <span class="mv-native-card__time">${escapeHtml(time)}</span>
                        </div>
                        <h3 class="mv-native-card__title">${escapeHtml(product?.name || 'Produit inconnu')}</h3>
                        <p class="mv-native-card__meta">${escapeHtml(product?.sku || 'Sans SKU')} · ${escapeHtml(warehouse?.name || 'Magasin inconnu')}</p>
                    </div>
                    <div class="mv-native-card__qty">
                        <strong class="${quantityClass}">${sign}${escapeHtml(Math.abs(quantity))}</strong>
                        <span>${escapeHtml(unit)}</span>
                    </div>
                </header>
                <dl class="mv-native-card__facts">
                    <div class="mv-native-card__fact"><dt>Stock actuel</dt><dd>${escapeHtml(currentStock)} ${escapeHtml(unit)}</dd></div>
                    <div class="mv-native-card__fact"><dt>Destination / raison</dt><dd>${escapeHtml(reason)}</dd></div>
                </dl>
                <div class="mv-native-card__actions">
                    <button type="button" class="btn btn-secondary" data-mv-card-toggle="${detailId}" aria-expanded="false" aria-controls="${detailId}">Détails</button>
                    ${documentInfo ? `<button type="button" class="btn btn-secondary" data-mv-document-url="${escapeHtml(documentInfo.url)}">${escapeHtml(documentInfo.label)}</button>` : ''}
                    ${deliveryAction}
                </div>
                <div class="mv-native-card__details" id="${detailId}" hidden>
                    <div class="mv-native-card__detail-line"><span>Opérateur</span><span>${escapeHtml(operator)}</span></div>
                    <div class="mv-native-card__detail-line"><span>Note</span><span>${escapeHtml(note)}</span></div>
                    <div class="mv-native-card__detail-line"><span>Lot</span><span>${escapeHtml(lot)}</span></div>
                    <div class="mv-native-card__detail-line"><span>Péremption</span><span>${escapeHtml(expiry)}</span></div>
                    ${movement.supplier ? `<div class="mv-native-card__detail-line"><span>Fournisseur</span><span>${escapeHtml(movement.supplier)}</span></div>` : ''}
                    ${movement.deliveryNote ? `<div class="mv-native-card__detail-line"><span>BL / référence</span><span>${escapeHtml(movement.deliveryNote)}</span></div>` : ''}
                </div>
            </article>`;
    };

    MvPage._renderMobileGroupCard = function renderMobileGroupCard(group) {
        const meta = TYPE_META.entry;
        const date = new Date(group.date);
        const time = Number.isNaN(date.getTime()) ? '—' : (global.Locale?.formatTime ? global.Locale.formatTime(date) : date.toLocaleTimeString('fr-FR', { hour: '2-digit', minute: '2-digit' }));
        const detailId = `mvGroupDetails_${safeId(group.id)}`;
        const quantity = Number(group.quantity || 0);
        const items = group.items || [];
        const warehouses = [...new Set(items.map((item) => (this.whs || []).find((warehouse) => String(warehouse.id) === String(item.warehouseId))?.name).filter(Boolean))];
        const lines = items.map((item) => {
            const product = (this.prods || []).find((candidate) => String(candidate.id) === String(item.productId));
            const warehouse = (this.whs || []).find((candidate) => String(candidate.id) === String(item.warehouseId));
            return `<div class="mv-group-line">
                <div><strong>${escapeHtml(product?.name || 'Produit inconnu')}</strong><small>${escapeHtml(product?.sku || 'Sans SKU')} · ${escapeHtml(warehouse?.name || 'Magasin inconnu')}</small></div>
                <span class="mv-group-line__qty">+${escapeHtml(Math.abs(Number(item.quantity || 0)))}</span>
            </div>`;
        }).join('');
        const orderUrl = `orders?open=${encodeURIComponent(group.bcId || '')}`;

        return `
            <article class="mv-native-card" data-type="entry">
                <header class="mv-native-card__head">
                    <div class="mv-native-card__identity">
                        <div class="mv-native-card__eyebrow"><span class="badge badge-success">${meta.badge}</span><span class="mv-native-card__time">${escapeHtml(time)}</span></div>
                        <h3 class="mv-native-card__title">Réception ${escapeHtml(group.bcId || '')}</h3>
                        <p class="mv-native-card__meta">${items.length} ligne${items.length > 1 ? 's' : ''} · ${escapeHtml(warehouses.join(', ') || 'Magasin non renseigné')}</p>
                    </div>
                    <div class="mv-native-card__qty"><strong class="text-success">+${escapeHtml(Math.abs(quantity))}</strong><span>total</span></div>
                </header>
                <div class="mv-native-card__actions">
                    <button type="button" class="btn btn-secondary" data-mv-card-toggle="${detailId}" aria-expanded="false" aria-controls="${detailId}">Voir les produits</button>
                    <button type="button" class="btn btn-primary" data-mv-document-url="${escapeHtml(orderUrl)}">Ouvrir la commande</button>
                </div>
                <div class="mv-native-card__details mv-group-lines" id="${detailId}" hidden>${lines}</div>
            </article>`;
    };

    MvPage.renderMobileCards = function renderMobileCards() {
        const list = document.getElementById('mvMobileList');
        const countElement = document.getElementById('mvMobileCount');
        if (!list) return;

        const rows = this._filtered || [];
        const totalMovements = (this._filteredRaw || []).length;
        if (countElement) countElement.textContent = `${totalMovements} mouvement${totalMovements > 1 ? 's' : ''}`;
        list.setAttribute('aria-busy', 'true');

        if (!rows.length) {
            list.innerHTML = '<div class="mv-mobile-empty"><div><strong>Aucun mouvement trouvé</strong><br><span>Modifiez la recherche ou les filtres pour élargir la période.</span></div></div>';
            list.setAttribute('aria-busy', 'false');
            this._refreshMobileFilterCount();
            return;
        }

        const visible = rows.slice(0, this._mobileLimit);
        const groups = new Map();
        visible.forEach((movement) => {
            const key = this._dateKey(movement.date);
            if (!groups.has(key)) groups.set(key, []);
            groups.get(key).push(movement);
        });

        const html = [...groups.entries()].map(([key, movements]) => `
            <section class="mv-date-group" aria-labelledby="mvDate_${safeId(key)}">
                <h3 class="mv-date-group__title" id="mvDate_${safeId(key)}"><span>${escapeHtml(this._dateHeading(key))}</span><span>${movements.length} élément${movements.length > 1 ? 's' : ''}</span></h3>
                <div class="mv-date-group__cards">
                    ${movements.map((movement) => movement.__group ? this._renderMobileGroupCard(movement) : this._renderMobileMovementCard(movement)).join('')}
                </div>
            </section>`).join('');

        const remaining = rows.length - visible.length;
        list.innerHTML = html + (remaining > 0
            ? `<button type="button" class="btn btn-secondary mv-mobile-load-more" data-mv-load-more>Afficher ${Math.min(this._mobileChunk, remaining)} mouvement${Math.min(this._mobileChunk, remaining) > 1 ? 's' : ''} de plus</button>`
            : '');
        list.setAttribute('aria-busy', 'false');
        this.updateDeliverySelectionBar();
        this._refreshMobileFilterCount();
    };

    MvPage.updateDeliverySelectionBar = function updateDeliverySelectionBarTerrain() {
        original.updateDeliverySelectionBar.call(this);
        document.querySelectorAll('.mv-delivery-check').forEach((checkbox) => {
            const selected = this._selectedDeliveryMoves.has(String(checkbox.dataset.id || ''));
            checkbox.closest('[data-movement-card]')?.classList.toggle('is-delivery-selected', selected);
        });
    };

    MvPage._currentRenderedDeliveryRows = function currentRenderedDeliveryRowsTerrain() {
        if (!this.isTerrainMode()) return original.currentRenderedDeliveryRows.call(this);
        return (this._filtered || []).slice(0, this._mobileLimit).filter((movement) => !movement.__group && this._isDeliverySelectable(movement));
    };

    MvPage.openOperationSheet = function openOperationSheet(options) {
        const opts = options || {};
        if (!global.ActionSheet || !this.isTerrainMode()) {
            this.openMvModal(opts.type || 'entry');
            return;
        }
        const content = document.createElement('div');
        content.className = 'mv-operation-grid';
        ['entry', 'add', 'remove', 'transfer'].forEach((type) => {
            const meta = TYPE_META[type];
            const button = document.createElement('button');
            button.type = 'button';
            button.className = 'mv-operation-choice';
            button.dataset.movementType = type;
            button.innerHTML = `<span class="mv-operation-choice__icon" aria-hidden="true">${meta.icon}</span><span class="mv-operation-choice__copy"><strong>${escapeHtml(meta.label)}</strong><small>${escapeHtml(meta.description)}</small></span><span class="mv-operation-choice__arrow" aria-hidden="true">›</span>`;
            content.appendChild(button);
        });

        const handle = global.ActionSheet.open({
            title: opts.scanIntent ? 'Scanner pour quel mouvement ?' : 'Choisir une opération',
            description: opts.scanIntent ? 'Le scanner démarrera après le choix du magasin source.' : 'Seuls les champs utiles à l’opération seront demandés.',
            trigger: opts.trigger,
            content,
            actions: [{ label: 'Annuler', className: 'btn btn-secondary' }]
        });

        content.addEventListener('click', (event) => {
            const choice = event.target.closest('[data-movement-type]');
            if (!choice) return;
            const type = choice.dataset.movementType;
            handle.close('choice');
            global.setTimeout(() => this.openTerrainFlow(type, null, null, !!opts.scanIntent), 210);
        });
    };

    MvPage.openMvModal = function openMovementResponsive(type, prefilledProduct, prefilledWarehouse) {
        if (this.isTerrainMode() && global.MobileDetailPage && global.MobileFlow) {
            return this.openTerrainFlow(type, prefilledProduct, prefilledWarehouse, false);
        }
        return original.openMvModal.call(this, type, prefilledProduct, prefilledWarehouse);
    };

    MvPage._unitOptions = function movementUnitOptions() {
        const unitLabel = (unit) => ({
            piece: 'pièce', ml: 'mètre linéaire', m2: 'm²', m3: 'm³', litre: 'litre', kg: 'kg',
            fut: 'fût', aerosol: 'aérosol', boite: 'boîte', carton: 'carton', palette: 'palette',
            bidon: 'bidon', sac: 'sac', seau: 'seau', rouleau: 'rouleau', bobine: 'bobine',
            tube: 'tube', paquet: 'paquet', m: 'mètre'
        }[unit] || unit);
        return ['piece', 'carton', 'palette', 'bidon', 'fut', 'aerosol', 'sac', 'seau', 'rouleau', 'bobine', 'tube', 'boite', 'paquet', 'ml', 'litre', 'kg', 'm', 'm2', 'm3']
            .map((unit) => `<option value="${unit}">${escapeHtml(unitLabel(unit))}</option>`).join('');
    };

    MvPage._terrainFlowHtml = function terrainFlowHtml(type) {
        const meta = TYPE_META[type] || TYPE_META.entry;
        const warehouseOptions = (this.whs || []).map((warehouse) => `<option value="${escapeHtml(warehouse.id)}">${escapeHtml(warehouse.name)}</option>`).join('');
        const supplierOptions = (this.suppliers || []).map((supplier) => `<option value="${escapeHtml(supplier.name)}">${escapeHtml(supplier.name)}</option>`).join('');
        const isTransfer = type === 'transfer';
        const isEntry = type === 'entry';
        const isRemove = type === 'remove';
        const isAdd = type === 'add';

        return `
        <div class="mv-terrain-flow" id="mvTerrainFlow" data-ms-flow>
            <header class="ms-flow__header">
                <span class="ms-flow__kicker">${escapeHtml(meta.short)} · parcours terrain</span>
                <h2 class="ms-flow__title">${escapeHtml(meta.label)}</h2>
                <p class="ms-flow__description">Étape <strong data-ms-flow-current>1</strong> sur <strong data-ms-flow-total>4</strong>. Les champs affichés dépendent de l’opération choisie.</p>
                <div class="ms-flow__progress" data-ms-flow-progress></div>
                <nav class="ms-flow__steps" data-ms-flow-indicators aria-label="Progression du mouvement">
                    <button type="button" class="ms-flow__indicator" data-ms-flow-indicator="1">Magasin</button>
                    <button type="button" class="ms-flow__indicator" data-ms-flow-indicator="2">Produit</button>
                    <button type="button" class="ms-flow__indicator" data-ms-flow-indicator="3">Quantité</button>
                    <button type="button" class="ms-flow__indicator" data-ms-flow-indicator="4">Vérification</button>
                </nav>
            </header>

            <section class="ms-flow__panel" id="mvStepWh" data-ms-flow-step="1">
                <h3 data-ms-flow-title>Choisir ${isTransfer ? 'les magasins' : 'le magasin'}</h3>
                <p class="mv-terrain-step__lead">Le magasin détermine les produits disponibles et le stock utilisable.</p>
                <div class="mv-terrain-fields">
                    <div class="mv-terrain-field">
                        <label for="mvmWh">Magasin ${isTransfer ? 'source' : ''} *</label>
                        <select class="sel" id="mvmWh" name="warehouseId" required>
                            <option value="">— Choisir un magasin —</option>${warehouseOptions}
                        </select>
                    </div>
                    ${isTransfer ? `<div class="mv-terrain-field"><label for="mvmWhDst">Magasin destination *</label><select class="sel" id="mvmWhDst" name="warehouseIdDest" required><option value="">— Choisir une destination —</option>${warehouseOptions}</select></div>` : ''}
                </div>
                <div class="ms-flow__actions"><button type="button" class="btn btn-primary" data-ms-flow-next>Suivant</button></div>
            </section>

            <section class="ms-flow__panel mv-step-locked" id="mvStepProd" data-ms-flow-step="2">
                <h3 data-ms-flow-title>Scanner ou rechercher le produit</h3>
                <p class="mv-terrain-step__lead">Le scan est prioritaire sur le terrain. Une recherche manuelle reste disponible.</p>
                <div class="mv-terrain-fields">
                    <div class="mv-terrain-scan mv-terrain-field--wide">
                        <div class="mv-terrain-scan__head"><div><strong>Code-barres / QR produit</strong><small>Scannez avec la caméra ou saisissez le code.</small></div><span class="mv-stock-badge" id="mvStockBadge" style="display:none"></span></div>
                        <div class="mv-terrain-scan__row">
                            <input type="text" class="inp" id="mvmScanner" inputmode="search" enterkeyhint="done" placeholder="Code-barres ou SKU" autocomplete="off">
                            <button type="button" class="btn btn-primary" id="mvmScanCamera" aria-label="Ouvrir la caméra" title="Ouvrir la caméra">📷</button>
                        </div>
                    </div>
                    <div class="mv-terrain-field mv-terrain-field--wide">
                        <label for="mvmProdSearch">Rechercher par nom, SKU ou code</label>
                        <input type="search" class="inp" id="mvmProdSearch" placeholder="Rechercher un produit…" autocomplete="off" disabled>
                    </div>
                    <div class="mv-terrain-field mv-terrain-field--wide">
                        <label for="mvmProd">Produit *</label>
                        <select class="sel" id="mvmProd" name="productId" required disabled><option value="">— Choisir d’abord un magasin —</option></select>
                    </div>
                </div>
                <div class="ms-flow__actions"><button type="button" class="btn btn-secondary" data-ms-flow-back>Retour</button><button type="button" class="btn btn-primary" data-ms-flow-next>Suivant</button></div>
            </section>

            <section class="ms-flow__panel mv-step-locked" id="mvStepQty" data-ms-flow-step="3">
                <h3 data-ms-flow-title>Saisir la quantité</h3>
                <p class="mv-terrain-step__lead">La conversion vers l’unité de base est effectuée lors de l’enregistrement.</p>
                <div class="mv-terrain-fields">
                    <div class="mv-terrain-field"><label for="mvmUnit">Unité *</label><select class="sel" id="mvmUnit" name="unit" required disabled>${this._unitOptions()}</select></div>
                    <div class="mv-terrain-field"><label for="mvmQty">Quantité *</label><input type="number" class="inp" id="mvmQty" name="quantity" min="0.01" step="0.01" value="1" inputmode="decimal" required disabled></div>
                    ${isAdd ? `<div class="mv-terrain-field mv-terrain-field--wide"><label for="mvmCost">Prix d’achat pour l’unité sélectionnée</label><input type="number" class="inp" id="mvmCost" min="0" step="0.0001" inputmode="decimal" placeholder="Ex. 12,50"></div>` : ''}
                    ${isEntry ? `<div class="mv-terrain-field mv-terrain-field--wide"><label for="mvmUnitCost">Coût unitaire (${escapeHtml(global.Locale?.currencyCode?.() || 'EUR')})</label><input type="number" class="inp" id="mvmUnitCost" min="0" step="0.01" value="0" inputmode="decimal"></div>` : ''}
                </div>
                <div class="ms-flow__actions"><button type="button" class="btn btn-secondary" data-ms-flow-back>Retour</button><button type="button" class="btn btn-primary" data-ms-flow-next>Suivant</button></div>
            </section>

            <section class="ms-flow__panel mv-step-locked" id="mvStepTrace" data-ms-flow-step="4">
                <h3 data-ms-flow-title>Compléter et vérifier</h3>
                <p class="mv-terrain-step__lead">Vérifiez le résumé avant l’application du mouvement au stock.</p>
                <div class="mv-terrain-fields">
                    <div class="mv-terrain-field"><label for="mvmLot">Numéro de lot</label><input type="text" class="inp" id="mvmLot" placeholder="Ex. LOT-2026-001" disabled></div>
                    <div class="mv-terrain-field"><label for="mvmExpiry">DLUO / péremption</label><input type="date" class="inp" id="mvmExpiry" disabled></div>
                    ${isEntry ? `<div class="mv-terrain-field"><label for="mvmSupplier">Fournisseur *</label><select class="sel" id="mvmSupplier" required><option value="">— Choisir —</option>${supplierOptions}</select></div><div class="mv-terrain-field"><label for="mvmDeliveryNote">BL / référence</label><input type="text" class="inp" id="mvmDeliveryNote" placeholder="Ex. BL-2026-001"></div>` : ''}
                    ${isRemove ? `<div class="mv-terrain-field"><label for="mvmReason">Raison / destination</label><select class="sel" id="mvmReason"><option value="Chantier">Chantier</option><option value="Réparation">Réparation</option><option value="Livraison client">Livraison client</option><option value="Consommable">Consommable</option><option value="Autre">Autre</option></select></div><div class="mv-terrain-field"><label for="mvmSite">Chantier / lieu</label><input type="text" class="inp" id="mvmSite" placeholder="Ex. Rue Dupont"></div><div class="mv-terrain-field mv-terrain-field--wide"><label for="mvmOper">Nom de l’opérateur</label><input type="text" class="inp" id="mvmOper" autocomplete="name" placeholder="Nom et prénom"></div>` : ''}
                    ${(isAdd || isTransfer) ? `<div class="mv-terrain-field mv-terrain-field--wide"><label for="mvmNote">Note</label><input type="text" class="inp" id="mvmNote" placeholder="Information complémentaire"></div>` : ''}
                </div>
                <div class="mv-terrain-review">
                    <dl class="mv-terrain-review__summary">
                        <div><dt>Opération</dt><dd id="mvReviewType">${escapeHtml(meta.label)}</dd></div>
                        <div><dt>Produit</dt><dd id="mvReviewProduct">—</dd></div>
                        <div><dt>Magasin</dt><dd id="mvReviewWarehouse">—</dd></div>
                        <div><dt>Quantité</dt><dd id="mvReviewQuantity">—</dd></div>
                        ${isTransfer ? '<div><dt>Destination</dt><dd id="mvReviewDestination">—</dd></div>' : ''}
                        <div><dt>Stock actuel</dt><dd id="mvReviewStock">—</dd></div>
                    </dl>
                </div>
                <div class="ms-flow__actions"><button type="button" class="btn btn-secondary" data-ms-flow-back>Retour</button><button type="button" class="btn btn-primary mv-terrain-submit" id="btnMvTerrainSubmit">Enregistrer le mouvement</button></div>
            </section>
        </div>`;
    };

    MvPage.openTerrainFlow = function openTerrainFlow(type, prefilledProduct, prefilledWarehouse, startWithScanner) {
        if (!global.MobileDetailPage || !global.MobileFlow) return original.openMvModal.call(this, type, prefilledProduct, prefilledWarehouse);
        const meta = TYPE_META[type] || TYPE_META.entry;
        this._mvType = type;
        this._pendingBarcodePid = null;
        this._terrainScanIntent = !!startWithScanner;
        this._terrainScanStarted = false;

        const content = document.createElement('div');
        content.innerHTML = this._terrainFlowHtml(type);
        const root = content.firstElementChild;

        const panel = global.MobileDetailPage.open({
            title: meta.label,
            description: 'Parcours guidé téléphone et tablette',
            content: root,
            className: `mv-terrain-page mv-terrain-page--${type}`,
            onClose: () => {
                this.stopCameraScan();
                this._terrainPanel = null;
                this._terrainFlowController?.destroy?.();
                this._terrainFlowController = null;
            }
        });
        this._terrainPanel = panel;

        const flow = global.MobileFlow.create(root, {
            restore: false,
            validateStep: ({ stepIndex }) => this._validateTerrainStep(stepIndex, type),
            onChange: ({ index }) => {
                if (index === 1 && this._terrainScanIntent && !this._terrainScanStarted) {
                    this._terrainScanStarted = true;
                    global.setTimeout(() => this._openCameraModal(), 180);
                }
                if (index === 3) this._updateTerrainSummary(type);
            }
        });
        this._terrainFlowController = flow;

        const source = root.querySelector('#mvmWh');
        source?.addEventListener('change', () => {
            this._onWhChange(type, null);
            this._updateTerrainSummary(type);
        });
        root.querySelector('#mvmWhDst')?.addEventListener('change', () => this._updateTerrainSummary(type));
        root.querySelector('#mvmProd')?.addEventListener('change', () => {
            this._onProdChange(type);
            this._updateTerrainSummary(type);
        });
        root.querySelector('#mvmQty')?.addEventListener('input', () => this._updateTerrainSummary(type));
        root.querySelector('#mvmUnit')?.addEventListener('change', () => this._updateTerrainSummary(type));
        root.querySelector('#btnMvTerrainSubmit')?.addEventListener('click', () => this.submitMv());

        const scannerInput = root.querySelector('#mvmScanner');
        scannerInput?.addEventListener('keydown', async (event) => {
            if (event.key !== 'Enter') return;
            event.preventDefault();
            const code = scannerInput.value.trim();
            if (code) await this.handleBarcodeScan(code);
        });
        root.querySelector('#mvmScanCamera')?.addEventListener('click', () => this._openCameraModal());

        if (prefilledWarehouse && source) {
            source.value = String(prefilledWarehouse);
            this._onWhChange(type, prefilledProduct || null);
        }
        if (prefilledProduct && !prefilledWarehouse) this._pendingBarcodePid = prefilledProduct;

        global.setTimeout(() => {
            if (prefilledWarehouse && prefilledProduct) {
                const productSelect = root.querySelector('#mvmProd');
                if (productSelect && [...productSelect.options].some((option) => String(option.value) === String(prefilledProduct))) {
                    productSelect.value = String(prefilledProduct);
                    this._onProdChange(type);
                }
            }
            (source || root.querySelector('button, input, select'))?.focus?.();
        }, 80);
        return panel;
    };

    MvPage._validateTerrainStep = function validateTerrainStep(stepIndex, type) {
        if (stepIndex === 0) {
            const source = document.getElementById('mvmWh')?.value || '';
            if (!source) {
                global.MageStockMobileUI?.announce?.('Choisissez le magasin.', 'assertive');
                return false;
            }
            if (type === 'transfer') {
                const destination = document.getElementById('mvmWhDst')?.value || '';
                if (!destination || destination === source) {
                    toast('Le magasin destination doit être différent de la source.', 'error');
                    global.MageStockMobileUI?.announce?.('Choisissez un magasin destination différent.', 'assertive');
                    return false;
                }
            }
        }
        if (stepIndex === 1 && !document.getElementById('mvmProd')?.value) {
            toast('Scannez ou choisissez un produit.', 'error');
            return false;
        }
        if (stepIndex === 2) {
            const quantity = Number(document.getElementById('mvmQty')?.value || 0);
            if (!(quantity > 0)) {
                toast('La quantité doit être supérieure à zéro.', 'error');
                return false;
            }
        }
        return true;
    };

    MvPage._updateTerrainSummary = function updateTerrainSummary(type) {
        if (!this._terrainPanel) return;
        const productId = document.getElementById('mvmProd')?.value || '';
        const warehouseId = document.getElementById('mvmWh')?.value || '';
        const destinationId = document.getElementById('mvmWhDst')?.value || '';
        const product = (this.prods || []).find((item) => String(item.id) === String(productId));
        const warehouse = (this.whs || []).find((item) => String(item.id) === String(warehouseId));
        const destination = (this.whs || []).find((item) => String(item.id) === String(destinationId));
        const quantity = Number(document.getElementById('mvmQty')?.value || 0);
        const unit = document.getElementById('mvmUnit')?.value || product?.packBaseUnit || product?.unitType || 'unité';
        const stock = productId && warehouseId ? this._getCurrentStock(productId, warehouseId) : null;
        const set = (id, value) => { const node = document.getElementById(id); if (node) node.textContent = value; };
        set('mvReviewType', TYPE_META[type]?.label || type);
        set('mvReviewProduct', product ? `${product.name}${product.sku ? ` · ${product.sku}` : ''}` : '—');
        set('mvReviewWarehouse', warehouse?.name || '—');
        set('mvReviewDestination', destination?.name || '—');
        set('mvReviewQuantity', quantity > 0 ? this._formatMovementQty(quantity, unit) : '—');
        set('mvReviewStock', stock == null ? '—' : this._formatMovementQty(stock, unit));
    };

    MvPage._onProdChange = function onProductChangeTerrain(type) {
        original.onProdChange.call(this, type);
        if (this.isTerrainMode() && this._terrainFlowController?.current < 2) {
            global.setTimeout(() => document.getElementById('mvmProd')?.focus?.(), 75);
        }
        this._updateTerrainSummary(type);
    };

    MvPage.handleBarcodeScan = async function handleTerrainBarcode(value) {
        const product = await original.handleBarcodeScan.call(this, value);
        if (product && this._terrainFlowController?.current === 1) {
            await this._terrainFlowController.goTo(2, { validate: true });
        }
        this._updateTerrainSummary(this._mvType);
        return product;
    };

    MvPage._openCameraModal = function openTerrainCamera(trigger) {
        return original.openCameraModal.call(this, trigger || document.getElementById('mvmScanCamera'));
    };

    MvPage.startCameraScan = async function startTerrainCameraScan() {
        return original.startCameraScan.call(this);
    };

    MvPage.submitMv = async function submitMovementTerrain() {
        if (this._movementSubmitting) return false;
        const type = this._mvType;
        const productId = document.getElementById('mvmProd')?.value || '';
        const warehouseId = document.getElementById('mvmWh')?.value || '';
        const quantity = Number(document.getElementById('mvmQty')?.value || 0);
        const unit = document.getElementById('mvmUnit')?.value || 'piece';

        if (!productId || !warehouseId || !(quantity > 0)) {
            toast('Produit, magasin et quantité sont obligatoires.', 'error');
            return false;
        }

        const product = (this.prods || []).find((item) => String(item.id) === String(productId));
        if (!product) {
            toast('Produit introuvable.', 'error');
            return false;
        }

        const lotNumber = String(document.getElementById('mvmLot')?.value || '').trim();
        const expiryDate = document.getElementById('mvmExpiry')?.value || '';
        if (product.trackLot && !lotNumber) {
            toast('Le numéro de lot est obligatoire pour ce produit.', 'error');
            return false;
        }
        if (product.trackExpiry && !expiryDate) {
            toast('La date de péremption est obligatoire pour ce produit.', 'error');
            return false;
        }

        const data = {
            type,
            productId: Number(productId),
            warehouseId: Number(warehouseId),
            quantity: Math.abs(quantity),
            unit,
            lotNumber: lotNumber || null,
            expiryDate: expiryDate || null
        };

        if (type === 'transfer') {
            const destination = document.getElementById('mvmWhDst')?.value || '';
            if (!destination || destination === warehouseId) {
                toast('Le magasin destination est obligatoire et doit être différent.', 'error');
                return false;
            }
            data.transferTo = Number(destination);
            data.note = String(document.getElementById('mvmNote')?.value || '').trim();
        }
        if (type === 'entry') {
            const supplier = document.getElementById('mvmSupplier')?.value || '';
            if (!supplier) {
                toast('Le fournisseur est obligatoire pour une entrée.', 'error');
                return false;
            }
            data.unitCost = Number(document.getElementById('mvmUnitCost')?.value || 0);
            data.deliveryNote = String(document.getElementById('mvmDeliveryNote')?.value || '').trim();
            data.supplier = supplier;
        }
        if (type === 'add') {
            const cost = Number(document.getElementById('mvmCost')?.value || 0);
            if (cost > 0) data.unitCost = cost;
            data.note = String(document.getElementById('mvmNote')?.value || '').trim();
        }
        if (type === 'remove') {
            data.reason = document.getElementById('mvmReason')?.value || '';
            data.site = String(document.getElementById('mvmSite')?.value || '').trim();
            data.operator = String(document.getElementById('mvmOper')?.value || '').trim();
        }

        const submitButton = document.getElementById('btnMvTerrainSubmit');
        this._movementSubmitting = true;
        if (submitButton) {
            submitButton.disabled = true;
            submitButton.dataset.originalLabel = submitButton.textContent;
            submitButton.textContent = 'Enregistrement…';
        }

        try {
            await Api.mvAdd(data);
            this.stock = await Api.stock();
            await this.load();
            toast('Mouvement enregistré.', 'success');

            const wasTerrain = !!this._terrainPanel;
            const panel = this._terrainPanel;
            if (wasTerrain) {
                panel?.close('saved');
                global.setTimeout(() => this._openPostSaveSheet(type, data.warehouseId), 240);
            } else {
                Modal.close();
            }
            return true;
        } catch (error) {
            toast(error?.message || 'Impossible d’enregistrer le mouvement.', 'error');
            global.MageStockMobileUI?.announce?.(error?.message || 'Erreur d’enregistrement.', 'assertive');
            return false;
        } finally {
            this._movementSubmitting = false;
            if (submitButton?.isConnected) {
                submitButton.disabled = false;
                submitButton.textContent = submitButton.dataset.originalLabel || 'Enregistrer le mouvement';
            }
        }
    };

    MvPage._openPostSaveSheet = function openPostSaveSheet(type, warehouseId) {
        if (!this.isTerrainMode() || !global.ActionSheet) return;
        const content = document.createElement('div');
        content.className = 'mv-terrain-stock';
        content.innerHTML = '<div class="mv-terrain-stock__item"><span>Résultat</span><strong>Mouvement appliqué au stock</strong></div><div class="mv-terrain-stock__item"><span>Suite</span><strong>Choisissez l’action suivante</strong></div>';
        global.ActionSheet.open({
            title: 'Mouvement enregistré',
            description: TYPE_META[type]?.label || 'Mouvement de stock',
            content,
            actions: [
                {
                    label: 'Scanner le suivant',
                    className: 'btn btn-primary',
                    onClick: () => { global.setTimeout(() => this.openTerrainFlow(type, null, warehouseId, true), 210); }
                },
                {
                    label: 'Même opération',
                    className: 'btn btn-secondary',
                    onClick: () => { global.setTimeout(() => this.openTerrainFlow(type, null, warehouseId, false), 210); }
                },
                { label: 'Terminer', className: 'btn btn-secondary' }
            ]
        });
    };

})(window, document);
