/**
 * Fichier  : assets/js/pages/reports-mobile.js
 * Auteur   : KREMER Régis
 * Version  : 2.6.85
 * Date     : 2026-07-20
 * Description : Rapports mobile/tablette — sélecteur, filtres en tiroir, cartes natives, graphiques et mode comparatif.
 *
 * Changelog :
 *   v2.6.85 (2026-07-20) — REPORTS MOBILE CARDS9 : application du socle mobile aux sept rapports.
 */
(function reportsMobileCards(global, document) {
    'use strict';

    if (!global || !document || global.MageStockReportsMobile) return;

    const CONFIG = {
        stock: {
            title: 'Stock',
            description: 'Quantités, seuils et valeur par produit.',
            tableId: 'stockTable',
            printId: 'btnStockPrint',
            csvId: 'btnStockCSV',
            filters: [
                ['stockSearch', 'Rechercher', 'search'],
                ['stockWarehouse', 'Magasin', 'select'],
                ['stockCategory', 'Catégorie', 'select'],
                ['stockBrand', 'Marque', 'select'],
                ['stockThreshold', 'État du stock', 'select']
            ],
            resetId: 'btnStockReset',
            comparable: false
        },
        value: {
            title: 'Valorisation',
            description: 'Comparaison de la valeur du stock entre magasins.',
            tableId: 'valueTable',
            printId: 'btnValuePrint',
            csvId: 'btnValueCSV',
            filters: [],
            comparable: true
        },
        movements: {
            title: 'Mouvements',
            description: 'Entrées, sorties et transferts sur la période.',
            tableId: 'movementsTable',
            printId: 'btnMovementsPrint',
            csvId: 'btnMovementsCSV',
            filters: [
                ['movementsDateFrom', 'Du', 'date'],
                ['movementsDateTo', 'Au', 'date'],
                ['movementsWarehouse', 'Magasin', 'select'],
                ['movementsType', 'Type de mouvement', 'select']
            ],
            resetId: 'btnMovementsReset',
            comparable: false
        },
        alerts: {
            title: 'Alertes',
            description: 'Produits sous leur seuil de sécurité.',
            tableId: 'alertsTable',
            printId: 'btnAlertsPrint',
            csvId: 'btnAlertsCSV',
            filters: [],
            comparable: false
        },
        brands: {
            title: 'Marques',
            description: 'Poids des marques dans le catalogue et le stock.',
            tableId: 'brandsTable',
            printId: 'btnBrandsPrint',
            csvId: 'btnBrandsCSV',
            filters: [],
            comparable: true
        },
        gap: {
            title: 'Écarts d’inventaire',
            description: 'Comparaison entre stock théorique et dernier comptage.',
            tableId: 'gapTable',
            printId: 'btnGapPrint',
            csvId: 'btnGapCSV',
            filters: [],
            comparable: true
        },
        prices: {
            title: 'Historique des prix',
            description: 'Évolution des prix d’achat par produit et fournisseur.',
            tableId: 'pricesTable',
            printId: 'btnPricesPrint',
            csvId: 'btnPricesCSV',
            filters: [['pricesProductFilter', 'Produit', 'select']],
            resetId: 'btnPricesReset',
            comparable: false
        }
    };

    const ORDER = ['stock', 'value', 'movements', 'alerts', 'brands', 'gap', 'prices'];
    const PAGE_SIZE = 30;

    function escapeHtml(value) {
        if (global.MageStockMobileUI?.escapeHtml) return global.MageStockMobileUI.escapeHtml(value);
        return String(value == null ? '' : value)
            .replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
            .replace(/"/g, '&quot;').replace(/'/g, '&#039;');
    }

    function isTactileLayout() {
        return global.MageStockMobileUI?.device?.profile !== 'desktop'
            || global.matchMedia?.('(max-width: 1199.98px)').matches;
    }

    function compactText(value) {
        return String(value == null ? '' : value).replace(/\s+/g, ' ').trim();
    }

    function parseNumeric(value) {
        let normalized = compactText(value).replace(/\u00a0/g, ' ').replace(/\s/g, '');
        normalized = normalized.replace(/[^0-9,.-]/g, '');
        if (normalized.includes(',') && normalized.includes('.')) {
            if (normalized.lastIndexOf(',') > normalized.lastIndexOf('.')) normalized = normalized.replace(/\./g, '').replace(',', '.');
            else normalized = normalized.replace(/,/g, '');
        } else if (normalized.includes(',')) normalized = normalized.replace(',', '.');
        const number = Number.parseFloat(normalized);
        return Number.isFinite(number) ? number : 0;
    }


    function getReportPage() {
        try { if (typeof ReportPage !== 'undefined') return ReportPage; } catch (_) {}
        return global.ReportPage || null;
    }

    function triggerSource(id) {
        const element = document.getElementById(id);
        if (!element) return;
        element.click();
    }

    const ReportsMobile = {
        initialized: false,
        currentTab: 'stock',
        shell: null,
        title: null,
        description: null,
        filterButton: null,
        filterCount: null,
        filterSummary: null,
        limits: Object.fromEntries(ORDER.map((key) => [key, PAGE_SIZE])),
        charts: {},

        init() {
            if (this.initialized || !document.getElementById('pgReports')) return;
            this.initialized = true;
            this.currentTab = getReportPage()?.currentTab || 'stock';
            this.prepareSourceTables();
            this.buildShell();
            this.prepareMobileHosts();
            this.bindDeviceEvents();
            this.updateShell();
            this.refresh(this.currentTab, { resetLimit: true });
        },

        prepareSourceTables() {
            ORDER.forEach((key) => {
                const table = document.getElementById(CONFIG[key].tableId);
                const card = table?.closest('.card');
                if (card) card.classList.add('reports-source-table-card');
                if (table) table.dataset.reportsTable = key;
                const tab = document.getElementById('tab' + key.charAt(0).toUpperCase() + key.slice(1));
                if (tab) tab.dataset.reportKey = key;
            });
        },

        buildShell() {
            const tabs = document.getElementById('reportTabs');
            if (!tabs) return;
            const shell = document.createElement('section');
            shell.className = 'reports-mobile-shell';
            shell.setAttribute('aria-label', 'Navigation des rapports');
            shell.innerHTML = `
                <button type="button" class="reports-mobile-selector" id="reportsMobileSelector" aria-haspopup="dialog">
                    <span class="reports-mobile-selector__icon" aria-hidden="true">▥</span>
                    <span class="reports-mobile-selector__copy">
                        <strong id="reportsMobileTitle">Stock</strong>
                        <small id="reportsMobileDescription">Quantités, seuils et valeur par produit.</small>
                    </span>
                    <span class="reports-mobile-selector__chevron" aria-hidden="true">⌄</span>
                </button>
                <div class="reports-mobile-quick-actions">
                    <button type="button" class="btn btn-secondary reports-mobile-quick" id="reportsMobileFilters">
                        <span aria-hidden="true">☷</span><span>Filtres</span><span class="reports-mobile-count" id="reportsMobileFilterCount" hidden>0</span>
                    </button>
                    <button type="button" class="btn btn-secondary reports-mobile-quick" id="reportsMobileActions">
                        <span aria-hidden="true">⋯</span><span>Actions</span>
                    </button>
                </div>
                <div class="reports-mobile-filter-summary" id="reportsMobileFilterSummary" aria-live="polite"></div>
            `;
            tabs.parentNode.insertBefore(shell, tabs);
            this.shell = shell;
            this.title = shell.querySelector('#reportsMobileTitle');
            this.description = shell.querySelector('#reportsMobileDescription');
            this.filterButton = shell.querySelector('#reportsMobileFilters');
            this.filterCount = shell.querySelector('#reportsMobileFilterCount');
            this.filterSummary = shell.querySelector('#reportsMobileFilterSummary');

            shell.querySelector('#reportsMobileSelector').addEventListener('click', (event) => this.openReportSelector(event.currentTarget));
            this.filterButton.addEventListener('click', (event) => this.openFilters(event.currentTarget));
            shell.querySelector('#reportsMobileActions').addEventListener('click', (event) => this.openActions(event.currentTarget));
        },

        prepareMobileHosts() {
            ORDER.forEach((key) => {
                const tab = this.getTabElement(key);
                const tableCard = document.getElementById(CONFIG[key].tableId)?.closest('.card');
                if (!tab || !tableCard) return;

                let chart = tab.querySelector('.reports-mobile-chart');
                if (!chart) {
                    chart = document.createElement('section');
                    chart.className = 'reports-mobile-chart';
                    chart.hidden = true;
                    chart.innerHTML = `
                        <div class="reports-mobile-section-heading">
                            <div><span>Vue synthétique</span><strong>Graphique du rapport</strong></div>
                        </div>
                        <div class="reports-mobile-chart__canvas"><canvas role="img"></canvas></div>
                    `;
                    tableCard.parentNode.insertBefore(chart, tableCard);
                }

                let host = tab.querySelector('.reports-mobile-cards');
                if (!host) {
                    host = document.createElement('section');
                    host.className = 'reports-mobile-cards';
                    host.id = 'reportsMobileCards' + key.charAt(0).toUpperCase() + key.slice(1);
                    host.setAttribute('aria-label', 'Résultats du rapport ' + CONFIG[key].title);
                    tableCard.parentNode.insertBefore(host, tableCard);
                }
            });
        },

        bindDeviceEvents() {
            document.addEventListener('magestock:devicechange', () => {
                if (isTactileLayout()) this.refresh(this.currentTab, { preserveLimit: true });
                else this.destroyCharts();
            });
        },

        getTabElement(key) {
            return document.getElementById('tab' + key.charAt(0).toUpperCase() + key.slice(1));
        },

        getRows(key) {
            const table = document.getElementById(CONFIG[key].tableId);
            if (!table) return [];
            const headers = Array.from(table.querySelectorAll('thead th')).map((th) => compactText(th.textContent));
            return Array.from(table.querySelectorAll('tbody tr')).filter((row) => {
                return !row.querySelector('.tbl-empty') && row.querySelectorAll('td').length > 1;
            }).map((row) => {
                const cells = Array.from(row.querySelectorAll('td'));
                const data = {};
                cells.forEach((cell, index) => {
                    const label = cell.dataset.label || headers[index] || ('Colonne ' + (index + 1));
                    data[label] = compactText(cell.textContent);
                });
                return { row, cells, data };
            });
        },

        refresh(key, options) {
            if (!this.initialized) return;
            const tabKey = CONFIG[key] ? key : this.currentTab;
            if (options?.resetLimit) this.limits[tabKey] = PAGE_SIZE;
            this.renderCards(tabKey);
            this.renderChart(tabKey);
            if (tabKey === this.currentTab) this.updateShell();
        },

        onTabChanged(key) {
            if (!CONFIG[key]) return;
            this.currentTab = key;
            this.getTabElement(key)?.classList.remove('reports-mobile-table-mode');
            this.limits[key] = PAGE_SIZE;
            this.updateShell();
            this.refresh(key, { resetLimit: true });
            if (isTactileLayout()) {
                const top = this.shell?.getBoundingClientRect().top || 0;
                if (top < 0) this.shell?.scrollIntoView({ block: 'start', behavior: 'smooth' });
            }
        },

        updateShell() {
            const config = CONFIG[this.currentTab] || CONFIG.stock;
            if (this.title) this.title.textContent = config.title;
            if (this.description) this.description.textContent = config.description;
            const hasFilters = !!config.filters.length;
            if (this.filterButton) this.filterButton.hidden = !hasFilters;
            this.renderFilterSummary();
        },

        openReportSelector(trigger) {
            if (!global.ActionSheet) return;
            const content = document.createElement('div');
            content.className = 'reports-mobile-report-list';
            let handle = null;
            ORDER.forEach((key) => {
                const config = CONFIG[key];
                const button = document.createElement('button');
                button.type = 'button';
                button.className = 'reports-mobile-report-option';
                if (key === this.currentTab) button.classList.add('is-current');
                button.setAttribute('aria-current', key === this.currentTab ? 'page' : 'false');
                button.innerHTML = `<span><strong>${escapeHtml(config.title)}</strong><small>${escapeHtml(config.description)}</small></span><span aria-hidden="true">${key === this.currentTab ? '✓' : '›'}</span>`;
                button.addEventListener('click', async () => {
                    await getReportPage()?.switchTab?.(key);
                    handle?.close('select');
                });
                content.appendChild(button);
            });
            handle = global.ActionSheet.open({
                title: 'Choisir un rapport',
                description: 'Une seule vue métier est affichée à la fois.',
                content,
                trigger
            });
        },

        openFilters(trigger) {
            const config = CONFIG[this.currentTab];
            if (!config?.filters?.length || !global.FilterDrawer) return;
            const form = document.createElement('form');
            form.className = 'reports-mobile-filter-form';
            form.addEventListener('submit', (event) => event.preventDefault());

            config.filters.forEach(([sourceId, labelText, kind]) => {
                const source = document.getElementById(sourceId);
                if (!source) return;
                const field = document.createElement('div');
                field.className = 'reports-mobile-filter-field';
                const label = document.createElement('label');
                const cloneId = 'reports-filter-' + sourceId;
                label.htmlFor = cloneId;
                label.textContent = labelText;
                const clone = source.cloneNode(true);
                clone.id = cloneId;
                clone.removeAttribute('style');
                clone.dataset.sourceId = sourceId;
                clone.classList.remove('btn-sm');
                if (kind === 'search') {
                    clone.type = 'search';
                    clone.setAttribute('enterkeyhint', 'search');
                }
                clone.value = source.value;
                const sync = (event) => {
                    source.value = clone.value;
                    source.dispatchEvent(new Event(event.type === 'input' ? 'input' : 'change', { bubbles: true }));
                    global.setTimeout(() => {
                        form.querySelectorAll('[data-source-id]').forEach((control) => {
                            const liveSource = document.getElementById(control.dataset.sourceId);
                            if (!liveSource) return;
                            if (control.tagName === 'SELECT' && control.innerHTML !== liveSource.innerHTML) control.innerHTML = liveSource.innerHTML;
                            control.value = liveSource.value;
                        });
                        this.renderFilterSummary();
                        this.refresh(this.currentTab, { resetLimit: true });
                    }, 30);
                };
                clone.addEventListener('input', sync);
                clone.addEventListener('change', sync);
                field.append(label, clone);
                form.appendChild(field);
            });

            const handle = global.FilterDrawer.open({
                title: 'Filtres — ' + config.title,
                description: 'Les résultats sont actualisés immédiatement.',
                content: form,
                trigger,
                actions: [
                    {
                        label: 'Réinitialiser',
                        className: 'btn btn-secondary',
                        close: false,
                        onClick: () => {
                            if (config.resetId) triggerSource(config.resetId);
                            global.setTimeout(() => {
                                form.querySelectorAll('[data-source-id]').forEach((control) => {
                                    const source = document.getElementById(control.dataset.sourceId);
                                    if (!source) return;
                                    if (control.tagName === 'SELECT') control.innerHTML = source.innerHTML;
                                    control.value = source.value;
                                });
                                this.renderFilterSummary();
                                this.refresh(this.currentTab, { resetLimit: true });
                            }, 30);
                            return false;
                        }
                    },
                    { label: 'Afficher les résultats', className: 'btn btn-primary' }
                ]
            });
            return handle;
        },

        openActions(trigger) {
            const config = CONFIG[this.currentTab];
            if (!config || !global.ActionSheet) return;
            const content = document.createElement('div');
            content.className = 'reports-mobile-action-list';
            let handle = null;

            const addAction = (label, detail, callback, className) => {
                const button = document.createElement('button');
                button.type = 'button';
                button.className = 'reports-mobile-action-option ' + (className || '');
                button.innerHTML = `<span><strong>${escapeHtml(label)}</strong><small>${escapeHtml(detail)}</small></span><span aria-hidden="true">›</span>`;
                button.addEventListener('click', () => {
                    callback();
                    handle?.close('action');
                });
                content.appendChild(button);
            };

            addAction('Imprimer', 'Ouvrir la mise en page A4 du rapport actif.', () => triggerSource(config.printId));
            addAction('Exporter en CSV', 'Télécharger toutes les données du rapport actif.', () => triggerSource(config.csvId));
            if (config.comparable) {
                const tab = this.getTabElement(this.currentTab);
                const showing = tab?.classList.contains('reports-mobile-table-mode');
                addAction(showing ? 'Revenir aux cartes' : 'Afficher le tableau comparatif', showing
                    ? 'Retrouver la lecture synthétique adaptée au tactile.'
                    : 'Comparer les colonnes dans un tableau à défilement contrôlé.', () => this.toggleComparativeTable());
            }

            handle = global.ActionSheet.open({
                title: 'Actions — ' + config.title,
                description: 'Impression et export restent fondés sur le tableau complet.',
                content,
                trigger
            });
        },

        toggleComparativeTable() {
            const tab = this.getTabElement(this.currentTab);
            if (!tab || !CONFIG[this.currentTab]?.comparable) return;
            const enabled = !tab.classList.contains('reports-mobile-table-mode');
            tab.classList.toggle('reports-mobile-table-mode', enabled);
            global.MageStockMobileUI?.announce?.(enabled ? 'Tableau comparatif affiché.' : 'Cartes synthétiques affichées.');
        },

        activeFilters() {
            const config = CONFIG[this.currentTab];
            if (!config?.filters?.length) return [];
            return config.filters.map(([sourceId, labelText]) => {
                const source = document.getElementById(sourceId);
                if (!source || !source.value) return null;
                let value = source.value;
                if (source.tagName === 'SELECT') value = source.selectedOptions?.[0]?.textContent || value;
                return { id: sourceId, label: labelText, value: compactText(value) };
            }).filter(Boolean);
        },

        renderFilterSummary() {
            if (!this.filterSummary || !this.filterCount) return;
            const filters = this.activeFilters();
            this.filterCount.hidden = !filters.length;
            this.filterCount.textContent = String(filters.length);
            this.filterSummary.innerHTML = '';
            filters.slice(0, 3).forEach((filter) => {
                const chip = document.createElement('span');
                chip.className = 'reports-mobile-filter-chip';
                chip.textContent = filter.label + ' : ' + filter.value;
                this.filterSummary.appendChild(chip);
            });
            if (filters.length > 3) {
                const more = document.createElement('span');
                more.className = 'reports-mobile-filter-chip';
                more.textContent = '+' + (filters.length - 3) + ' filtre(s)';
                this.filterSummary.appendChild(more);
            }
        },

        renderCards(key) {
            const host = document.getElementById('reportsMobileCards' + key.charAt(0).toUpperCase() + key.slice(1));
            if (!host) return;
            const rows = this.getRows(key);
            host.innerHTML = '';

            if (!rows.length) {
                const empty = document.createElement('div');
                empty.className = 'reports-mobile-empty';
                empty.innerHTML = '<strong>Aucune donnée à afficher</strong><span>Modifiez les filtres ou actualisez le rapport.</span>';
                host.appendChild(empty);
                return;
            }

            const list = document.createElement('div');
            list.className = 'reports-mobile-card-grid';
            host.appendChild(list);
            const limit = Math.min(rows.length, this.limits[key] || PAGE_SIZE);
            rows.slice(0, limit).forEach((row, index) => {
                const card = this.createCard(key, row, index);
                if (card) list.appendChild(card);
            });

            const footer = document.createElement('div');
            footer.className = 'reports-mobile-list-footer';
            const count = document.createElement('span');
            count.textContent = limit + ' résultat(s) affiché(s) sur ' + rows.length;
            footer.appendChild(count);
            if (limit < rows.length) {
                const more = document.createElement('button');
                more.type = 'button';
                more.className = 'btn btn-secondary';
                more.textContent = 'Afficher ' + Math.min(PAGE_SIZE, rows.length - limit) + ' de plus';
                more.addEventListener('click', () => {
                    this.limits[key] = limit + PAGE_SIZE;
                    this.renderCards(key);
                });
                footer.appendChild(more);
            }
            host.appendChild(footer);
        },

        createCard(key, row, index) {
            const d = row.data;
            const details = this.createDetails(key, row);
            const common = { id: key + '-' + index, content: details };
            let config;

            switch (key) {
                case 'stock':
                    config = {
                        eyebrow: d.Magasin || '',
                        title: d.Produit || 'Produit',
                        meta: [d.SKU, d.Catégorie].filter((v) => v && v !== '—').join(' · '),
                        badge: d.Statut || '',
                        badgeClass: /bas/i.test(d.Statut || '') ? 'is-danger' : 'is-success',
                        state: /bas/i.test(d.Statut || '') ? 'warning' : 'ok',
                        fields: [
                            { label: 'Stock', value: d['Stock actuel'] || '0' },
                            { label: 'Seuil', value: d['Seuil mini'] || '0' },
                            { label: 'Valeur', value: d['Valeur totale'] || '—' }
                        ]
                    };
                    break;
                case 'value':
                    config = {
                        eyebrow: 'Magasin', title: d.Magasin || 'Magasin',
                        badge: d['Valeur totale'] || '—',
                        fields: [
                            { label: 'Produits', value: d.Produits || '0' },
                            { label: 'Unités', value: d.Unités || '0' },
                            { label: 'Moyenne / produit', value: d['Valeur moyenne / produit'] || d['Moyenne / produit'] || '—' }
                        ]
                    };
                    break;
                case 'movements':
                    config = {
                        eyebrow: d['Date / Heure'] || d.Date || '',
                        title: d.Produit || 'Mouvement',
                        meta: d.Magasin || '', badge: d.Type || '',
                        badgeClass: /sortie/i.test(d.Type || '') ? 'is-danger' : (/transfert/i.test(d.Type || '') ? 'is-info' : 'is-success'),
                        fields: [
                            { label: 'Quantité', value: d.Quantité || '—' },
                            { label: 'Motif / chantier', value: d['Raison / Chantier'] || d['Motif / site'] || '—' },
                            { label: 'Opérateur', value: d.Opérateur || d.Agent || '—' }
                        ]
                    };
                    break;
                case 'alerts': {
                    const originalButton = row.row.querySelector('button');
                    config = {
                        eyebrow: d.Magasin || '', title: d.Produit || 'Alerte',
                        meta: d.SKU || '', badge: d.Priorité || 'Alerte', badgeClass: 'is-danger', state: 'warning',
                        fields: [
                            { label: 'Stock', value: d['Stock actuel'] || '0' },
                            { label: 'Seuil', value: d['Seuil mini'] || '0' },
                            { label: 'Déficit', value: d.Déficit || '0', className: 'is-danger' }
                        ],
                        actions: originalButton ? [{ label: 'Approvisionner', className: 'btn btn-primary', onClick: () => originalButton.click() }] : []
                    };
                    break;
                }
                case 'brands':
                    config = {
                        eyebrow: 'Marque', title: d.Marque || 'Sans marque', meta: d.Fournisseurs || '',
                        badge: d['Valeur totale'] || '—',
                        fields: [
                            { label: 'Produits', value: d.Produits || '0' },
                            { label: 'Stock total', value: d['Stock total'] || '0' }
                        ]
                    };
                    break;
                case 'gap': {
                    const gap = d['Écart qté'] || d.Écart || '0';
                    const gapValue = parseNumeric(gap);
                    config = {
                        eyebrow: d.Magasin || '', title: d.Produit || 'Produit', meta: d.SKU || '',
                        badge: gap, badgeClass: gapValue === 0 ? 'is-success' : 'is-danger', state: gapValue === 0 ? 'ok' : 'warning',
                        fields: [
                            { label: 'Théorique', value: d.Théorique || d['Stock théorique'] || '0' },
                            { label: 'Compté', value: d.Compté || d['Stock compté'] || '0' },
                            { label: 'Valeur de l’écart', value: d['Écart valeur'] || d['Valeur écart'] || '—' }
                        ]
                    };
                    break;
                }
                case 'prices':
                    config = {
                        eyebrow: d.Date || '', title: d.Produit || 'Produit',
                        meta: [d.SKU, d.Fournisseur].filter((v) => v && v !== '—').join(' · '),
                        badge: d['Prix base'] || '—',
                        fields: [
                            { label: 'Quantité', value: d.Quantité || '—' },
                            { label: 'Prix unitaire', value: d['Prix unitaire'] || '—' }
                        ]
                    };
                    break;
                default:
                    return null;
            }

            return global.MobileEntityCard?.create(Object.assign(common, config)) || null;
        },

        createDetails(key, row) {
            const excluded = {
                stock: ['Produit', 'SKU', 'Magasin', 'Catégorie', 'Stock actuel', 'Seuil mini', 'Statut', 'Valeur totale'],
                value: ['Magasin', 'Produits', 'Unités', 'Valeur totale', 'Valeur moyenne / produit', 'Moyenne / produit'],
                movements: ['Date / Heure', 'Date', 'Type', 'Produit', 'Magasin', 'Quantité', 'Raison / Chantier', 'Motif / site', 'Opérateur', 'Agent'],
                alerts: ['Priorité', 'Produit', 'SKU', 'Magasin', 'Stock actuel', 'Seuil mini', 'Déficit', 'Action'],
                brands: ['Marque', 'Produits', 'Stock total', 'Valeur totale', 'Fournisseurs'],
                gap: ['Produit', 'SKU', 'Magasin', 'Théorique', 'Stock théorique', 'Compté', 'Stock compté', 'Écart qté', 'Écart', 'Écart valeur', 'Valeur écart'],
                prices: ['Date', 'Produit', 'SKU', 'Quantité', 'Prix unitaire', 'Prix base', 'Fournisseur']
            }[key] || [];
            const entries = Object.entries(row.data).filter(([label, value]) => !excluded.includes(label) && value && value !== '—');
            if (!entries.length) return null;
            const details = document.createElement('details');
            details.className = 'reports-mobile-card-details';
            const summary = document.createElement('summary');
            summary.textContent = 'Plus de détails';
            const list = document.createElement('dl');
            entries.forEach(([label, value]) => {
                const item = document.createElement('div');
                const term = document.createElement('dt');
                const definition = document.createElement('dd');
                term.textContent = label;
                definition.textContent = value;
                item.append(term, definition);
                list.appendChild(item);
            });
            details.append(summary, list);
            return details;
        },

        renderChart(key) {
            const tab = this.getTabElement(key);
            const section = tab?.querySelector('.reports-mobile-chart');
            const canvas = section?.querySelector('canvas');
            if (!section || !canvas) return;
            this.destroyChart(key);
            if (!isTactileLayout() || typeof global.Chart !== 'function') {
                section.hidden = true;
                return;
            }
            const rows = this.getRows(key);
            const definition = this.chartDefinition(key, rows);
            if (!definition || !definition.data.labels.length) {
                section.hidden = true;
                return;
            }
            section.hidden = false;
            canvas.setAttribute('aria-label', definition.ariaLabel || ('Graphique du rapport ' + CONFIG[key].title));
            const style = getComputedStyle(document.documentElement);
            const textColor = style.getPropertyValue('--text')?.trim() || '#1f2937';
            const gridColor = style.getPropertyValue('--border')?.trim() || 'rgba(148,163,184,.25)';
            const colors = [
                style.getPropertyValue('--primary')?.trim() || '#2563eb',
                style.getPropertyValue('--green')?.trim() || '#16a34a',
                style.getPropertyValue('--orange')?.trim() || '#ea580c',
                style.getPropertyValue('--red')?.trim() || '#dc2626',
                style.getPropertyValue('--purple')?.trim() || '#7c3aed',
                '#0891b2', '#64748b', '#ca8a04'
            ];
            definition.data.datasets.forEach((dataset, index) => {
                if (!dataset.backgroundColor) dataset.backgroundColor = definition.type === 'doughnut' ? colors.slice(0, dataset.data.length) : colors[index % colors.length];
                if (!dataset.borderColor) dataset.borderColor = definition.type === 'doughnut' ? 'transparent' : colors[index % colors.length];
            });
            const options = Object.assign({
                responsive: true,
                maintainAspectRatio: false,
                animation: global.matchMedia?.('(prefers-reduced-motion: reduce)').matches ? false : { duration: 250 },
                plugins: {
                    legend: { display: definition.type === 'doughnut', position: 'bottom', labels: { color: textColor, boxWidth: 14 } },
                    tooltip: { intersect: false }
                },
                scales: definition.type === 'doughnut' ? undefined : {
                    x: { grid: { color: gridColor }, ticks: { color: textColor, autoSkip: true, maxRotation: 0 } },
                    y: { beginAtZero: true, grid: { color: gridColor }, ticks: { color: textColor } }
                }
            }, definition.options || {});
            this.charts[key] = new global.Chart(canvas.getContext('2d'), { type: definition.type, data: definition.data, options });
        },

        chartDefinition(key, rows) {
            if (!rows.length) return null;
            if (key === 'stock') {
                const low = rows.filter((row) => /bas/i.test(row.data.Statut || '')).length;
                return { type: 'doughnut', ariaLabel: 'Répartition des produits en stock normal et stock bas', data: { labels: ['Stock normal', 'Stock bas'], datasets: [{ data: [rows.length - low, low] }] } };
            }
            if (key === 'value') {
                const top = rows.slice().sort((a, b) => parseNumeric(b.data['Valeur totale']) - parseNumeric(a.data['Valeur totale'])).slice(0, 8);
                return { type: 'bar', ariaLabel: 'Valeur du stock par magasin', data: { labels: top.map((row) => row.data.Magasin), datasets: [{ label: 'Valeur du stock', data: top.map((row) => parseNumeric(row.data['Valeur totale'])) }] }, options: { indexAxis: top.length > 5 ? 'y' : 'x' } };
            }
            if (key === 'movements') {
                const groups = {};
                rows.forEach((row) => { const label = row.data.Type || 'Autre'; groups[label] = (groups[label] || 0) + 1; });
                return { type: 'doughnut', ariaLabel: 'Répartition des mouvements par type', data: { labels: Object.keys(groups), datasets: [{ data: Object.values(groups) }] } };
            }
            if (key === 'alerts') {
                const groups = {};
                rows.forEach((row) => { const label = (row.data.Priorité || 'Alerte').replace(/[🔴🟠🟡]/g, '').trim(); groups[label] = (groups[label] || 0) + 1; });
                return { type: 'doughnut', ariaLabel: 'Répartition des alertes par priorité', data: { labels: Object.keys(groups), datasets: [{ data: Object.values(groups) }] } };
            }
            if (key === 'brands') {
                const top = rows.slice().sort((a, b) => parseNumeric(b.data['Valeur totale']) - parseNumeric(a.data['Valeur totale'])).slice(0, 8);
                return { type: 'bar', ariaLabel: 'Valeur du stock des principales marques', data: { labels: top.map((row) => row.data.Marque), datasets: [{ label: 'Valeur du stock', data: top.map((row) => parseNumeric(row.data['Valeur totale'])) }] }, options: { indexAxis: 'y' } };
            }
            if (key === 'gap') {
                const gapValue = (row) => parseNumeric(row.data['Écart valeur'] || row.data['Valeur écart'] || '0');
                const top = rows.slice().sort((a, b) => Math.abs(gapValue(b)) - Math.abs(gapValue(a))).slice(0, 8);
                return { type: 'bar', ariaLabel: 'Principaux écarts d’inventaire en valeur', data: { labels: top.map((row) => row.data.Produit), datasets: [{ label: 'Valeur absolue de l’écart', data: top.map((row) => Math.abs(gapValue(row)))}] }, options: { indexAxis: 'y' } };
            }
            if (key === 'prices') {
                const selected = document.getElementById('pricesProductFilter')?.value || '';
                if (!selected || rows.length < 2) return null;
                const ordered = rows.slice().reverse().slice(-24);
                return { type: 'line', ariaLabel: 'Évolution du prix de base du produit sélectionné', data: { labels: ordered.map((row) => row.data.Date), datasets: [{ label: 'Prix de base', data: ordered.map((row) => parseNumeric(row.data['Prix base'])), fill: false, tension: 0.25 }] } };
            }
            return null;
        },

        destroyChart(key) {
            if (this.charts[key]) {
                try { this.charts[key].destroy(); } catch (_) {}
                delete this.charts[key];
            }
        },

        destroyCharts() {
            Object.keys(this.charts).forEach((key) => this.destroyChart(key));
        }
    };

    function wrapReportPage() {
        const page = getReportPage();
        if (!page || page.__reportsMobileWrapped) return;
        page.__reportsMobileWrapped = true;

        const originalInit = page.init;
        page.init = async function wrappedReportsInit(...args) {
            const result = await originalInit.apply(this, args);
            ReportsMobile.init();
            return result;
        };

        const originalSwitch = page.switchTab;
        page.switchTab = async function wrappedReportSwitch(tabName, ...args) {
            const result = await originalSwitch.call(this, tabName, ...args);
            ReportsMobile.onTabChanged(tabName);
            return result;
        };

        const renderMap = {
            renderStockTable: 'stock',
            renderValueTable: 'value',
            renderMovementsTable: 'movements',
            renderAlertsTable: 'alerts',
            renderBrandsTable: 'brands',
            renderGapTable: 'gap',
            renderPricesTable: 'prices'
        };
        Object.entries(renderMap).forEach(([method, key]) => {
            const original = page[method];
            if (typeof original !== 'function') return;
            page[method] = function wrappedReportRender(...args) {
                const result = original.apply(this, args);
                const after = () => {
                    if (!ReportsMobile.initialized) return;
                    ReportsMobile.limits[key] = PAGE_SIZE;
                    global.requestAnimationFrame(() => ReportsMobile.refresh(key, { resetLimit: true }));
                };
                if (result && typeof result.then === 'function') return result.then((value) => { after(); return value; });
                after();
                return result;
            };
        });
    }

    global.MageStockReportsMobile = ReportsMobile;
    wrapReportPage();
})(window, document);
