/**
 * =========================================================================
 * PLATEFORME ECO-PANNEAU.FR - VERSION 1.0.0
 * Socle UI (1/4) - Utilitaires, API et Logique Métier
 * =========================================================================
 */

// =========================================================================
// 1. SYSTÈME DE NOTIFICATIONS GLOBAL (TOASTS) - DROP IN
// =========================================================================
window.showToast = (message, type = 'info', duration = 4000) => {
    let container = document.getElementById('toast-container');
    if (!container) {
        container = document.createElement('div');
        container.id = 'toast-container';
        container.className = 'fixed bottom-4 right-4 sm:bottom-6 sm:right-6 z-[99999] flex flex-col items-end gap-3 pointer-events-none';
        document.body.appendChild(container);
    }

    const toast = document.createElement('div');
    const bgColor = type === 'success' ? 'bg-emerald-600/95 border-emerald-500 text-white' :
                    (type === 'error' ? 'bg-red-500/95 border-red-400 text-white' : 
                    (type === 'warning' ? 'bg-amber-500/95 border-amber-400 text-white' : 'bg-blue-600/95 border-blue-500 text-white'));
    
    const svgWrap = (path) => `<svg width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" viewBox="0 0 24 24">${path}</svg>`;

    const iconSvg = type === 'success' ? svgWrap(window.svgPaths?.CheckCircle || '') :
                    (type === 'error' || type === 'warning' ? svgWrap(window.svgPaths?.AlertTriangle || '') :
                    svgWrap(window.svgPaths?.Info || ''));

    toast.className = `flex items-center gap-3 px-5 py-4 rounded-2xl border shadow-2xl ${bgColor} pointer-events-auto backdrop-blur-md max-w-xs transition-all duration-500 ease-out`;
    toast.innerHTML = `
        <div class="shrink-0">${iconSvg}</div>
        <div class="font-bold text-sm leading-tight break-words">${message}</div>
    `;
    
    toast.style.transform = 'translateY(-100vh)';
    toast.style.opacity = '0';
    
    container.appendChild(toast);
    void toast.offsetWidth;
    
    toast.style.transform = 'translateY(0)';
    toast.style.opacity = '1';
    
    setTimeout(() => {
        toast.style.transform = 'translateX(120%)';
        setTimeout(() => {
            toast.remove();
            if (container && container.childNodes.length === 0) container.remove();
        }, 500); 
    }, duration);
};

// =========================================================================
// 2. UTILITAIRES ET LOGIQUE MÉTIER (BUSINESS LOGIC)
// =========================================================================

window.apiFetch = async (endpoint, options = {}) => {
    const { method = 'POST', body, setLoading, successMessage, errorMessage = "Erreur réseau ou serveur" } = options;
    if (setLoading) setLoading(true);
    try {
        const fetchOptions = { method };
        if (body) {
            const cleanBody = JSON.parse(JSON.stringify(body), (key, value) => {
                if (key === 'imageUrl' || key === 'maitreOuvrageLogoUrl' || key === 'logoUrl') {
                    return undefined; 
                }
                if (typeof value === 'string' && value.includes('data:image/')) {
                    return value.replace(/(src|href)=["']data:image\/[^;]+;base64,[^"']+["']/gi, '$1="#"');
                }
                return value;
            });
            fetchOptions.body = JSON.stringify(cleanBody);
        }
        
        const res = await fetch(window.ECO_CONFIG.apiBaseUrl + endpoint, fetchOptions);
        const d = await res.json();
        
        if (d.status === 'success') {
            if (successMessage) window.showToast(successMessage, "success");
            return d;
        } else {
            window.showToast(d.message || errorMessage, "error");
            return null;
        }
    } catch (e) {
        window.showToast(errorMessage, "error");
        return null;
    } finally {
        if (setLoading) setLoading(false);
    }
};

window.formatDate = (dateStr, fallback = '-') => {
    if (!dateStr) return fallback;
    const d = new Date(String(dateStr).replace(' ', 'T'));
    return isNaN(d.getTime()) ? fallback : d.toLocaleDateString('fr-FR');
};

