/**
 * Fichier  : assets/js/pages/inventory.js
 * Auteur   : KREMER Régis
 * Version  : 2.7.0
 * Date     : 2026-03-01
 * Description : JS — Inventaires : historique en page, titre, impression, détail
 *
 * Changelog :
 *   v2.2.0 — Version initiale
 *   v2.7.0 — Historique en page (plus de modal), titre obligatoire, impression PDF,
 *             vue détail par inventaire, opérateur enregistré
 */
const InvPage = {
    whs:          [],
    cats:         [],
    invData:      null,
    scannerActive:false,
    _currentInv:  null,

    // ══════════════════════════════════════════════════════════════
    // INIT
    // ══════════════════════════════════════════════════════════════
    async init() {
        const [whs, cats, prods, stockAll] = await Promise.all([
            Api.warehouses(), Api.categories(), Api.products(), Api.stock()
        ]);
        this.whs   = whs   || [];
        this.cats  = cats  || [];
        this.prods = prods || [];
        this.stockAll = stockAll || [];

        // Selects
        const whSel = document.getElementById('invWhSelect');
        if (whSel) whSel.innerHTML = '<option value="">Sélectionner un magasin</option>' +
            this.whs.map(w => `<option value="${w.id}">${w.name}</option>`).join('');

        // Multi-select catégorie (chips visibles)
        this._initCatChips();

        const refreshCats = (wid) => {
            const cats = window.StockCascade
                ? StockCascade.getWarehouseCategories(this.whs || [], wid || '', this.prods || [], this.cats || [])
                : (!wid ? this.cats : []);
            this._buildCatChips(cats);
        };
        refreshCats(''); // init sans filtre
        if (whSel) whSel.addEventListener('change', () => refreshCats(whSel.value));

        // Boutons home
        document.getElementById('btnNewInventory')?.addEventListener('click', () => this._showView('new'));
        document.getElementById('btnBackToHome')?.addEventListener('click', () => this._showView('home'));
        document.getElementById('btnCancelNew')?.addEventListener('click', () => this._showView('home'));

        // Boutons démarrage
        document.getElementById('btnStartInv')?.addEventListener('click', () => this.startInventory());

        // Boutons en cours
        document.getElementById('btnInvCancel')?.addEventListener('click', () => this.cancelInventory());
        document.getElementById('btnInvValidate')?.addEventListener('click', () => this.validateInventory());
        document.getElementById('btnInvScan')?.addEventListener('click', () => this.openScannerModal());
        document.getElementById('btnInvPrint')?.addEventListener('click', () => this.printCurrent());
        document.getElementById('btnInvManualAdd')?.addEventListener('click', () => this.searchAndFocus());
        document.getElementById('btnInvValidateAll')?.addEventListener('click', () => this.validateAll());
        document.getElementById('btnInvResetAll')?.addEventListener('click', () => this.resetAll());

        const scanInput = document.getElementById('invScanInput');
        if (scanInput) scanInput.onkeydown = e => { if (e.key === 'Enter') { e.preventDefault(); this.searchAndFocus(); } };

        // Détail
        document.getElementById('btnBackFromDetail')?.addEventListener('click', () => this._showView('home'));
        document.getElementById('btnDetailPrint')?.addEventListener('click', () => this.printDetail());

        // Charger l'historique
        await this.loadHistory();
    },

    _showView(view) {
        ['home','new','progress','detail'].forEach(v => {
            const el = document.getElementById(`invView${v.charAt(0).toUpperCase()+v.slice(1)}`);
            if (el) el.style.display = v === view ? 'block' : 'none';
        });
    },

    // ══════════════════════════════════════════════════════════════
    // HISTORIQUE (page principale)
    // ══════════════════════════════════════════════════════════════
    async loadHistory() {
        const tbody = document.getElementById('invHistoryBody');
        const countEl = document.getElementById('invHistoryCount');
        try {
            const invs = await Api.invHistory();
            if (countEl) countEl.textContent = (invs.length || 0) + ' inventaire(s)';

            if (!invs.length) {
                tbody.innerHTML = `<tr><td colspan="9" class="tbl-empty">
                    <div style="display:flex;flex-direction:column;align-items:center;gap:10px;padding:24px">
                        <svg viewBox="0 0 24 24" width="36" height="36" fill="none" stroke="currentColor" stroke-width="1.5" style="opacity:.3">
                            <path d="M9 11l3 3L22 4"/><path d="M21 12v7a2 2 0 01-2 2H5a2 2 0 01-2-2V5a2 2 0 012-2h11"/>
                        </svg>
                        <span>Aucun inventaire — cliquez <strong>Nouvel inventaire</strong> pour commencer</span>
                    </div>
                </td></tr>`;
                return;
            }

            tbody.innerHTML = invs.map(inv => {
                const wh    = this.whs.find(w => String(w.id) === String(inv.warehouseId));
                const date  = this._fmtDate(inv.date);
                const lines = inv.lines || [];
                const counted = lines.filter(l => l.counted !== null && l.counted !== undefined).length;
                const disc  = inv.discrepancies || 0;
                const title = inv.title || `Inventaire du ${this._fmtDateShort(inv.date)}`;
                const cat   = Array.isArray(inv.categories) && inv.categories.length > 0
                            ? inv.categories.join(', ')
                            : (inv.category || '—');
                const oper  = inv.operator || '—';

                const resultBadge = disc === 0
                    ? `<span class="badge badge-green">✓ Sans écart</span>`
                    : `<span class="badge badge-orange">${disc} écart${disc > 1 ? 's' : ''}</span>`;

                return `<tr class="inv-history-row" onclick="InvPage.showDetail('${inv.id}')">
                    <td><strong>${this._esc(title)}</strong></td>
                    <td class="text-muted">${date}</td>
                    <td>${wh ? wh.name : '?'}</td>
                    <td class="text-muted">${this._esc(cat)}</td>
                    <td class="cell-number">${counted} / ${lines.length}</td>
                    <td class="cell-number"><strong ${disc > 0 ? 'style="color:var(--orange)"' : ''}>${disc}</strong></td>
                    <td class="text-muted">${this._esc(oper)}</td>
                    <td>${resultBadge}</td>
                    <td class="cell-actions" onclick="event.stopPropagation()">
                        <button class="btn btn-sm btn-secondary" title="Voir le détail"
                            onclick="InvPage.showDetail('${inv.id}')">
                            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
                                <path d="M1 12s4-8 11-8 11 8 11 8-4 8-11 8-11-8-11-8z"/><circle cx="12" cy="12" r="3"/>
                            </svg>
                        </button>
                        <button class="btn btn-sm btn-ghost" title="Imprimer"
                            onclick="InvPage.printById('${inv.id}')">
                            <svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2">
                                <polyline points="6 9 6 2 18 2 18 9"/>
                                <path d="M6 18H4a2 2 0 01-2-2v-5a2 2 0 012-2h16a2 2 0 012 2v5a2 2 0 01-2 2h-2"/>
                                <rect x="6" y="14" width="12" height="8"/>
                            </svg>
                        </button>
                    </td>
                </tr>`;
            }).join('');

        } catch(e) {
            if (tbody) tbody.innerHTML = `<tr><td colspan="9" class="tbl-empty" style="color:var(--red)">Erreur chargement</td></tr>`;
        }
    },

    // ══════════════════════════════════════════════════════════════
    // VUE DÉTAIL
    // ══════════════════════════════════════════════════════════════
    async showDetail(invId) {
        try {
            const invs = await Api.invHistory();
            const inv  = invs.find(i => String(i.id) === String(invId));
            if (!inv) { toast('Inventaire introuvable', 'error'); return; }

            this._currentInv = inv;
            const wh    = this.whs.find(w => String(w.id) === String(inv.warehouseId));
            const title = inv.title || `Inventaire du ${this._fmtDateShort(inv.date)}`;
            const lines = inv.lines || [];
            const counted = lines.filter(l => l.counted !== null).length;
            const disc  = inv.discrepancies || 0;

            // Titre page
            document.getElementById('invDetailTitle').textContent = title;

            // Méta card
            document.getElementById('invDetailMeta').innerHTML = `
                <div class="card-body inv-detail-meta-grid">
                    <div class="inv-detail-meta-item">
                        <span class="inv-detail-meta-label">Date</span>
                        <span class="inv-detail-meta-val">${this._fmtDate(inv.date)}</span>
                    </div>
                    <div class="inv-detail-meta-item">
                        <span class="inv-detail-meta-label">Magasin</span>
                        <span class="inv-detail-meta-val">${wh ? wh.name : '?'}</span>
                    </div>
                    <div class="inv-detail-meta-item">
                        <span class="inv-detail-meta-label">Catégorie(s)</span>
                        <span class="inv-detail-meta-val">${this._esc(this._fmtCategories(inv))}</span>
                    </div>
                    <div class="inv-detail-meta-item">
                        <span class="inv-detail-meta-label">Opérateur</span>
                        <span class="inv-detail-meta-val">${this._esc(inv.operator || '—')}</span>
                    </div>
                    <div class="inv-detail-meta-item">
                        <span class="inv-detail-meta-label">Produits comptés</span>
                        <span class="inv-detail-meta-val">${counted} / ${lines.length}</span>
                    </div>
                    <div class="inv-detail-meta-item">
                        <span class="inv-detail-meta-label">Écarts</span>
                        <span class="inv-detail-meta-val ${disc > 0 ? 'text-orange' : 'text-green'}">
                            <strong>${disc === 0 ? '✓ Aucun écart' : disc + ' écart(s)'}</strong>
                        </span>
                    </div>
                </div>`;

            // Stats badges
            document.getElementById('invDetailStats').innerHTML = `
                <span class="badge badge-blue">${lines.length} produits</span>
                <span class="badge badge-green">${counted} comptés</span>
                ${disc > 0 ? `<span class="badge badge-orange">${disc} écarts</span>` : ''}`;

            // Tableau lignes
            const tbody = document.getElementById('invDetailBody');
            tbody.innerHTML = lines.map(l => {
                const hasCounted = l.counted !== null && l.counted !== undefined;
                const diff = hasCounted ? l.counted - l.expected : null;
                let diffHtml = '<span class="text-muted">—</span>';
                let resultHtml = '<span class="badge badge-gray">Non compté</span>';
                if (hasCounted) {
                    if (diff === 0) {
                        diffHtml = '<span class="text-green font-bold">✓ 0</span>';
                        resultHtml = '<span class="badge badge-green">Conforme</span>';
                    } else {
                        const sign = diff > 0 ? '+' : '';
                        const cls  = diff > 0 ? 'text-orange' : 'text-red';
                        diffHtml   = `<span class="${cls} font-bold">${sign}${diff}</span>`;
                        resultHtml = `<span class="badge badge-orange">Écart ${sign}${diff}</span>`;
                    }
                }
                return `<tr class="${hasCounted && diff !== 0 ? 'inv-row-diff' : hasCounted ? 'inv-row-ok' : ''}">
                    <td><strong>${this._esc(l.name)}</strong></td>
                    <td class="col-mono">${l.sku || '—'}</td>
                    <td class="text-muted">${l.category || '—'}</td>
                    <td class="cell-number">${l.expected}</td>
                    <td class="cell-number">${hasCounted ? l.counted : '<span class="text-muted">—</span>'}</td>
                    <td class="cell-number">${diffHtml}</td>
                    <td>${resultHtml}</td>
                </tr>`;
            }).join('');

            this._showView('detail');
        } catch(e) {
            toast(e.message || 'Erreur', 'error');
        }
    },

    // ══════════════════════════════════════════════════════════════
    // DÉMARRER
    // ══════════════════════════════════════════════════════════════
    async startInventory() {
        const title = document.getElementById('invTitle')?.value?.trim() || '';
        const wid   = document.getElementById('invWhSelect')?.value || '';
        const cats  = this._getSelectedCategories(); // tableau de catégories (ou vide = toutes)

        if (!title) { toast('Veuillez saisir un titre pour l\'inventaire', 'warning'); document.getElementById('invTitle')?.focus(); return; }
        if (!wid)   { toast('Veuillez sélectionner un magasin', 'warning'); return; }

        try {
            const data = await Api.invStart({ warehouseId: parseInt(wid), categories: cats.length > 0 ? cats : null });
            this.invData = { ...data, title, categories: cats };

            // Mettre à jour le header
            const wh = this.whs.find(w => String(w.id) === String(wid));
            const catLabel = cats.length > 0 ? cats.join(', ') : '';
            document.getElementById('invHeaderTitle').textContent = title;
            document.getElementById('invHeaderMeta').textContent =
                (wh ? wh.name : '') + (catLabel ? ' — ' + catLabel : '') + ' — ' + this._fmtDateShort(new Date().toISOString());

            this._showView('progress');
            this.renderItems();
            this.updateStats();
            setTimeout(() => document.getElementById('invScanInput')?.focus(), 100);
            toast(`Inventaire démarré — ${data.lines.length} produits`, 'success');
        } catch(e) {
            toast(e.message, 'error');
        }
    },

    // ══════════════════════════════════════════════════════════════
    // RENDU ITEMS
    // ══════════════════════════════════════════════════════════════
    renderItems() {
        const tbody = document.getElementById('invItemsBody');
        if (!tbody || !this.invData) return;
        const lines = this.invData.lines;

        if (!lines.length) {
            tbody.innerHTML = '<tr><td colspan="7" class="tbl-empty">Aucun produit dans cette sélection</td></tr>';
            return;
        }

        tbody.innerHTML = lines.map((line, i) => {
            const counted    = line.counted;
            const hasCounted = counted !== null && counted !== undefined;
            const diff       = hasCounted ? counted - line.expected : null;

            let diffDisplay = '—', diffClass = '', rowClass = '';
            if (hasCounted) {
                if (diff === 0)      { diffDisplay = '✓ 0';    diffClass = 'text-success'; rowClass = 'inv-row-ok'; }
                else if (diff > 0)   { diffDisplay = `+${diff}`; diffClass = 'text-warning'; rowClass = 'inv-row-diff'; }
                else                 { diffDisplay = `${diff}`;  diffClass = 'text-danger';  rowClass = 'inv-row-diff'; }
            }

            return `<tr id="invRow-${i}" class="${rowClass}">
                <td><strong>${this._esc(line.name)}</strong></td>
                <td class="col-mono">${line.sku || '—'}</td>
                <td class="text-muted" style="font-size:12px">${line.category || '—'}</td>
                <td class="cell-number">${line.expected}</td>
                <td class="cell-number">
                    <input type="number" class="inp inv-count-input ${hasCounted ? 'has-value' : ''}"
                        id="invCount-${i}" min="0" value="${hasCounted ? counted : ''}" placeholder="—"
                        onchange="InvPage.onCountChange(${i})" onfocus="InvPage.onFocusLine(${i})">
                </td>
                <td class="cell-number ${diffClass}"><strong>${diffDisplay}</strong></td>
                <td class="cell-actions">
                    <button class="btn btn-sm btn-secondary" onclick="InvPage.quickCount(${i})" title="Valider comme conforme (copier théorique)">
                        <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5"><polyline points="20 6 9 17 4 12"/></svg>
                    </button>
                    <button class="btn btn-sm btn-ghost" onclick="InvPage.resetLine(${i})" title="Effacer">
                        <svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>
                    </button>
                </td>
            </tr>`;
        }).join('');
    },

    onCountChange(i) {
        const input = document.getElementById('invCount-' + i);
        if (!input || !this.invData) return;
        const v = input.value.trim();
        this.invData.lines[i].counted = v === '' ? null : (parseInt(v) || 0);
        input.classList.toggle('has-value', v !== '');
        this.updateRow(i);
        this.updateStats();
    },

    updateRow(i) {
        const line = this.invData.lines[i];
        const row  = document.getElementById('invRow-' + i);
        if (!row) return;
        const hasCounted = line.counted !== null && line.counted !== undefined;
        const diff = hasCounted ? line.counted - line.expected : null;
        row.className = hasCounted ? (diff === 0 ? 'inv-row-ok' : 'inv-row-diff') : '';
        const diffCell = row.cells[5];
        if (diffCell) {
            let html = '—', cls = '';
            if (hasCounted) {
                if (diff === 0)    { html = '✓ 0';      cls = 'text-success'; }
                else if (diff > 0) { html = `+${diff}`; cls = 'text-warning'; }
                else               { html = `${diff}`;  cls = 'text-danger'; }
            }
            diffCell.className = `cell-number ${cls}`;
            diffCell.innerHTML = `<strong>${html}</strong>`;
        }
    },

    onFocusLine(i) {
        document.querySelectorAll('#invItemsBody tr').forEach(r => r.classList.remove('inv-row-active'));
        document.getElementById('invRow-' + i)?.classList.add('inv-row-active');
    },

    quickCount(i) {
        const line  = this.invData.lines[i];
        const input = document.getElementById('invCount-' + i);
        if (!input) return;
        input.value = line.expected; line.counted = line.expected;
        input.classList.add('has-value');
        this.updateRow(i); this.updateStats();
        this.focusNextEmpty(i);
    },

    resetLine(i) {
        const input = document.getElementById('invCount-' + i);
        if (!input) return;
        input.value = ''; input.classList.remove('has-value');
        this.invData.lines[i].counted = null;
        this.updateRow(i); this.updateStats();
    },

    validateAll() {
        if (!this.invData || !confirm('Valider tous les produits comme conformes au stock théorique ?')) return;
        this.invData.lines.forEach((l, i) => {
            l.counted = l.expected;
            const input = document.getElementById('invCount-' + i);
            if (input) { input.value = l.expected; input.classList.add('has-value'); }
            this.updateRow(i);
        });
        this.updateStats();
        toast('Tous les produits validés comme conformes', 'success');
    },

    resetAll() {
        if (!this.invData || !confirm('Effacer tous les comptages ?')) return;
        this.invData.lines.forEach((l, i) => {
            l.counted = null;
            const input = document.getElementById('invCount-' + i);
            if (input) { input.value = ''; input.classList.remove('has-value'); }
            this.updateRow(i);
        });
        this.updateStats();
    },

    focusNextEmpty(cur) {
        for (let i = cur + 1; i < this.invData.lines.length; i++) {
            const l = this.invData.lines[i];
            if (l.counted === null || l.counted === undefined) {
                const inp = document.getElementById('invCount-' + i);
                if (inp) { inp.focus(); inp.select(); this.scrollToRow(i); }
                return;
            }
        }
    },

    scrollToRow(i) {
        document.getElementById('invRow-' + i)?.scrollIntoView({ behavior: 'smooth', block: 'center' });
    },

    updateStats() {
        if (!this.invData) return;
        const lines = this.invData.lines;
        let checked = 0, diff = 0, pending = 0;
        lines.forEach(l => {
            if (l.counted === null || l.counted === undefined) pending++;
            else if (l.counted === l.expected) checked++;
            else diff++;
        });
        const total = lines.length;
        const pct   = total > 0 ? Math.round(((checked + diff) / total) * 100) : 0;

        document.getElementById('invTotalCount').textContent   = total;
        document.getElementById('invCheckedCount').textContent = checked;
        document.getElementById('invDiffCount').textContent    = diff;
        document.getElementById('invPendingCount').textContent = pending;

        const bar = document.getElementById('invProgressBar');
        const pctEl = document.getElementById('invProgressPct');
        if (bar) bar.style.width = pct + '%';
        if (pctEl) pctEl.textContent = pct + '%';
    },

    searchAndFocus() {
        const input = document.getElementById('invScanInput');
        if (!input || !this.invData) return;
        const code = input.value.trim().toLowerCase();
        if (!code) return;
        const idx = this.invData.lines.findIndex(l =>
            (l.sku || '').toLowerCase() === code ||
            (l.barcode || '').toLowerCase() === code ||
            (l.name || '').toLowerCase().includes(code)
        );
        if (idx >= 0) {
            const inp = document.getElementById('invCount-' + idx);
            if (inp) { inp.focus(); inp.select(); this.scrollToRow(idx); this.onFocusLine(idx); }
            const row = document.getElementById('invRow-' + idx);
            if (row) { row.classList.add('inv-row-highlight'); setTimeout(() => row.classList.remove('inv-row-highlight'), 1500); }
            input.value = '';
        } else {
            toast('Produit non trouvé : ' + code, 'warning');
        }
    },

    // ══════════════════════════════════════════════════════════════
    // VALIDATION
    // ══════════════════════════════════════════════════════════════
    async validateInventory() {
        if (!this.invData) return;
        const counted = this.invData.lines.filter(l => l.counted !== null && l.counted !== undefined);
        if (!counted.length) { toast('Comptez au moins un produit avant de valider', 'warning'); return; }

        const pending = this.invData.lines.length - counted.length;
        const diffs   = counted.filter(l => l.counted !== l.expected).length;
        let msg = `Valider l'inventaire "${this.invData.title}" ?\n\n`;
        msg += `• ${counted.length} produits comptés\n`;
        if (pending > 0) msg += `• ${pending} produits non comptés (ignorés)\n`;
        if (diffs > 0)   msg += `• ${diffs} écarts seront appliqués au stock\n`;
        if (!confirm(msg)) return;

        try {
            await Api.invValidate({
                warehouseId: this.invData.warehouseId,
                title:       this.invData.title,
                categories:  this.invData.categories || [],
                lines:       this.invData.lines,
            });
            toast('Inventaire validé et enregistré !', 'success');
            this.invData = null;
            this.stopScanner();
            this._showView('home');
            await this.loadHistory();
        } catch(e) {
            toast(e.message, 'error');
        }
    },

    cancelInventory() {
        if (!confirm('Abandonner cet inventaire ? Les saisies seront perdues.')) return;
        this.invData = null;
        this.stopScanner();
        this._showView('home');
    },

    // ══════════════════════════════════════════════════════════════
    // IMPRESSION
    // ══════════════════════════════════════════════════════════════
    printCurrent() {
        if (!this.invData) return;
        const inv = {
            title:      this.invData.title,
            date:       new Date().toISOString(),
            warehouseId:this.invData.warehouseId,
            category:   Array.isArray(this.invData.categories) && this.invData.categories.length > 0
                        ? this.invData.categories.join(', ') : '',
            lines:      this.invData.lines,
        };
        const wh = this.whs.find(w => String(w.id) === String(inv.warehouseId));
        this._doPrint(inv, wh, false);
    },

    printDetail() {
        if (!this._currentInv) return;
        const wh = this.whs.find(w => String(w.id) === String(this._currentInv.warehouseId));
        this._doPrint(this._currentInv, wh, true);
    },

    async printById(invId) {
        try {
            const invs = await Api.invHistory();
            const inv  = invs.find(i => String(i.id) === String(invId));
            if (!inv) { toast('Inventaire introuvable', 'error'); return; }
            const wh = this.whs.find(w => String(w.id) === String(inv.warehouseId));
            this._doPrint(inv, wh, true);
        } catch(e) { toast(e.message, 'error'); }
    },

    _doPrint(inv, wh, validated) {
        const title   = inv.title || `Inventaire du ${this._fmtDateShort(inv.date)}`;
        const date    = this._fmtDate(inv.date || new Date().toISOString());
        const lines   = inv.lines || [];
        const company = document.title.replace(' — StockMaster Pro','') || 'StockMaster Pro';

        const rows = lines.map(l => {
            const hasCounted = validated && l.counted !== null && l.counted !== undefined;
            const diff = hasCounted ? l.counted - l.expected : null;
            const diffStr = diff === null ? '' : diff === 0 ? '✓ 0' : (diff > 0 ? `+${diff}` : String(diff));
            return `<tr>
                <td>${this._esc(l.name)}</td>
                <td>${l.sku || '—'}</td>
                <td>${l.category || '—'}</td>
                <td style="text-align:right">${l.expected}</td>
                <td style="text-align:right;border:1px solid #aaa;min-width:50px">${hasCounted ? l.counted : ''}</td>
                <td style="text-align:right;font-weight:700;color:${diff===null?'inherit':diff===0?'green':diff>0?'orange':'red'}">${diffStr}</td>
            </tr>`;
        }).join('');

        const html = `<!DOCTYPE html><html lang="fr"><head><meta charset="UTF-8">
        <title>${title}</title>
        <style>
            body{font-family:Arial,sans-serif;font-size:11px;margin:20px;color:#111}
            h1{font-size:16px;margin:0 0 4px}h2{font-size:13px;margin:0 0 16px;color:#555;font-weight:normal}
            .meta{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:10px;background:#f8f9fa;border-radius:4px;margin-bottom:16px;border:1px solid #ddd}
            .meta-item{display:flex;flex-direction:column;gap:2px}
            .meta-label{font-size:10px;color:#888;text-transform:uppercase;letter-spacing:.05em}
            .meta-val{font-size:12px;font-weight:700}
            table{width:100%;border-collapse:collapse}
            th{background:#f1f3f5;padding:6px 8px;text-align:left;font-size:10px;text-transform:uppercase;letter-spacing:.05em;border-bottom:2px solid #ddd}
            td{padding:5px 8px;border-bottom:1px solid #eee;vertical-align:middle}
            tr:nth-child(even) td{background:#fafafa}
            @media print{body{margin:10px}.no-print{display:none}}
            .footer{margin-top:20px;font-size:10px;color:#aaa;text-align:center}
            .signature-row{display:grid;grid-template-columns:1fr 1fr;gap:32px;margin-top:32px}
            .signature-box{border-top:1px solid #aaa;padding-top:8px;font-size:11px;color:#555}
        </style></head><body>
        <h1>${this._esc(title)}</h1>
        <h2>${company}</h2>
        <div class="meta">
            <div class="meta-item"><span class="meta-label">Date</span><span class="meta-val">${date}</span></div>
            <div class="meta-item"><span class="meta-label">Magasin</span><span class="meta-val">${wh ? this._esc(wh.name) : '?'}</span></div>
            <div class="meta-item"><span class="meta-label">Catégorie(s)</span><span class="meta-val">${this._esc(this._fmtCategories(inv))}</span></div>
            <div class="meta-item"><span class="meta-label">Produits</span><span class="meta-val">${lines.length}</span></div>
            ${inv.operator ? `<div class="meta-item"><span class="meta-label">Opérateur</span><span class="meta-val">${this._esc(inv.operator)}</span></div>` : ''}
            ${validated && inv.discrepancies !== undefined ? `<div class="meta-item"><span class="meta-label">Écarts</span><span class="meta-val" style="color:${inv.discrepancies>0?'orange':'green'}">${inv.discrepancies === 0 ? '✓ Aucun' : inv.discrepancies}</span></div>` : ''}
        </div>
        <table>
            <thead><tr>
                <th>Produit</th><th>SKU</th><th>Catégorie</th>
                <th style="text-align:right">Théorique</th>
                <th style="text-align:right">Compté</th>
                <th style="text-align:right">Écart</th>
            </tr></thead>
            <tbody>${rows}</tbody>
        </table>
        ${!validated ? `
        <div class="signature-row">
            <div class="signature-box">Opérateur (nom + signature) : ___________________</div>
            <div class="signature-box">Date de comptage : ___________________</div>
        </div>` : ''}
        <div class="footer">Généré par StockMaster Pro — ${date}</div>
        <script>window.print();<\/script>
        </body></html>`;

        const w = window.open('', '_blank');
        if (w) { w.document.write(html); w.document.close(); }
    },

    // ══════════════════════════════════════════════════════════════
    // SCANNER (conservé intact)
    // ══════════════════════════════════════════════════════════════
    openScannerModal() {
        Modal.show('Scanner un produit', `
            <div class="scanner-container">
                <div id="invScannerVideo" style="width:100%;height:280px;background:var(--bg-elevated);border-radius:12px;overflow:hidden;position:relative;">
                    <div class="scanner-frame" style="position:absolute;inset:15%;border:2px solid var(--blue);border-radius:8px;pointer-events:none;"></div>
                </div>
                <div class="scanner-result" id="invScannerResult" style="margin-top:12px;padding:12px;background:var(--bg-input);border-radius:8px;display:none;">
                    <strong id="invScannerResultText"></strong>
                </div>
                <div style="margin-top:16px">
                    <label class="lbl">Saisie manuelle</label>
                    <div style="display:flex;gap:8px">
                        <input type="text" class="inp" id="invScannerManual" placeholder="SKU ou code-barres..." style="flex:1">
                        <button class="btn btn-primary" id="btnInvScannerManualGo">OK</button>
                    </div>
                </div>
            </div>`,
            [{ label: 'Fermer', cls: 'btn-secondary', onclick: () => { this.stopScanner(); Modal.close(); }}], 'info');

        const manualInput = document.getElementById('invScannerManual');
        document.getElementById('btnInvScannerManualGo')?.addEventListener('click', () => this.handleScanResult(manualInput?.value));
        if (manualInput) manualInput.onkeydown = e => { if (e.key === 'Enter') this.handleScanResult(manualInput.value); };
        this.startScanner();
    },

    async startScanner() {
        const container = document.getElementById('invScannerVideo');
        if (!container) return;
        if (typeof Html5Qrcode !== 'undefined') {
            try {
                this.html5QrCode = new Html5Qrcode('invScannerVideo');
                await this.html5QrCode.start({ facingMode: 'environment' }, { fps: 10, qrbox: { width: 250, height: 150 } },
                    code => this.handleScanResult(code), () => {});
                this.scannerActive = true; return;
            } catch(e) { console.warn('Html5Qrcode:', e); }
        }
        if (typeof Quagga !== 'undefined') {
            try {
                await new Promise((res, rej) => Quagga.init({ inputStream: { type: 'LiveStream', target: container, constraints: { facingMode: 'environment' } }, decoder: { readers: ['code_128_reader','ean_reader','ean_8_reader','code_39_reader'] }, locate: true }, e => e ? rej(e) : res()));
                Quagga.start();
                Quagga.onDetected(r => { if (r?.codeResult?.code) this.handleScanResult(r.codeResult.code); });
                this.scannerActive = true; return;
            } catch(e) { console.warn('Quagga:', e); }
        }
        toast('Caméra non disponible. Utilisez la saisie manuelle.', 'warning');
    },

    stopScanner() {
        try { this.html5QrCode?.stop(); this.html5QrCode = null; } catch(_) {}
        try { if (typeof Quagga !== 'undefined' && this.scannerActive) Quagga.stop(); } catch(_) {}
        this.scannerActive = false;
    },

    handleScanResult(code) {
        if (!code || !this.invData) return;
        code = code.trim();
        const idx = this.invData.lines.findIndex(l =>
            (l.sku || '').toLowerCase() === code.toLowerCase() ||
            (l.barcode || '').toLowerCase() === code.toLowerCase()
        );
        if (idx >= 0) {
            const line = this.invData.lines[idx];
            const res = document.getElementById('invScannerResult');
            const txt = document.getElementById('invScannerResultText');
            if (res && txt) { res.style.display = 'block'; txt.innerHTML = `✓ ${line.name} <span class="text-muted">(${line.sku})</span>`; }
            this.stopScanner(); Modal.close();
            setTimeout(() => {
                const inp = document.getElementById('invCount-' + idx);
                if (inp) { inp.focus(); inp.select(); this.scrollToRow(idx); this.onFocusLine(idx); }
                const row = document.getElementById('invRow-' + idx);
                if (row) { row.classList.add('inv-row-highlight'); setTimeout(() => row.classList.remove('inv-row-highlight'), 1500); }
            }, 100);
            toast(`Produit trouvé : ${line.name}`, 'success');
        } else {
            toast('Produit non trouvé : ' + code, 'warning');
            const inp = document.getElementById('invScannerManual');
            if (inp) { inp.value = ''; inp.focus(); }
        }
    },

    // ══════════════════════════════════════════════════════════════
    // SÉLECTEUR CATÉGORIES (compact, scroll, recherche)
    // ══════════════════════════════════════════════════════════════
    _allCats: [],

    _initCatChips() {
        const container = document.getElementById('invCatChips');
        const btnAll    = document.getElementById('invCatBtnAll');
        const search    = document.getElementById('invCatSearch');
        const scroll    = document.getElementById('invCatScroll');
        if (!container) return;

        // Click sur chip individuel
        container.addEventListener('click', (e) => {
            const chip = e.target.closest('.inv-cat-chip');
            if (!chip) return;
            chip.classList.toggle('active');
            this._syncCatUI();
        });

        // Bouton "Toutes"
        if (btnAll) btnAll.addEventListener('click', () => {
            container.querySelectorAll('.inv-cat-chip.active').forEach(c => c.classList.remove('active'));
            this._syncCatUI();
        });

        // Recherche temps réel
        if (search) search.addEventListener('input', () => {
            const q = search.value.trim().toLowerCase();
            const chips = container.querySelectorAll('.inv-cat-chip');
            let visible = 0;
            chips.forEach(c => {
                const match = !q || (c.dataset.catValue || '').toLowerCase().includes(q);
                c.classList.toggle('filtered-out', !match);
                if (match) visible++;
            });
            // No match message
            let noMatch = container.querySelector('.inv-cat-no-match');
            if (visible === 0 && q) {
                if (!noMatch) { noMatch = document.createElement('div'); noMatch.className = 'inv-cat-no-match'; container.appendChild(noMatch); }
                noMatch.textContent = 'Aucune catégorie trouvée';
                noMatch.style.display = '';
            } else if (noMatch) {
                noMatch.style.display = 'none';
            }
            this._updateScrollFade();
        });

        // Scroll fade
        if (scroll) scroll.addEventListener('scroll', () => this._updateScrollFade());

        // Click sur tags sélectionnés (retirer)
        const selected = document.getElementById('invCatSelected');
        if (selected) selected.addEventListener('click', (e) => {
            const tag = e.target.closest('.inv-cat-sel-tag');
            if (!tag) return;
            const val = tag.dataset.catValue;
            const chip = container.querySelector(`.inv-cat-chip[data-cat-value="${val}"]`);
            if (chip) chip.classList.remove('active');
            this._syncCatUI();
        });
    },

    _buildCatChips(cats) {
        const container = document.getElementById('invCatChips');
        const search    = document.getElementById('invCatSearch');
        if (!container) return;
        this._allCats = cats || [];

        if (search) search.value = '';

        if (!this._allCats.length) {
            container.innerHTML = '<div class="inv-cat-empty">Sélectionnez un magasin pour voir les catégories</div>';
            this._syncCatUI();
            return;
        }

        container.innerHTML = this._allCats.map(c =>
            `<button type="button" class="inv-cat-chip" data-cat-value="${this._esc(c)}">${this._esc(c)}</button>`
        ).join('');

        this._syncCatUI();
        // Delay pour que le DOM soit rendu avant de calculer le fade
        requestAnimationFrame(() => this._updateScrollFade());
    },

    _syncCatUI() {
        const container = document.getElementById('invCatChips');
        const btnAll    = document.getElementById('invCatBtnAll');
        const counterEl = document.getElementById('invCatCounter');
        const selEl     = document.getElementById('invCatSelected');
        if (!container) return;

        const selected = this._getSelectedCategories();
        const isAll = selected.length === 0;

        // Bouton "Toutes"
        if (btnAll) btnAll.classList.toggle('inactive', !isAll);

        // Compteur
        if (counterEl) {
            counterEl.innerHTML = isAll
                ? `<strong>${this._allCats.length}</strong> catégories — toutes incluses`
                : `<strong>${selected.length}</strong> / ${this._allCats.length} sélectionnée${selected.length > 1 ? 's' : ''}`;
        }

        // Tags sélectionnés
        if (selEl) {
            selEl.innerHTML = selected.map(c =>
                `<span class="inv-cat-sel-tag" data-cat-value="${this._esc(c)}">${this._esc(c)} <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="3"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg></span>`
            ).join('');
        }

        this._updateScrollFade();
    },

    _updateScrollFade() {
        const scroll = document.getElementById('invCatScroll');
        const fade   = document.getElementById('invCatFade');
        if (!scroll || !fade) return;
        const hasMore = scroll.scrollHeight > scroll.clientHeight + 4;
        const atBottom = scroll.scrollTop + scroll.clientHeight >= scroll.scrollHeight - 4;
        fade.classList.toggle('hidden', !hasMore || atBottom);
    },

    _getSelectedCategories() {
        const container = document.getElementById('invCatChips');
        if (!container) return [];
        return [...container.querySelectorAll('.inv-cat-chip.active')].map(c => c.dataset.catValue).filter(Boolean);
    },

    /** Format categories for display — backward compatible (string or array) */
    _fmtCategories(inv) {
        if (Array.isArray(inv.categories) && inv.categories.length > 0) {
            return inv.categories.join(', ');
        }
        // Backward compat: old inventories stored a single string
        if (inv.category && inv.category !== '') return inv.category;
        return 'Toutes';
    },

    // ══════════════════════════════════════════════════════════════
    // HELPERS
    // ══════════════════════════════════════════════════════════════
    _esc(s) {
        return String(s || '').replace(/[&<>"']/g, m =>
            ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;' }[m]));
    },
    _fmtDate(iso) {
        if (!iso) return '—';
        try { return new Date(iso).toLocaleDateString('fr-FR', { day:'2-digit', month:'short', year:'numeric', hour:'2-digit', minute:'2-digit' }); } catch { return iso; }
    },
    _fmtDateShort(iso) {
        if (!iso) return '—';
        try { return new Date(iso).toLocaleDateString('fr-FR', { day:'2-digit', month:'2-digit', year:'numeric' }); } catch { return iso; }
    },
};
