/**
 * Fichier  : assets/js/api.js
 * Auteur   : KREMER Régis
 * Version  : 2.3.0
 * Date     : 2026-02-26
 * 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
 */
// ================================================================
// api.js — Centralized HTTP client for all StockMaster API calls
// ================================================================
const Api = {

    _req: async function(method, url, body, isFormData){
        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(body) opts.body = isFormData ? body : JSON.stringify(body);
        const res = await fetch(url, opts);
        if(res.status === 401){ window.location.href='login.php'; 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:', url, '| 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) throw new Error(json?.error || 'Erreur serveur '+res.status);
        // 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),

    // ── 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}),
    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),

    // ── 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'),
    invStart:      (d) => Api.post('api/inventories.php', Object.assign({action:'start'},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),

    // ── SETTINGS ──
    settings:      ()  => Api.get('api/settings.php'),
    settingsSave:  (d) => Api.post('api/settings.php', d),

    // ── BACKUP ──
    backupExport:  ()  => { window.location.href='api/backup.php'; },
    backupImport:  (d) => Api.post('api/backup.php', d),
    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) => Api.post('api/archive.php?action=delete', {file:f}),
    archiveRestore:(f) => Api.post('api/archive.php?action=restore', {file:f}),
    // ── 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)}),

    // ── 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)),
};


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=[]){
        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 fromProducts = (products||[])
            .filter(p => this.productInWarehouse(p, warehouseId, {includeUnrestricted:false}))
            .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));
    },
    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;