window.getFileUrl = (type, id, isPrivate = false, isPreview = false) => {
    if (!id) return '';
    let url = `?api=file/download&type=${type}&id=${id}`;
    if (isPrivate) url += '&private=1';
    if (isPreview) url += '&preview=1';
    return url;
};

window.getPanelAccessRights = (allPanels = [], clientUid) => {
    const pendingInvites = allPanels.filter(p => p.client_uid !== clientUid && p.collaborators?.some(c => c.uid === clientUid && c.status === 'pending'));
    const ownedPanels = allPanels.filter(p => p.client_uid === clientUid);
    const acceptedCollabPanels = allPanels.filter(p => p.client_uid !== clientUid && p.collaborators?.some(c => c.uid === clientUid && c.status === 'accepted'));
    const visiblePanels = [...ownedPanels, ...acceptedCollabPanels];
    return { pendingInvites, ownedPanels, acceptedCollabPanels, visiblePanels };
};

window.computeClientUnread = (interactions = [], myClientData = {}, visiblePanels = [], pendingInvites = []) => {
    let unread = 0;
    
    const expectedFullName = myClientData.full_name || 'Utilisateur';
    const expectedCompany = myClientData.name || 'Société';
    const expectedAuthorName = `${expectedFullName} (${expectedCompany})`;
    
    interactions.forEach(m => {
        if (m.resolved) return;

        const isMyOwnMessage = (m.authorType === 'Client' && (
            m.author === 'Client' || 
            m.author === myClientData.email || 
            m.author === myClientData.full_name || 
            m.author === myClientData.name ||
            m.author === expectedAuthorName
        ));

        if (m.panneauId.startsWith('SUPPORT_')) {
            if (m.authorType !== 'Client') unread++;
        } 
        else if (m.panneauId.startsWith('GROUP_')) {
            const pId = m.panneauId.replace('GROUP_', '');
            const hasAccess = visiblePanels.some(p => p.id === pId) || pendingInvites.some(p => p.id === pId);
            if (hasAccess && !isMyOwnMessage && m.authorType !== 'Systeme') {
                unread++;
            }
        } 
        else {
            if (m.authorType !== 'Client' && m.authorType !== 'Admin' && m.authorType !== 'Systeme') {
                unread++;
            }
        }
    });
    
    return unread;
};

