/**
 * Fichier  : assets/js/api.js
 * Auteur   : KREMER Régis
 * Version  : 2.6.119
 * Date     : 2026-07-25
 * Description : JS — Client HTTP centralisé, CSRF automatique, endpoints API
 *
 * Changelog :
 *   v2.1.0 (2026-02-21) — Version initiale auditée et sécurisée
 *   v2.1.1 (2026-02-21) — Corrections sécurité, utils.js centralisé
 *   v2.2.0 (2026-02-21) — Permissions magasins, bons de commande, rapport d'écart,
 *   v2.3.0 (2026-02-26) — Lot 1: endpoints Emplacements (locations).
 *                      import CSV, historique prix, bon de livraison multi-lignes
 *   v2.6.10 (2026-07-08) — Résolution runtime des URLs API/login via APP_BASE_PATH.
 *   v2.6.14 (2026-07-08) — Endpoints logo entreprise : upload et suppression via FormData.
 *   v2.6.17 (2026-07-08) — Helper publicUrl pour images/logo compatible filtres et APP_BASE_PATH.
 *   v2.6.18 (2026-07-08) — Helper imageUrl : proxy sécurisé pour uploads/assets locaux après filtres et changements de magasin.
 *   v2.6.25 (2026-07-09) — PLAN 1 : helpers API plans d’emplacements et objets de plan.
 *   v2.6.26 (2026-07-09) — PLAN 2 : helper de sauvegarde groupée des objets de plan.
 *   v2.6.28 (2026-07-09) — PLAN 4 : helpers de lecture emplacements et informations métier via API plans.
 *   v2.6.29 (2026-07-09) — PLAN 5 : helper de localisation produit vers le bon plan.
 *   v2.6.75 (2026-07-17) — INVENTORY RESUME REVIEW2 : brouillons, reprise, revue et clôture explicite.
 *   v2.6.76 (2026-07-17) — INVENTORY FIELD RECETTE3 : sauvegarde keepalive compacte pour mise en arrière-plan PWA.
 *   v2.6.80 (2026-07-17) — ORDERS MOBILE FLOW4 : helper de revue serveur avant réception définitive.
 *   v2.6.84 (2026-07-20) — ADMIN COMPLEX FORMS8 : confirmations dédiées pour restauration et archives.
 *   v2.6.119 (2026-07-25) — Endpoint de création des jetons de récupération administrateur.
 */
