// ECO-PANNEAU.FR - __react/socle/_socle_messagerie_chatbox.jsx

const { useState, useEffect, useRef, useCallback, memo } = React;

// 1. - Moteur d'échange (ChatBox et Upload)
window.pano_ChatBox = ({ messages, currentUserRole, panneauId, refreshData, onSend, clientName, settings = {}, themeColor = '#059669', readOnly = false, onAction }) => {
    
    // Conformité de l'API History (Google 2026)
    const { activeDialog: routerActiveDialog, openDialog: routerOpenDialog, closeCurrentLayer: routerCloseLayer } = window.pano_useUrlModal();

    // 1.1 - SÉCURITÉ ANTI-FUITE DE MÉMOIRE : Utilisation du Hook Global Zéro-Dette
    const { isMounted, safeFetch } = window.pano_useSafeFetch();

    const [forceRender, setForceRender] = useState(0);
    useEffect(() => {
        const onPop = () => { if (isMounted.current) setForceRender(prev => prev + 1); };
        window.addEventListener('popstate', onPop);
        return () => window.removeEventListener('popstate', onPop);
    }, [isMounted]);

    const { ArrowUpIcon, ArrowDownIcon, LoaderIcon, ImageIcon, FileTextIcon, PaperclipIcon, XIcon } = window.pano_getIcons();
    const { Button, Modal, ModerationModal, UniversalViewer } = window.pano_getComponents();

    const containerRef = useRef(null);
    const textareaRef = useRef(null);
    const [draft, setDraft] = useState('');
    const [sending, setSending] = useState(false);
    const [uploadProgress, setUploadProgress] = useState('');
    const [selectedFiles, setSelectedFiles] = useState([]);
    const [fileError, setFileError] = useState('');
    const [isAtBottom, setIsAtBottom] = useState(true);
    const [isDragging, setIsDragging] = useState(false);
    const [dragCounter, setDragCounter] = useState(0);
    
    const [moderationWarning, setModerationWarning] = useState(null);
    const [viewingAttachment, setViewingAttachment] = useState(null);
    
    const [rejectTarget, setRejectTarget] = useState(null);
    const [rejectReason, setRejectReason] = useState("");

    const isAtBottomRef = useRef(true);
    
    // RÈGLE MÉTIER : Débridage pour les rôles de confiance
    const maxFiles = (currentUserRole === 'public' || currentUserRole === 'riverain') ? 1 : 10;

    useEffect(() => {
        isAtBottomRef.current = isAtBottom;
    }, [isAtBottom]);

    useEffect(() => {
        if (routerActiveDialog !== 'view_attachment') setViewingAttachment(null);
        if (routerActiveDialog !== 'moderation_warning') setModerationWarning(null);
    }, [routerActiveDialog]);

    useEffect(() => {
        if (textareaRef.current) {
            textareaRef.current.style.height = 'auto';
            textareaRef.current.style.height = `${textareaRef.current.scrollHeight}px`;
        }
    }, [draft]);

    useEffect(() => { 
        const authorRole = currentUserRole === 'admin' ? 'Admin' : 'Client';
        
        const unread = messages.filter(msg => {
            if (msg.resolved) return false;
            const isMe = (currentUserRole === 'admin' && msg.authorType === 'Admin') || 
                         (currentUserRole === 'client' && msg.authorType === 'Client' && (msg.author === 'Client' || msg.author === clientName)) || 
                         (currentUserRole === 'public' && msg.authorType !== 'Admin' && msg.authorType !== 'Client' && msg.authorType !== 'Système' && msg.authorType !== 'Systeme' && msg.authorType !== 'System');
            return !isMe;
        });
        
        if (unread.length > 0 && currentUserRole !== 'public' && currentUserRole !== 'riverain' && !readOnly) { 
            safeFetch('interactions/read', { 
                body: { panneauId, author: authorRole },
                silent: true
            }).then((d) => { 
                if (!isMounted.current) return;
                if(d && refreshData) {
                    refreshData(true); 
                }
            }); 
            
            if (window.Notification && Notification.permission === 'granted' && localStorage.getItem('eco_push_enabled') === 'true') {
                const latestMsgId = unread[0].id || unread[0].created_at; 
                const lastNotified = sessionStorage.getItem('last_notified_msg_' + panneauId);
                
                if (lastNotified !== latestMsgId) {
                    new Notification("eco-panneau.fr", { body: "Nouveau message reçu.", icon: "/favicon.svg" });
                    sessionStorage.setItem('last_notified_msg_' + panneauId, latestMsgId);
                }
            }
        }
    }, [messages, panneauId, currentUserRole, refreshData, readOnly, clientName, safeFetch, isMounted]);

    const handleScroll = useCallback(() => {
        if (!containerRef.current) return;
        const { scrollTop, scrollHeight, clientHeight } = containerRef.current;
        const isBottom = scrollHeight - scrollTop - clientHeight < 50;
        setIsAtBottom(isBottom);
    }, []);

    const scrollToBottom = useCallback((smooth = true) => {
        if (containerRef.current) {
            containerRef.current.scrollTo({ 
                top: containerRef.current.scrollHeight, 
                behavior: smooth ? 'smooth' : 'auto' 
            });
        }
    }, []);

    const handleImageLoad = useCallback(() => {
        if (isAtBottomRef.current) {
            scrollToBottom(true);
        }
    }, [scrollToBottom]);

    useEffect(() => {
        const timer = setTimeout(() => scrollToBottom(false), 100);
        return () => clearTimeout(timer);
    }, [scrollToBottom]);

    useEffect(() => {
        if (isAtBottom) {
            scrollToBottom(true);
        }
    }, [messages.length, isAtBottom, scrollToBottom]);

    // GESTION DES FICHIERS MULTIPLES
    const addFiles = (fileList) => {
        setFileError('');
        const validTypes = ['image/jpeg', 'image/png', 'image/webp', 'application/pdf'];
        const files = Array.from(fileList).filter(f => validTypes.includes(f.type));
        
        if (files.length === 0) {
            setFileError("Format de fichier non supporté.");
            return;
        }

        if (selectedFiles.length + files.length > maxFiles) {
            setFileError(`Maximum ${maxFiles} fichier${maxFiles > 1 ? 's' : ''} autorisé${maxFiles > 1 ? 's' : ''}.`);
            return;
        }
        
        const oversized = files.filter(f => f.size > 5 * 1024 * 1024);
        if (oversized.length > 0) {
            setFileError("Certains fichiers dépassent la limite de 5 Mo.");
            return;
        }

        setSelectedFiles(prev => [...prev, ...files]);
    };

    const handleFileChange = (e) => {
        addFiles(e.target.files);
        e.target.value = null;
    };

    const handlePaste = (e) => {
        if (readOnly) return;
        if (e.clipboardData && e.clipboardData.files && e.clipboardData.files.length > 0) {
            addFiles(e.clipboardData.files);
        }
    };

    const handleDragEnter = (e) => {
        if (readOnly) return;
        e.preventDefault(); e.stopPropagation();
        setDragCounter(prev => prev + 1);
        setIsDragging(true);
    };

    const handleDragLeave = (e) => {
        if (readOnly) return;
        e.preventDefault(); e.stopPropagation();
        setDragCounter(prev => {
            const nc = prev - 1;
            if (nc === 0) setIsDragging(false);
            return nc;
        });
    };

    const handleDragOver = (e) => { if (readOnly) return; e.preventDefault(); e.stopPropagation(); };

    const handleDrop = (e) => {
        if (readOnly) return;
        e.preventDefault(); e.stopPropagation();
        setDragCounter(0);
        setIsDragging(false);
        if (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length > 0) {
            addFiles(e.dataTransfer.files);
        }
    };

    const removeFile = (index) => {
        const newFiles = [...selectedFiles];
        newFiles.splice(index, 1);
        setSelectedFiles(newFiles);
    };

    const executeSend = async (isPendingControl = false) => {
        setSending(true);
        setUploadProgress('');
        let finalDraft = draft.trim();
        
        for (let file of selectedFiles) {
            if (!isMounted.current) return; 
            
            const fileType = file.type.includes('pdf') ? 'pdf' : 'image';
            try {
                const isPublic = currentUserRole === 'public' || currentUserRole === 'riverain';
                const fileId = await window.pano_uploadFile(
                    file, 
                    fileType, 
                    (msg, percent) => {
                        if (!isMounted.current) return; 
                        if (percent < 100) setUploadProgress(`${percent}%`);
                        else setUploadProgress('Finalisation...');
                    }, 
                    isPublic, 
                    false, 
                    null, 
                    panneauId
                );
                
                if (!isMounted.current) return; 
                
                const actualId = fileId.id || fileId;
                const actualNumPages = fileId.numPages || 1;
                finalDraft += `\n[ATTACHMENT:${actualId}:${fileType}:${actualNumPages}]`;
            } catch(err) {
                if (!isMounted.current) return; 
                setFileError("Erreur lors de l'upload : " + err.message);
                setSending(false);
                setUploadProgress('');
                return;
            }
        }

        if (!isMounted.current) return; 

        if (onSend) await onSend(finalDraft, isPendingControl);
        
        if (!isMounted.current) return; 

        setDraft(''); 
        setSelectedFiles([]);
        setUploadProgress('');
        setSending(false);
        setTimeout(() => { if (isMounted.current) scrollToBottom(true); }, 50);
    };

    const handleInitialSend = (e) => {
        if (e && e.preventDefault) e.preventDefault(); 
        
        if (sending) return;
        
        if (!draft.trim() && selectedFiles.length === 0) return;
        
        if (currentUserRole === 'public' || currentUserRole === 'riverain') {
            if (window.pano_checkForBlacklist) {
                const word = window.pano_checkForBlacklist(draft, settings);
                if (word) { 
                    setModerationWarning(word); 
                    routerOpenDialog('moderation_warning');
                    return; 
                }
            } else {
                if (window.pano_logFallback) window.pano_logFallback("Bouclier sémantique introuvable. Modération ignorée sur ChatBox.");
            }
        }
        executeSend(false);
    };

    const triggerModerate = (msgId, action) => {
        if (action === 'reject') {
            setRejectTarget(msgId);
            setRejectReason("");
        } else {
            executeModerate(msgId, action);
        }
    };

    const executeModerate = async (msgId, action, reason = "") => {
        const d = await safeFetch('interactions/moderate', {
            body: { id: msgId, action: action, reason: reason },
            setLoading: setSending,
            successMessage: action === 'approve' ? "Message validé et transmis." : "Message rejeté et supprimé."
        });
        
        if (!isMounted.current) return; 

        if (d && refreshData) refreshData(true);
        setRejectTarget(null);
    };

    const triggerViewAttachment = (att) => {
        setViewingAttachment(att);
        routerOpenDialog('view_attachment');
    };

    // ACTION MÉTIER : Suppression d'une pièce jointe pour libérer du quota
    const handleDeleteAttachment = async (msgId, attId, attType) => {
        const d = await safeFetch('interactions/delete_attachment', {
            body: { message_id: msgId, file_id: attId, file_type: attType },
            setLoading: setSending,
            successMessage: "Pièce jointe supprimée avec succès pour libérer de l'espace."
        });
        if (!isMounted.current) return;
        if (d && refreshData) refreshData(true);
    };
    
    // COMPOSANT BULLE DE MESSAGE INTÉGRÉ ICI
    const MessageBubble = window.pano_MessageBubble || memo(({ msg, isMe, displayAuthor, onImageLoad, onViewAttachment, isAdmin, onModerate, themeColor = '#059669', onAction, canDeleteAttachment, onDeleteAttachment }) => {
        const { AlertTriangleIcon, LoaderIcon, FileTextIcon, EditIcon, CheckCircleIcon, Trash2Icon } = window.pano_getIcons();
        const { Button, PdfThumbnail } = window.pano_getComponents();

        useEffect(() => {
            if (!window.DOMPurify && window.pano_logFallback) {
                window.pano_logFallback("DOMPurify introuvable dans la bulle locale de ChatBox. Utilisation du fallback regex pour la sécurisation HTML.");
            }
        }, []);

        const attachments = [];
        const deletedAttachments = [];
        
        const regex = /\[ATTACHMENT:([a-zA-Z0-9_.\/-]+):(image|pdf)(?::(\d+))?\]/g;
        let match;
        
        while ((match = regex.exec(msg.detail || '')) !== null) {
            attachments.push({ id: match[1], type: match[2], numPages: match[3] ? parseInt(match[3], 10) : 1 });
        }

        const delRegex = /\[DELETED_ATTACHMENT(?::(image|pdf))?\]/g;
        let delMatch;
        while ((delMatch = delRegex.exec(msg.detail || '')) !== null) {
            deletedAttachments.push({ type: delMatch[1] || 'fichier' });
        }
        
        const textContent = (msg.detail || '').replace(regex, '').replace(delRegex, '').trim();
        const isHtmlAllowed = msg.authorType === 'Admin' || msg.authorType === 'Système' || msg.authorType === 'Systeme' || msg.authorType === 'System';

        const suspendRegex = /Le panneau "([^"]+)" a été (?:suspendu|rejeté)/i;
        const suspendMatch = textContent.match(suspendRegex);
        
        const isTemp = msg.id.toString().startsWith('temp_');

        const handleCorrigerClick = (e) => {
            e.preventDefault();
            if (onAction && suspendMatch) {
                onAction('correct_panel', suspendMatch[1]);
            }
        };

        const handleHtmlClick = (e) => {
            const grantBtn = e.target.closest('[data-action="grant_access"]');
            if (grantBtn) {
                e.preventDefault();
                e.stopPropagation();
                window.dispatchEvent(new CustomEvent('chat_grant_access'));
            }
        };

        return (
            <div className={`flex flex-col w-full ${isMe ? 'items-end' : 'items-start'} mb-4`}>
                {msg.isAlert === 2 && (
                    <div className="bg-amber-100 text-amber-800 text-[10px] font-bold px-2 py-1 rounded-t-lg mb-0.5 flex flex-col sm:flex-row sm:items-center gap-2 w-fit max-w-[85%] sm:max-w-[70%]">
                        <span className="flex items-center gap-1 shrink-0"><AlertTriangleIcon size={12}/> En attente de contrôle</span>
                        {isAdmin && onModerate && (
                            <div className="flex items-center gap-1 sm:ml-auto">
                                <Button variant="successSolid" onClick={() => onModerate(msg.id, 'approve')} className="py-1 px-2 text-[9px] h-auto min-h-0 shadow-none rounded-md" icon={CheckCircleIcon}>
                                    Valider
                                </Button>
                                <Button variant="dangerSolid" shape="round" onClick={() => onModerate(msg.id, 'reject')} className="p-1 h-auto min-h-0 shadow-none" icon={Trash2Icon} title="Rejeter" />
                            </div>
                        )}
                    </div>
                )}
                
                <div 
                    className={`max-w-[85%] sm:max-w-[70%] p-3 rounded-2xl flex flex-col shadow-sm ${
                        isMe 
                        ? 'text-white rounded-br-sm' 
                        : 'bg-white border border-slate-200 text-slate-700 rounded-bl-sm'
                    } ${msg.isAlert === 2 && !isMe ? 'opacity-60 border-amber-300 bg-amber-50' : ''}`}
                    style={isMe ? { backgroundColor: themeColor } : undefined}
                >
                    
                    {textContent && (
                        isHtmlAllowed ? (
                            <div 
                                onClick={handleHtmlClick}
                                dangerouslySetInnerHTML={{ 
                                    __html: window.DOMPurify 
                                        ? window.DOMPurify.sanitize(textContent, { USE_PROFILES: { html: true } }) 
                                        : textContent.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;')
                                }} 
                                className="text-sm leading-relaxed whitespace-pre-wrap admin-html break-words" 
                            />
                        ) : (
                            <p className="text-sm leading-relaxed whitespace-pre-wrap break-words">{textContent}</p>
                        )
                    )}

                    {suspendMatch && msg.authorType === 'Admin' && !isMe && (
                        <div className="mt-3 pt-3 border-t border-slate-200/50 w-full">
                            <Button 
                                variant="primary" 
                                icon={EditIcon} 
                                onClick={handleCorrigerClick}
                                className="w-full justify-center py-2.5 shadow-md"
                            >
                                Corriger le panneau
                            </Button>
                        </div>
                    )}
                    
                    {attachments.length > 0 && (
                        <div className={`mt-2 flex flex-wrap gap-2 ${textContent ? 'pt-2 border-t border-white/10' : ''}`}>
                            {attachments.map((att, i) => (
                                <React.Fragment key={i}>
                                    {att.type === 'image' && (
                                        <div className="relative group shrink-0 max-w-full">
                                            <div onClick={() => onViewAttachment(att)} className="cursor-pointer">
                                                <img 
                                                    src={`${window.pano_CONFIG.apiBaseUrl}file/download&type=image&id=${att.id}&preview=1`} 
                                                    className={`rounded-lg max-h-48 max-w-full object-contain border transition-transform hover:scale-[1.02] ${isMe ? 'border-white/20 bg-black/10' : 'border-slate-200/50 bg-slate-50'}`} 
                                                    alt="Pièce jointe" 
                                                    loading="lazy"
                                                    onLoad={onImageLoad}
                                                />
                                            </div>
                                            {canDeleteAttachment && onDeleteAttachment && (
                                                <Button 
                                                    variant="danger" 
                                                    shape="round" 
                                                    icon={Trash2Icon}
                                                    onClick={(e) => { e.stopPropagation(); onDeleteAttachment(msg.id, att.id, att.type); }}
                                                    className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-all shadow-md p-1.5 w-7 h-7"
                                                    title="Supprimer cette pièce jointe (Libérer de l'espace)"
                                                />
                                            )}
                                        </div>
                                    )}
                                    {att.type === 'pdf' && PdfThumbnail && (
                                        <div className="relative group shrink-0 w-32">
                                            <div onClick={() => onViewAttachment(att)} className={`cursor-pointer rounded-xl border flex flex-col text-left transition-transform hover:scale-[1.02] h-full ${isMe ? 'border-white/20 bg-black/10' : 'border-slate-200 bg-white'}`}>
                                                <PdfThumbnail id={att.id} />
                                                <div className={`p-2 flex items-center gap-1.5 shrink-0 border-t rounded-b-xl ${isMe ? 'border-white/20 text-white' : 'border-slate-100 text-slate-700'}`}>
                                                    <FileTextIcon size={12} className="shrink-0" />
                                                    <span className="text-[9px] font-bold break-words leading-tight">Aperçu PDF</span>
                                                </div>
                                            </div>
                                            {canDeleteAttachment && onDeleteAttachment && (
                                                <Button 
                                                    variant="danger" 
                                                    shape="round" 
                                                    icon={Trash2Icon}
                                                    onClick={(e) => { e.stopPropagation(); onDeleteAttachment(msg.id, att.id, att.type); }}
                                                    className="absolute top-1 right-1 opacity-0 group-hover:opacity-100 transition-all shadow-md p-1.5 w-7 h-7"
                                                    title="Supprimer cette pièce jointe (Libérer de l'espace)"
                                                />
                                            )}
                                        </div>
                                    )}
                                </React.Fragment>
                            ))}
                        </div>
                    )}

                    {deletedAttachments.length > 0 && (
                        <div className={`mt-2 flex flex-col gap-2 ${textContent || attachments.length > 0 ? 'pt-2 border-t border-white/10' : ''}`}>
                            {deletedAttachments.map((att, i) => (
                                <div key={`del_${i}`} className={`p-3 rounded-xl flex items-center gap-3 border min-w-[200px] ${isMe ? 'bg-black/10 border-white/20 text-white' : 'bg-slate-100 border-slate-200 text-slate-500'}`}>
                                    <div className={`w-10 h-10 rounded-lg flex items-center justify-center shrink-0 ${isMe ? 'bg-black/20 text-white/50' : 'bg-slate-200 text-slate-400'}`}>
                                        <Trash2Icon size={20} />
                                    </div>
                                    <span className="text-xs font-medium italic leading-snug">
                                        Fichier supprimé par l'utilisateur pour libérer de l'espace.
                                    </span>
                                </div>
                            ))}
                        </div>
                    )}
                </div>
                
                <span className="text-[10px] font-bold text-slate-400 mt-1 px-1 flex items-center gap-1 break-words">
                    {displayAuthor} • {isTemp ? <span className="italic text-slate-400">Envoi en cours...</span> : new Date(String(msg.created_at || '').replace(' ', 'T')).toLocaleString('fr-FR')}
                    {isTemp && <LoaderIcon size={10} className="animate-spin inline text-slate-300 ml-1" />}
                </span>
            </div>
        );
    }, (prevProps, nextProps) => {
        return prevProps.msg.id === nextProps.msg.id && 
               prevProps.msg.resolved === nextProps.msg.resolved && 
               prevProps.msg.isAlert === nextProps.msg.isAlert && 
               prevProps.isAdmin === nextProps.isAdmin &&
               prevProps.themeColor === nextProps.themeColor;
    });

    return (
        <div 
            className="flex flex-col h-full bg-slate-50 relative rounded-b-3xl xl:rounded-b-none xl:rounded-br-3xl overflow-x-hidden"
            onDragEnter={handleDragEnter}
            onDragLeave={handleDragLeave}
            onDragOver={handleDragOver}
            onDrop={handleDrop}
        >
            {isDragging && !readOnly && (
                <div className="absolute inset-0 z-[100] bg-emerald-500/20 backdrop-blur-sm border-4 border-emerald-500 border-dashed rounded-2xl flex flex-col items-center justify-center pointer-events-none">
                    <div className="bg-white p-6 rounded-2xl shadow-xl flex flex-col items-center text-center">
                        <ArrowUpIcon size={48} className="text-emerald-500 mb-4 animate-bounce" />
                        <span className="text-xl font-black text-slate-800 break-words">Déposez vos fichiers ici</span>
                        <span className="text-sm font-bold text-slate-500 mt-2 break-words">Image ou PDF ({maxFiles} max)</span>
                    </div>
                </div>
            )}

            {rejectTarget && Modal && (
                window.ReactDOM ? window.ReactDOM.createPortal(
                    <Modal 
                        title="Refuser le message" 
                        type="warning" 
                        onClose={() => setRejectTarget(null)} 
                        preventClose={sending} 
                        actions={(close) => (
                            <>
                                <Button variant="secondary" onClick={close} disabled={sending} className="flex-1 py-3 justify-center text-xs">Annuler</Button>
                                <Button variant="warningSolid" onClick={() => executeModerate(rejectTarget, 'reject', rejectReason)} disabled={sending} className="flex-[2] py-3 uppercase tracking-widest justify-center shadow-md text-xs">
                                    {sending ? 'Traitement...' : 'Confirmer le refus'}
                                </Button>
                            </>
                        )}
                    >
                        <div className="space-y-4">
                            <p className="text-sm text-slate-600 break-words">Vous êtes sur le point de rejeter ce message. Il sera supprimé et non transmis au destinataire.</p>
                            <div className="space-y-2">
                                <label className="text-xs font-bold text-slate-500 uppercase block break-words">Explication pour l'auteur (Optionnel)</label>
                                <textarea 
                                    value={rejectReason} 
                                    onChange={e => setRejectReason(e.target.value)} 
                                    placeholder="Ex: Les injures ne sont pas tolérées sur la plateforme..." 
                                    rows={3} 
                                    disabled={sending}
                                    className="w-full border-2 border-slate-200 rounded-xl p-3 outline-none focus:border-amber-500 transition resize-none custom-scrollbar text-sm break-words"
                                />
                                <p className="text-[10px] text-slate-400 break-words">Cette explication sera incluse dans l'e-mail de notification envoyé à l'auteur.</p>
                            </div>
                        </div>
                    </Modal>,
                    document.body
                ) : null
            )}

            {routerActiveDialog === 'moderation_warning' && moderationWarning && ModerationModal && (
                window.ReactDOM ? window.ReactDOM.createPortal(
                    <ModerationModal word={moderationWarning} onModify={routerCloseLayer} onRequestControl={() => { routerCloseLayer(); setTimeout(() => executeSend(true), 100); }} />,
                    document.body
                ) : null
            )}

            {routerActiveDialog === 'view_attachment' && viewingAttachment && UniversalViewer && (
                window.ReactDOM ? window.ReactDOM.createPortal(
                    <UniversalViewer 
                        fileId={viewingAttachment.id}
                        fileType={viewingAttachment.type}
                        fileName="Pièce jointe"
                        numPages={viewingAttachment.numPages}
                        isPrivate={false}
                        useSafePdfMode={true} 
                        onClose={routerCloseLayer}
                    />,
                    document.body
                ) : null
            )}

            <div ref={containerRef} onScroll={handleScroll} className="flex-1 min-h-0 overflow-y-auto p-4 flex flex-col custom-scrollbar relative overflow-x-hidden">
                {messages.map((msg) => {
                    const isMe = (currentUserRole === 'admin' && msg.authorType === 'Admin') || 
                                 (currentUserRole === 'client' && msg.authorType === 'Client' && (msg.author === 'Client' || msg.author === clientName)) || 
                                 (currentUserRole === 'public' && msg.authorType !== 'Admin' && msg.authorType !== 'Client' && msg.authorType !== 'Système' && msg.authorType !== 'Systeme' && msg.authorType !== 'System');

                    let displayAuthor = msg.author;
                    if (panneauId === 'CONTACT_PUBLIC') {
                        if (msg.authorType === 'Admin' || msg.author === 'Admin') displayAuthor = 'Service client';
                        else if (msg.authorType === 'Système' || msg.authorType === 'Systeme' || msg.author === 'Système' || msg.author === 'Systeme' || msg.authorType === 'System') displayAuthor = 'Système';
                        else displayAuthor = isMe ? 'Vous' : `Visiteur ${msg.author}`;
                    } else {
                        if (msg.authorType === 'Admin' || msg.author === 'Admin') displayAuthor = 'Support';
                        else if (msg.authorType === 'Client' || msg.author === 'Client') displayAuthor = isMe ? 'Vous' : msg.author; 
                        else if (msg.authorType === 'Système' || msg.authorType === 'Systeme' || msg.author === 'Système' || msg.author === 'Systeme' || msg.authorType === 'System') displayAuthor = 'Système';
                        else displayAuthor = isMe ? 'Vous' : 'Riverain';
                    }

                    return (
                        <MessageBubble 
                            key={msg.id} 
                            msg={msg} 
                            isMe={isMe} 
                            displayAuthor={displayAuthor} 
                            onImageLoad={handleImageLoad} 
                            onViewAttachment={triggerViewAttachment} 
                            isAdmin={currentUserRole === 'admin'}
                            onModerate={triggerModerate} 
                            themeColor={themeColor} 
                            onAction={onAction}
                            canDeleteAttachment={currentUserRole === 'admin' || currentUserRole === 'client'}
                            onDeleteAttachment={handleDeleteAttachment}
                        />
                    );
                })}

                {messages.length === 0 && <div className="text-center text-slate-400 text-sm font-medium italic my-auto break-words">Aucun message.</div>}
            </div>

            {!isAtBottom && messages.length > 0 && (
                <div className="absolute bottom-24 left-0 right-0 flex justify-center pointer-events-none z-10">
                    <div role="button" tabIndex={0} onClick={() => scrollToBottom(true)} className="bg-slate-800/90 backdrop-blur text-white px-4 py-2 rounded-full shadow-lg text-xs font-bold pointer-events-auto hover:bg-slate-900 transition flex items-center gap-2 animate-in fade-in slide-in-from-bottom-2 cursor-pointer">
                        <ArrowDownIcon size={14}/> Nouveaux messages
                    </div>
                </div>
            )}
            
            {readOnly ? (
                <div className="flex flex-col w-full bg-slate-50 border-t border-slate-200 shrink-0 z-20 transition-colors rounded-b-3xl xl:rounded-b-none xl:rounded-br-3xl p-5 text-center text-slate-500">
                    <p className="font-bold text-sm break-words">Mode lecture seule</p>
                    <p className="text-xs mt-1 break-words">Vous n'avez pas les droits requis pour envoyer des messages dans cette conversation.</p>
                </div>
            ) : (
                <div className="flex flex-col w-full bg-white border-t border-slate-200 shrink-0 z-20 transition-colors rounded-b-3xl xl:rounded-b-none xl:rounded-br-3xl overflow-x-hidden">
                    {fileError && <p className="text-xs font-bold text-red-500 px-4 pt-2 break-words">{fileError}</p>}
                    
                    {selectedFiles.length > 0 && (
                        <div className="px-4 py-2 mt-2 mx-2 bg-slate-50 border border-slate-200 rounded-xl flex flex-col gap-2 max-h-32 overflow-y-auto custom-scrollbar">
                            {selectedFiles.map((f, i) => (
                                <div key={i} className="flex items-center justify-between shrink-0 min-w-0">
                                    <span className="text-xs font-bold text-slate-600 flex items-center gap-2 min-w-0 flex-1 pr-2">
                                        {f.type.includes('pdf') ? <FileTextIcon size={14} className="shrink-0"/> : <ImageIcon size={14} className="shrink-0"/>}
                                        <span className="break-all leading-tight">{f.name}</span>
                                    </span>
                                    <Button variant="danger" shape="round" onClick={() => { if(!sending) removeFile(i); }} disabled={sending} className="p-1.5 shadow-none border-none hover:bg-red-100 shrink-0" icon={XIcon} title="Supprimer la pièce jointe" />
                                </div>
                            ))}
                        </div>
                    )}
                    
                    <form onSubmit={handleInitialSend} className="p-3 flex gap-2 shrink-0 items-end">
                        <label className={`cursor-pointer p-3 rounded-xl transition shadow-sm flex items-center justify-center shrink-0 border ${sending || selectedFiles.length >= maxFiles ? 'bg-slate-50 border-slate-100 text-slate-400 opacity-50' : 'bg-white border-slate-200 text-slate-500 hover:text-slate-800 hover:bg-slate-50'}`} style={(!sending && selectedFiles.length < maxFiles) ? { color: themeColor } : {}} title={`Joindre une photo ou un PDF (${maxFiles} max)`}>
                            <PaperclipIcon size={20} />
                            <input type="file" multiple={maxFiles > 1} className="hidden" accept="image/jpeg, image/png, image/webp, application/pdf" disabled={sending || selectedFiles.length >= maxFiles} onChange={handleFileChange} />
                        </label>
                        <textarea 
                            ref={textareaRef}
                            value={draft} 
                            onChange={e => setDraft(e.target.value)} 
                            onPaste={handlePaste}
                            onKeyDown={(e) => {
                                if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
                                    e.preventDefault();
                                    if (!sending && (draft.trim() || selectedFiles.length > 0)) {
                                        handleInitialSend(e);
                                    }
                                }
                            }}
                            placeholder="Écrire une réponse... (Ctrl+Entrée pour envoyer)" 
                            disabled={sending} 
                            rows="1" 
                            className="flex-1 min-w-0 border-2 border-slate-100 rounded-xl px-4 py-3 outline-none focus:border-slate-300 text-sm transition resize-none custom-scrollbar max-h-32 overflow-y-auto disabled:opacity-50 break-words" 
                        />
                        <Button 
                            type="submit"
                            onClick={handleInitialSend}
                            disabled={sending || (!draft.trim() && selectedFiles.length === 0)}
                            className="text-white p-3 shadow-sm shrink-0 border-none"
                            style={{ backgroundColor: themeColor }}
                            title="Envoyer le message"
                        >
                            {sending ? (
                                uploadProgress ? <span className="text-[10px] font-black px-1">{uploadProgress}</span> : <LoaderIcon className="animate-spin w-5 h-5"/>
                            ) : (
                                <ArrowUpIcon size={20} />
                            )}
                        </Button>
                    </form>
                </div>
            )}
        </div>
    );
};

/* EOF ========== [__react/socle/_socle_messagerie_chatbox.jsx] */