window.uploadFile = async (file, type, onProgress, isPublic = false, isPrivate = false, clientUid = null, panneauId = null) => {
    let fileToSend = file;
    let previewB64s = {};
    let totalPages = 1;
    let hasLocalProcessing = false;
    
    try {
        if (type === 'image' && file.type.startsWith('image/')) {
            if (onProgress) onProgress("optimisation", 10);
            
            const img = new Image();
            const imgUrl = URL.createObjectURL(file);
            
            await new Promise((resolve, reject) => {
                img.onload = resolve;
                img.onerror = () => reject(new Error("Le navigateur n'a pas pu lire l'image."));
                img.src = imgUrl;
            });
            
            const canvas = document.createElement('canvas');
            const ctx = canvas.getContext('2d');
            let width = img.width;
            let height = img.height;
            const MAX_SIZE = 1920; 
            
            if (width > MAX_SIZE || height > MAX_SIZE) {
                if (width > height) {
                    height = Math.round((height * MAX_SIZE) / width);
                    width = MAX_SIZE;
                } else {
                    width = Math.round((width * MAX_SIZE) / height);
                    height = MAX_SIZE;
                }
            }
            
            canvas.width = width;
            canvas.height = height;
            ctx.drawImage(img, 0, 0, width, height);
            URL.revokeObjectURL(imgUrl);
            
            const blob = await new Promise((resolve, reject) => {
                canvas.toBlob(b => {
                    if (b) resolve(b);
                    else {
                        canvas.toBlob(b2 => {
                            if (b2) resolve(b2);
                            else reject(new Error("Échec de la conversion de l'image (toBlob)."));
                        }, 'image/jpeg', 0.85);
                    }
                }, 'image/webp', 0.85);
            });
            
            const ext = blob.type === 'image/jpeg' ? '.jpg' : '.webp';
            fileToSend = new File([blob], file.name.replace(/\.[^/.]+$/, "") + ext, { type: blob.type });
            
            canvas.width = 0; canvas.height = 0;
            hasLocalProcessing = true;
            
            if (onProgress) onProgress("optimisation", 30);
        }
        else if (type === 'pdf' && file.type === 'application/pdf') {
            if (onProgress) onProgress("préparation", 5);
            
            if (!window.pdfjsLib) {
                await new Promise((resolve, reject) => {
                    const script = document.createElement('script');
                    script.src = '_externe/pdfjs/pdf.min.js';
                    script.onload = () => {
                        window.pdfjsLib.GlobalWorkerOptions.workerSrc = '_externe/pdfjs/pdf.worker.min.js';
                        resolve();
                    };
                    script.onerror = () => reject(new Error("Impossible de charger PDF.js."));
                    document.head.appendChild(script);
                });
            }

            if (window.pdfjsLib) {
                if (onProgress) onProgress("préparation", 10);
                const arrayBuffer = await file.arrayBuffer();
                const loadingTask = window.pdfjsLib.getDocument({ data: arrayBuffer });
                const pdf = await loadingTask.promise;
                
                totalPages = Math.min(pdf.numPages, 50); 
                
                const page1 = await pdf.getPage(1);
                const viewportThumb = page1.getViewport({ scale: 0.4 });
                const canvasThumb = document.createElement('canvas');
                canvasThumb.width = viewportThumb.width;
                canvasThumb.height = viewportThumb.height;
                await page1.render({ canvasContext: canvasThumb.getContext('2d'), viewport: viewportThumb }).promise;
                previewB64s['preview_b64_0'] = canvasThumb.toDataURL('image/webp', 0.6);
                
                canvasThumb.width = 0; canvasThumb.height = 0;
                page1.cleanup();

                for (let i = 1; i <= totalPages; i++) {
                    if (onProgress) onProgress(`page ${i}/${totalPages}`, 10 + Math.round((i/totalPages)*20));
                    const page = await pdf.getPage(i);
                    
                    const viewport = page.getViewport({ scale: 1.0 });
                    const canvas = document.createElement('canvas');
                    canvas.width = viewport.width;
                    canvas.height = viewport.height;
                    
                    await page.render({ canvasContext: canvas.getContext('2d'), viewport }).promise;
                    
                    let dataUrl = canvas.toDataURL('image/webp', 0.7);
                    if (!dataUrl || dataUrl === 'data:,') {
                        dataUrl = canvas.toDataURL('image/jpeg', 0.7);
                    }
                    previewB64s[`preview_b64_${i}`] = dataUrl;
                    
                    canvas.width = 0; canvas.height = 0;
                    page.cleanup();
                }
                
                pdf.destroy();
                hasLocalProcessing = true;
            }
        }
    } catch (e) {
        console.error("Erreur lors du traitement local du fichier :", e);
        window.showToast("Note : Le document est envoyé sans optimisation ni miniature locale (Aperçu web indisponible).", "warning");
        hasLocalProcessing = false;
        fileToSend = file; 
        previewB64s = {}; 
        totalPages = 1;
    }

    return new Promise((resolve, reject) => {
        const formData = new FormData();
        formData.append('file', fileToSend);
        formData.append('type', type);
        
        if (isPublic) formData.append('isPublic', 'true');
        if (isPrivate) formData.append('is_private', 'true');
        if (clientUid) formData.append('client_uid', clientUid);
        if (panneauId) formData.append('panneau_id', panneauId);
        
        if (Object.keys(previewB64s).length > 0) {
            for (const [key, b64] of Object.entries(previewB64s)) {
                formData.append(key, b64);
            }
            formData.append('numPages', totalPages);
        }

        const xhr = new XMLHttpRequest();
        xhr.open('POST', window.ECO_CONFIG.apiBaseUrl + 'file/upload', true);
        
        xhr.upload.onprogress = (e) => {
            if (e.lengthComputable) {
                const base = hasLocalProcessing ? 30 : 0;
                const multiplier = hasLocalProcessing ? 70 : 100;
                const percent = Math.round(base + ((e.loaded / e.total) * multiplier));
                if (onProgress) onProgress(`envoi ${Math.round((e.loaded / e.total) * 100)}%`, percent);
            }
        };

        xhr.onload = () => {
            if (xhr.status >= 200 && xhr.status < 300) {
                try {
                    const res = JSON.parse(xhr.responseText);
                    if (res.status === 'success') {
                        if (onProgress) onProgress("terminé", 100);
                        
                        const ret = new String(res.data.id);
                        ret.id = res.data.id;
                        ret.numPages = res.data.numPages || 1;
                        resolve(ret);
                    } else reject(res.message || 'Erreur upload serveur');
                } catch (err) { reject('Réponse invalide du serveur'); }
            } else reject('Erreur HTTP ' + xhr.status);
        };
        
        xhr.onerror = () => reject("Erreur réseau (déconnexion ou blocage)");
        xhr.send(formData);
    });
};