// ================================================================
// api.js — Client HTTP centralisé MageStock, portable APP_BASE_PATH
// ================================================================
const Api = {

    url: function(url){
        const raw = String(url || '');
        if(!raw) return raw;
        if(/^(?:https?:)?\/\//i.test(raw) || /^(?:data|blob|mailto|tel):/i.test(raw)) return raw;
        if(raw.charAt(0) === '/') return raw;
        const runtime = window.MageStockRuntime || {};
        const base = String(runtime.basePath || '').replace(/\/+$/, '');
        const path = raw.replace(/^\.\//, '').replace(/^\/+/, '');
        return (base ? base : '') + '/' + path;
    },

    publicUrl: function(url){
        const raw = String(url || '').trim();
        if(!raw) return '';
        if(/^(?:https?:)?\/\//i.test(raw) || /^(?:data|blob):/i.test(raw)) return raw;
        const runtime = window.MageStockRuntime || {};
        const base = String(runtime.basePath || '').replace(/\/+$/, '');
        let path = raw.replace(/^\.\//, '').replace(/^\/+/, '');
        const cleanBase = base.replace(/^\/+/, '');
        if(cleanBase && path.startsWith(cleanBase + '/')){
            path = path.slice(cleanBase.length + 1);
        }
        return (base ? base : '') + '/' + path;
    },

    imageUrl: function(url){
        const raw = String(url || '').trim();
        if(!raw) return '';
        if(/^(?:https?:)?\/\//i.test(raw) || /^(?:data|blob):/i.test(raw)) return raw;
        const runtime = window.MageStockRuntime || {};
        const base = String(runtime.basePath || '').replace(/\/+$/, '');
        let path = raw.replace(/^\.\//, '').replace(/^\/+/, '');
        const cleanBase = base.replace(/^\/+/, '');
        if(cleanBase && path.startsWith(cleanBase + '/')) path = path.slice(cleanBase.length + 1);
        if(path.startsWith('uploads/') || path.startsWith('assets/img/')){
            return Api.url('api/image.php?src=' + encodeURIComponent(path));
        }
        return Api.publicUrl(path);
    },

    _req: async function(method, url, body, isFormData, requestOptions){
        const hdrs = { 
            'X-Requested-With': 'XMLHttpRequest'
        };
        
        // Add CSRF token for non-GET requests
        if(method !== 'GET') {
            const csrfToken = document.querySelector('meta[name="csrf-token"]')?.content;
            if(csrfToken) {
                hdrs['X-CSRF-TOKEN'] = csrfToken;
            }
        }
        
        if(!isFormData && body) hdrs['Content-Type'] = 'application/json';
        let opts = { method, headers: hdrs, credentials: 'same-origin' };
        if(requestOptions && requestOptions.keepalive) opts.keepalive = true;
        if(body) opts.body = isFormData ? body : JSON.stringify(body);
        const requestUrl = Api.url(url);
        const res = await fetch(requestUrl, opts);
        if(res.status === 401){ window.location.href=Api.url('login'); throw new Error('Non authentifié'); }
        
        // Lire le body UNE SEULE FOIS (text), puis parser en JSON
        // Évite le "Body already consumed" si res.json() échoue
        const rawText = await res.text();
        let json;
        try {
            json = JSON.parse(rawText);
        } catch(e) {
            console.error('[API] Invalid JSON from:', requestUrl, '| Status:', res.status);
            console.error('[API] Raw response:', rawText.substring(0, 500));
            throw new Error('Réponse invalide du serveur — voir console (F12)');
        }

        if(!res.ok) {
            const error = new Error(json?.error || 'Erreur serveur ' + res.status);
            error.status = res.status;
            error.payload = json;
            throw error;
        }
        // Unwrap {data: ...} responses to keep front code simple
        if(json && typeof json === 'object' && ('data' in json)) return json.data;
        return json;
    },

    // ── Generic ──
    get:    (url)         => Api._req('GET',    url),
    post:   (url, data)   => Api._req('POST',   url, data),
    put:    (url, data)   => Api._req('PUT',    url, data),
    delete: (url, data)   => Api._req('DELETE', url, data),
    postForm:(url, fd)    => Api._req('POST',   url, fd, true),
    postKeepalive:(url,data)=> Api._req('POST',   url, data, false, {keepalive:true}),

    // ── AUTH ──
    me:            ()  => Api.get('api/auth.php?action=me'),

    // ── WAREHOUSES ──
    warehouses:    ()  => Api.get('api/warehouses.php'),
    // Orders (bons de commande fournisseurs)
    orders:          (params) => Api.get('api/orders.php' + (params ? '?' + new URLSearchParams(params) : '')),
    orderGet:        (id) => Api.get('api/orders.php?id=' + id),
    orderCreate:     (d) => Api.post('api/orders.php', d),
    orderUpdate:     (d) => Api.put('api/orders.php', d),
    orderStatus:     (id, status) => Api.put('api/orders.php', {id, action:'status', status}),
    orderReceiveReview:(d) => Api.put('api/orders.php', {...d, action:'receive_review'}),
    orderReceive:    (d) => Api.put('api/orders.php', {...d, action:'receive'}),
    orderApproval:   (id, validationStatus, note='') => Api.put('api/orders.php', {id, action:'approval', validationStatus, note}),
    orderStore:      (id) => Api.put('api/orders.php', {id, action:'store'}),
    orderDelete:     (id) => Api.delete('api/orders.php?id=' + id, {id}),

    // Warehouse CRUD
    warehouseAdd:  (d) => Api.post('api/warehouses.php', d),
    warehouseEdit: (d) => Api.put('api/warehouses.php', d),
    warehouseDel:  (id)=> Api.delete('api/warehouses.php?id='+id, {id}),
    // Legacy aliases (kept for backward compatibility)
    whAdd:         (d) => Api.post('api/warehouses.php', d),
    whEdit:        (d) => Api.put('api/warehouses.php', d),
    whDel:         (id)=> Api.delete('api/warehouses.php?id='+id, {id}),

    // ── WAREHOUSE CATEGORIES ──
    warehouseCategories: (wid) => Api.get('api/warehouse-categories.php?warehouseId='+wid).then(r=> (r && r.categories) ? r.categories : (Array.isArray(r)?r:[])),
    warehouseCategoryAdd:   (d)   => Api.post('api/warehouse-categories.php', d),
    warehouseCategoryDel:   (d)   => Api.delete('api/warehouse-categories.php', d),
    warehouseCategoryRename:(d)   => Api.put('api/warehouse-categories.php', d),

    // ── PRODUCTS ──
    products:      (q,cat) => Api.get('api/products.php'+(q||cat?'?':'')+(q?'q='+encodeURIComponent(q):'')+((q&&cat)?'&':'')+(cat?'category='+encodeURIComponent(cat):'')),
    prodAdd:       (d) => Api.post('api/products.php', d),
    prodEdit:      (d) => Api.put('api/products.php', d),
    prodDel:       (id)=> Api.delete('api/products.php?id='+id, {id}),
    prodAddImg:    (fd)=> Api.postForm('api/products.php', fd),
    prodEditImg:   (fd)=> Api.postForm('api/products.php?mode=editImage', fd),
    variantFamilies: () => Api.get('api/variant_families.php'),
    variantFamilyAdd:(d) => Api.post('api/variant_families.php', d),
    variantFamilyEdit:(d) => Api.put('api/variant_families.php', d),
    variantFamilyDel:(id) => Api.delete('api/variant_families.php?id='+encodeURIComponent(id), {id}),

    // ── SUPPLIERS ──
    suppliers:     (q) => Api.get('api/suppliers.php'+(q?'?q='+encodeURIComponent(q):'')),
    supplierAdd:   (d) => Api.post('api/suppliers.php', d),
    supplierEdit:  (d) => Api.put('api/suppliers.php', d),
    supplierDel:   (id)=> Api.delete('api/suppliers.php?id='+id, {id}),

        // ── SUPPLIER BRANDS ──
    supplierBrands: (sid) => Api.get('api/supplier-brands.php?supplierId='+sid).then(r=> (r && r.brands) ? r.brands : (Array.isArray(r)?r:[])),

// ── BRANDS (GLOBAL) ──
    brands:        (q) => Api.get('api/brands.php'+(q?'?q='+encodeURIComponent(q):'')),
    brandAdd:      (d) => Api.post('api/brands.php', d),
    brandEdit:     (d) => Api.put('api/brands.php', d),
    brandDel:      (id)=> Api.delete('api/brands.php?id='+id, {id}),

    // ── STOCK ──
    stock:         (pid,wid) => Api.get('api/stock.php'+(pid?'?productId='+pid:'')+(wid?((pid?'&':'?')+'warehouseId='+wid):'')),
    stockSet:      (d) => Api.post('api/stock.php', d),

    // ── MOVEMENTS ──
    movements:     (params={}) => {
        let q = Object.entries(params).filter(([,v])=>v!==''&&v!=null).map(([k,v])=>k+'='+encodeURIComponent(v)).join('&');
        return Api.get('api/movements.php'+(q?'?'+q:''));
    },
    mvAdd:         (d) => Api.post('api/movements.php', d),
    mvClear:       ()  => Api.delete('api/movements.php'),

    // ── ALERTS ──
    alerts:        (wid) => Api.get('api/alerts.php'+(wid?'?warehouseId='+wid:'')),
    alertAdd:      (d) => Api.post('api/alerts.php', d),
    alertEdit:     (d) => Api.put('api/alerts.php', d),
    alertDel:      (id)=> Api.delete('api/alerts.php?id='+id, {id}),

    // ── INVENTORIES ──
    invHistory:      ()   => Api.get('api/inventories.php'),
    invDrafts:       ()   => Api.get('api/inventories.php?scope=drafts'),
    invDraft:        (id) => Api.get('api/inventories.php?scope=draft&id=' + encodeURIComponent(id)),
    invStart:        (d)  => Api.post('api/inventories.php', Object.assign({action:'start'}, d)),
    invSaveDraft:    (d)  => Api.post('api/inventories.php', Object.assign({action:'save'}, d)),
    invSaveDraftKeepalive:(d)=> Api.postKeepalive('api/inventories.php', Object.assign({action:'save'}, d)),
    invReviewDraft:  (d)  => Api.post('api/inventories.php', Object.assign({action:'review'}, d)),
    invDiscardDraft: (d)  => Api.post('api/inventories.php', Object.assign({action:'discard'}, d)),
    invValidate:     (d)  => Api.post('api/inventories.php', Object.assign({action:'validate'}, d)),

    // ── CATEGORIES ──
    categories:    ()  => Api.get('api/categories.php'),
    catAdd:        (d) => Api.post('api/categories.php', d),
    catDel:        (name)=> Api.delete('api/categories.php?name='+encodeURIComponent(name), {name}),

    // ── USERS ──
    users:         ()  => Api.get('api/users.php'),
    userAdd:       (d) => Api.post('api/users.php', d),
    userEdit:      (d) => Api.put('api/users.php', d),
    userDel:       (id)=> Api.delete('api/users.php?id='+id, {id}),
    userPerms:     (userId) => Api.get('api/permissions.php?userId='+encodeURIComponent(userId)),
    userPermsEdit: (d)      => Api.put('api/permissions.php', d),
    userRecoveryToken: (userId, durationMinutes=60) => Api.post('api/password-recovery.php', {action:'create', userId, durationMinutes}),

    // ── SETTINGS ──
    settings:      ()  => Api.get('api/settings.php'),
    settingsSave:  (d) => Api.post('api/settings.php', d),
    settingsLogoUpload: (fd) => Api.postForm('api/settings.php?action=uploadLogo', fd),
    settingsLogoDelete: () => Api.post('api/settings.php?action=deleteLogo', {}),

    // ── LDAP / ACTIVE DIRECTORY ──
    ldapConfig:      () => Api.get('api/ldap.php?action=config'),
    ldapDiagnostics: () => Api.get('api/ldap.php?action=diagnostics'),
    ldapAudit:       () => Api.get('api/ldap.php?action=audit'),
    ldapSaveConfig:  (config, confirmation='') => Api.post('api/ldap.php?action=saveConfig', {config, confirmation}),
    ldapSetSecret:   (secret) => Api.post('api/ldap.php?action=setSecret', {secret}),
    ldapDeleteSecret:() => Api.post('api/ldap.php?action=deleteSecret', {}),
    ldapSetCertificate:(pem) => Api.post('api/ldap.php?action=setCertificate', {pem}),
    ldapDeleteCertificate:() => Api.post('api/ldap.php?action=deleteCertificate', {}),
    ldapTestConnection:(config) => Api.post('api/ldap.php?action=testConnection', {config}),
    ldapTestBind:    (config) => Api.post('api/ldap.php?action=testBind', {config}),
    ldapTestSearch:  (username,config) => Api.post('api/ldap.php?action=testSearch', {username,config}),
    ldapSimulate:    (username,config) => Api.post('api/ldap.php?action=simulate', {username,config}),
    ldapTestAuthentication:(username,password,config) => Api.post('api/ldap.php?action=testAuthentication', {username,password,config}),
    ldapSyncKnownUsers:() => Api.post('api/ldap.php?action=syncKnownUsers', {}),

    // ── BACKUP ──
    backupExport:  ()  => { window.location.href=Api.url('api/backup.php'); },
    backupImport:  (d, confirmation='') => Api.post('api/backup.php', {backup:d, confirmation}),
    archiveStatus: ()  => Api.get('api/archive.php?action=status'),
    archiveList:   ()  => Api.get('api/archive.php?action=list'),
    archiveRead:   (f) => Api.get('api/archive.php?action=read&file='+encodeURIComponent(f)),
    archiveCreate: (d) => Api.post('api/archive.php?action=create', d),
    archiveDelete: (f, confirmation='') => Api.post('api/archive.php?action=delete', {file:f, confirmation}),
    archiveRestore:(f, confirmation='') => Api.post('api/archive.php?action=restore', {file:f, confirmation}),
    // ── LOT 4 (Kits, Reservations, EAN, Product versioning) ──
    kits:            (q) => Api.get('api/kits.php'+(q?'?q='+encodeURIComponent(q):'')),
    kitCreate:       (d) => Api.post('api/kits.php', d),
    kitUpdate:       (d) => Api.put('api/kits.php', d),
    kitDelete:       (id)=> Api.delete('api/kits.php?id='+encodeURIComponent(id), {id}),

    reservations:     (params={}) => {
        let q = Object.entries(params).filter(([,v])=>v!==''&&v!=null).map(([k,v])=>k+'='+encodeURIComponent(v)).join('&');
        return Api.get('api/reservations.php'+(q?'?'+q:''));
    },
    reservationCreate:(d) => Api.post('api/reservations.php', d),
    reservationUpdate:(d) => Api.put('api/reservations.php', d),
    reservationDelete:(id)=> Api.delete('api/reservations.php?id='+encodeURIComponent(id), {id}),

    eanLookup:        (code)=> Api.get('api/ean.php?code='+encodeURIComponent(code)),
    productVersions:  (pid)=> Api.get('api/product_versions.php'+(pid?'?productId='+encodeURIComponent(pid):'')),

    // ── LOT 1 (Emplacements internes) ──
    locations:        (warehouseId) => Api.get('api/locations.php' + (warehouseId ? ('?warehouseId=' + encodeURIComponent(warehouseId)) : '')),
    locationSave:     (d) => Api.post('api/locations.php', d),
    locationUpdate:   (d) => Api.put('api/locations.php', d),
    locationDelete:   (id)=> Api.delete('api/locations.php', {id: String(id)}),

    // ── PLANS D’EMPLACEMENTS (PLAN 1) ──
    plans:           (params={}) => Api.get('api/plans.php' + (params ? ('?' + new URLSearchParams(Object.entries(params).filter(([,v])=>v!==undefined&&v!==null&&v!==''))) : '')),
    planGet:         (id, includeObjects=false) => Api.get('api/plans.php?id=' + encodeURIComponent(id) + (includeObjects ? '&includeObjects=1' : '')),
    planSchema:      () => Api.get('api/plans.php?action=schema'),
    planObjects:     (planId) => Api.get('api/plans.php?action=objects&planId=' + encodeURIComponent(planId)),
    planLocations:   (params={}) => Api.get('api/plans.php?action=locations' + (params ? ('&' + new URLSearchParams(Object.entries(params).filter(([,v])=>v!==undefined&&v!==null&&v!==''))) : '')),
    planLocationInfo:(id) => Api.get('api/plans.php?action=locationInfo&id=' + encodeURIComponent(id)),
    planProductLocation:(productId, params={}) => Api.get('api/plans.php?action=productLocation&productId=' + encodeURIComponent(productId) + (params ? ('&' + new URLSearchParams(Object.entries(params).filter(([,v])=>v!==undefined&&v!==null&&v!==''))) : '')),
    planCreate:      (d) => Api.post('api/plans.php', d),
    planUpdate:      (d) => Api.put('api/plans.php', d),
    planDelete:      (id) => Api.delete('api/plans.php?id=' + encodeURIComponent(id), {id: String(id)}),
    planObjectCreate:(d) => Api.post('api/plans.php?action=object', d),
    planObjectUpdate:(d) => Api.put('api/plans.php?action=object', d),
    planObjectDelete:(id) => Api.delete('api/plans.php?action=object&id=' + encodeURIComponent(id), {id: String(id)}),
    planObjectsSave:(planId, objects) => Api.put('api/plans.php?action=saveObjects', {planId: String(planId), objects}),

    // ── LOT 3 (Pilotage / Analytics) ──
    analytics:        (params={}) => Api.get('api/analytics.php' + (params ? ('?' + new URLSearchParams(Object.entries(params).filter(([,v])=>v!==undefined&&v!==null&&v!==''))) : '')),

    // ── SEARCH ──
    search:        (q) => Api.get('api/search.php?q='+encodeURIComponent(q)),
};


window.Api = Api;

const StockCascade = {
    parseIdList(raw){
        if(Array.isArray(raw)) return raw.map(v=>String(v)).filter(Boolean);
        if(raw == null || raw === '') return [];
        try{
            const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
            if(Array.isArray(parsed)) return parsed.map(v=>String(v)).filter(Boolean);
        }catch(e){}
        return [];
    },
    parseNameList(raw){
        if(Array.isArray(raw)) return raw.map(v=>String(v).trim()).filter(Boolean);
        if(raw == null || raw === '') return [];
        try{
            const parsed = typeof raw === 'string' ? JSON.parse(raw) : raw;
            if(Array.isArray(parsed)) return parsed.map(v=>String(v).trim()).filter(Boolean);
        }catch(e){}
        return [];
    },
    uniqueSorted(values){
        return [...new Set((values||[]).map(v=>String(v).trim()).filter(Boolean))].sort((a,b)=>a.localeCompare(b,'fr'));
    },
    getWarehouse(warehouses, warehouseId){
        return (warehouses||[]).find(w => String(w?.id) === String(warehouseId)) || null;
    },
    getWarehouseCategories(warehouses, warehouseId, products=[], fallbackCategories=[], stock=[]){
        if(!warehouseId){
            return this.uniqueSorted([
                ...(fallbackCategories||[]),
                ...((warehouses||[]).flatMap(w => this.parseNameList(w?.categories))),
                ...((products||[]).map(p => p?.category).filter(Boolean))
            ]);
        }
        const warehouse = this.getWarehouse(warehouses, warehouseId);
        const declared = warehouse ? this.parseNameList(warehouse.categories) : [];
        const scopedProducts = Array.isArray(stock) && stock.length
            ? this.getProductsForWarehouseStock(products, stock, warehouseId, { includeZeroRows: true, checkAllowed: true })
            : (products||[]).filter(p => this.productInWarehouse(p, warehouseId, {includeUnrestricted:true}));
        const fromProducts = scopedProducts
            .map(p => p?.category)
            .filter(Boolean);
        return this.uniqueSorted([ ...declared, ...fromProducts ]);
    },
    productInWarehouse(product, warehouseId, options={}){
        if(!warehouseId) return true;
        if(!product) return false;
        const includeUnrestricted = options.includeUnrestricted !== false;
        const allowed = this.parseIdList(product.allowedWarehouses);
        if(!allowed.length) return includeUnrestricted;
        return allowed.includes(String(warehouseId));
    },
    getStockRowsForProduct(stock=[], productId='', warehouseId=''){
        return (Array.isArray(stock) ? stock : []).filter(row => {
            if(String(row?.productId ?? '') !== String(productId)) return false;
            if(warehouseId && String(row?.warehouseId ?? '') !== String(warehouseId)) return false;
            return true;
        });
    },
    productHasStockRow(product, warehouseId, stock=[], options={}){
        if(!warehouseId) return true;
        if(!product) return false;
        const includeZeroRows = options.includeZeroRows !== false;
        const rows = this.getStockRowsForProduct(stock, product.id, warehouseId);
        if(!rows.length) return false;
        if(includeZeroRows) return true;
        return rows.some(row => (Number(row?.quantity) || 0) > 0);
    },
    getProductsForWarehouseStock(products, stock=[], warehouseId='', options={}){
        if(!warehouseId) return Array.isArray(products) ? products.slice() : [];
        const includeZeroRows = options.includeZeroRows !== false;
        const checkAllowed = options.checkAllowed !== false;
        return (Array.isArray(products) ? products : []).filter(product => {
            if(checkAllowed && !this.productInWarehouse(product, warehouseId, { includeUnrestricted: true })){
                return false;
            }
            return this.productHasStockRow(product, warehouseId, stock, { includeZeroRows });
        });
    },
    filterProducts(products, scope={}){
        const { warehouseId='', category='', supplierId='', brand='', includeUnrestricted=true } = scope || {};
        let out = Array.isArray(products) ? products.slice() : [];
        if(warehouseId) out = out.filter(p => this.productInWarehouse(p, warehouseId, {includeUnrestricted}));
        if(category) out = out.filter(p => String(p?.category||'') === String(category));
        if(supplierId) out = out.filter(p => String(p?.supplier||'') === String(supplierId));
        if(brand) out = out.filter(p => String(p?.brand||'') === String(brand));
        return out;
    },
    getSupplierLinkedBrandNames(suppliers, brands, supplierId){
        const supplier = (suppliers||[]).find(s => String(s?.id) === String(supplierId));
        if(!supplier) return [];
        const brandNames = this.parseNameList(supplier?.brands);
        const brandIds = this.parseIdList(supplier?.linkedBrands);
        const linkedNames = (brands||[])
            .filter(b => brandIds.includes(String(b?.id)))
            .map(b => b?.name)
            .filter(Boolean);
        return this.uniqueSorted([ ...brandNames, ...linkedNames ]);
    },
    getBrandNamesForScope(products, suppliers=[], brands=[], scope={}){
        const { supplierId='' } = scope || {};
        const fromProducts = this.filterProducts(products, scope).map(p => p?.brand).filter(Boolean);
        if(!supplierId) return this.uniqueSorted(fromProducts);
        const linked = this.getSupplierLinkedBrandNames(suppliers, brands, supplierId);
        return this.uniqueSorted([ ...linked, ...fromProducts ]);
    },
    getSupplierIdsForScope(products, scope={}){
        return [...new Set(this.filterProducts(products, scope).map(p => String(p?.supplier||'')).filter(Boolean))];
    },
    getProductsForWarehouse(products, warehouseId, includeUnrestricted=false){
        return this.filterProducts(products, {warehouseId, includeUnrestricted});
    },
    getLocationsForWarehouse(locations, warehouseId){
        if(!warehouseId) return Array.isArray(locations) ? locations.slice() : [];
        return (locations||[]).filter(l => String(l?.warehouseId) === String(warehouseId));
    }
};
window.StockCascade = StockCascade;