// =========================================================================
// 3. HOOKS ET FACTORISATION UI
// =========================================================================

/**
 * Hook personnalisé pour gérer l'état des modales/dialogues via l'URL
 * Centralise la logique SPA Layer 1 (modal) et Layer 2 (dialog)
 */
window.useUrlModal = () => {
    const { useState, useEffect } = React;
    
    const [activeModal, setActiveModal] = useState(new URLSearchParams(window.location.search).get('modal'));
    const [activeDialog, setActiveDialog] = useState(new URLSearchParams(window.location.search).get('dialog'));
    const [targetId, setTargetId] = useState(new URLSearchParams(window.location.search).get('targetId'));
    const [alertParam, setAlertParam] = useState(new URLSearchParams(window.location.search).get('alert'));

    useEffect(() => {
        const handlePopState = () => {
            const params = new URLSearchParams(window.location.search);
            setActiveModal(params.get('modal'));
            setActiveDialog(params.get('dialog'));
            setTargetId(params.get('targetId'));
            setAlertParam(params.get('alert'));
        };
        window.addEventListener('popstate', handlePopState);
        return () => window.removeEventListener('popstate', handlePopState);
    }, []);

    const openModal = (type, id = null) => {
        const u = new URL(window.location);
        u.searchParams.set('modal', type);
        if (id) u.searchParams.set('targetId', id);
        window.history.pushState({ modal: type }, '', u);
        window.dispatchEvent(new Event('popstate'));
    };

    const openDialog = (type, id = null) => {
        const u = new URL(window.location);
        u.searchParams.set('dialog', type);
        if (id) u.searchParams.set('targetId', id);
        window.history.pushState({ dialog: type }, '', u);
        window.dispatchEvent(new Event('popstate'));
    };

    const setAlert = (type) => {
        const u = new URL(window.location);
        u.searchParams.set('alert', type);
        window.history.pushState({ alert: type }, '', u);
        window.dispatchEvent(new Event('popstate'));
    };

    const closeCurrentLayer = () => {
        window.history.back();
    };

    return { activeModal, activeDialog, targetId, alertParam, openModal, openDialog, setAlert, closeCurrentLayer };
};

/**
 * Utilitaire pour récupérer les icônes de manière sécurisée sans polluer le scope global des composants
 */
window.getIcons = () => {
    const fallback = () => null;
    return new Proxy({}, {
        get: (target, prop) => {
            if (typeof window[prop] !== 'undefined') return window[prop];
            return fallback;
        }
    });
};
/* EOF ========== [_www/_react/_ui_utils.jsx] */