import { useCallback, useEffect, useRef, useState } from 'react';
import { useSearchParams, useNavigate } from 'react-router-dom';
import { api } from '../api.js';
import { useAuth } from '../context/AuthContext.jsx';
import NotifTypeAvatar, { TYPE_META } from '../components/NotifTypeAvatar.jsx';
import { fmtDate } from '../utils/format.js';

// ── Helpers ────────────────────────────────────────────────────────────────
function timeAgo(dateStr) {
  if (!dateStr) return '';
  const diff = (Date.now() - new Date(dateStr + 'Z').getTime()) / 1000;
  if (diff < 60)        return 'À l\'instant';
  if (diff < 3600)      return `Il y a ${Math.floor(diff / 60)} min`;
  if (diff < 86400)     return `Il y a ${Math.floor(diff / 3600)} h`;
  if (diff < 86400 * 7) return `Il y a ${Math.floor(diff / 86400)} j`;
  return new Date(dateStr + 'Z').toLocaleDateString('fr-FR', { day: '2-digit', month: 'short', year: 'numeric' });
}

function fmtSize(bytes) {
  if (bytes < 1024) return `${bytes} o`;
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(0)} Ko`;
  return `${(bytes / 1024 / 1024).toFixed(1)} Mo`;
}

function initials(name) {
  if (!name) return '?';
  // Si c'est un email, prendre les 2 premières lettres avant le @
  if (name.includes('@')) return name.split('@')[0].slice(0, 2).toUpperCase();
  return name.split(' ').filter(Boolean).map(w => w[0]).join('').toUpperCase().slice(0, 2);
}

// ── Avatar lettres ─────────────────────────────────────────────────────────
function UserAvatar({ name, isAdmin, size = 38 }) {
  const bg = isAdmin ? 'var(--primary)' : '#64748b';
  return (
    <div style={{
      width: size, height: size, borderRadius: '50%', flexShrink: 0,
      background: bg, color: '#fff', fontSize: size * 0.36,
      display: 'flex', alignItems: 'center', justifyContent: 'center',
      fontWeight: 700, letterSpacing: '-0.5px',
    }}>
      {initials(name)}
    </div>
  );
}

// ── Badge statut ticket ────────────────────────────────────────────────────
function StatusBadge({ status }) {
  const styles = {
    open:     { bg: '#dcfce7', color: '#16a34a', label: 'Ouvert' },
    resolved: { bg: '#f1f5f9', color: '#64748b', label: 'Résolu' },
  };
  const s = styles[status] ?? styles.open;
  return (
    <span style={{
      fontSize: 11, fontWeight: 600, padding: '2px 8px', borderRadius: 99,
      background: s.bg, color: s.color,
    }}>{s.label}</span>
  );
}

// ── Icône fichier selon MIME ───────────────────────────────────────────────
function FileIcon({ mime }) {
  if (mime?.startsWith('image/')) return '🖼';
  if (mime === 'application/pdf') return '📄';
  if (mime?.includes('zip') || mime?.includes('compressed')) return '🗜';
  if (mime?.includes('spreadsheet') || mime?.includes('excel')) return '📊';
  return '📎';
}

// ── Chip fichier avec aperçu image ────────────────────────────────────────
function FileChip({ file, onRemove }) {
  const isImage = file.type.startsWith('image/');
  const [url, setUrl] = useState(null);
  useEffect(() => {
    if (!isImage) return;
    const objUrl = URL.createObjectURL(file);
    setUrl(objUrl);
    return () => URL.revokeObjectURL(objUrl);
  }, [file, isImage]);
  return (
    <span className={`comm-file-chip${isImage ? ' has-preview' : ''}`}>
      {isImage && url
        ? <img src={url} alt={file.name} className="comm-file-chip-img" />
        : <span style={{ fontSize: 16 }}><FileIcon mime={file.type} /></span>
      }
      <span className="comm-attach-name">{file.name}</span>
      <button type="button" className="comm-file-remove" onClick={onRemove}>×</button>
    </span>
  );
}

// ── Gestion du coller image ───────────────────────────────────────────────
function onPasteImage(setter) {
  return (e) => {
    const items = Array.from(e.clipboardData?.items ?? []);
    const images = items
      .filter(item => item.type.startsWith('image/'))
      .map(item => item.getAsFile())
      .filter(Boolean)
      .map((f, i) => {
        const ext = f.type.split('/')[1]?.replace('jpeg', 'jpg') || 'png';
        return new File([f], `image-collée-${Date.now()}${i ? '-' + i : ''}.${ext}`, { type: f.type });
      });
    if (images.length) {
      setter(prev => [...prev, ...images]);
    }
  };
}

// ── Helper nom téléchargement ─────────────────────────────────────────────
function buildDownloadName(ticketNumber, msgDate, originalName) {
  const d    = msgDate ? new Date(msgDate.endsWith('Z') ? msgDate : msgDate + 'Z') : new Date();
  const yyyy = d.getFullYear();
  const mm   = String(d.getMonth() + 1).padStart(2, '0');
  const dd   = String(d.getDate()).padStart(2, '0');
  const hh   = String(d.getHours()).padStart(2, '0');
  const mn   = String(d.getMinutes()).padStart(2, '0');
  const ext  = (originalName.match(/\.[^.]+$/) ?? [''])[0];
  const base = originalName.replace(/\.[^.]+$/, '').replace(/[^\w\-]/g, '_').slice(0, 40);
  return `${ticketNumber}_${yyyy}-${mm}-${dd}_${hh}h${mn}_${base}${ext}`;
}

// ── Image jointe dans le thread ───────────────────────────────────────────
function AttachmentImage({ filename, originalName, size, onExpand, ticketNumber, msgDate }) {
  const [url, setUrl] = useState(null);
  useEffect(() => {
    let objUrl;
    api.blob(`/tickets/attachments/${filename}`)
    .then(blob => {
      objUrl = URL.createObjectURL(blob);
      setUrl(objUrl);
    }).catch(() => {});
    return () => { if (objUrl) URL.revokeObjectURL(objUrl); };
  }, [filename]);

  const handleDownload = () => {
    if (!url) return;
    const a = document.createElement('a');
    a.href = url; a.download = buildDownloadName(ticketNumber || 'TK', msgDate, originalName); a.click();
  };

  return (
    <div className="comm-attach-image">
      {url
        ? <img src={url} alt={originalName} className="comm-attach-img" />
        : <div className="comm-attach-img-skeleton">🖼</div>
      }
      {url && (
        <div className="comm-attach-image-overlay">
          <button className="comm-attach-overlay-btn" title="Agrandir" onClick={() => onExpand(url, originalName)}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>
          </button>
          <button className="comm-attach-overlay-btn" title="Télécharger" onClick={handleDownload}>
            <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
          </button>
        </div>
      )}
      <div className="comm-attach-img-name">{originalName} · {fmtSize(size)}</div>
    </div>
  );
}

// ── Constantes tags tickets ───────────────────────────────────────────────
const TICKET_TYPES = [
  { value: 'bug_bloquant',     label: 'Bug bloquant' },
  { value: 'bug_non_bloquant', label: 'Bug non bloquant' },
  { value: 'amelioration',     label: 'Amélioration' },
  { value: 'question',         label: 'Question' },
];
const TICKET_PAGES = [
  { value: 'tableau_de_bord',  label: 'Tableau de bord' },
  { value: 'plateformes',      label: 'Plateformes' },
  { value: 'investissements',  label: 'Investissements' },
  { value: 'depots_retraits',  label: 'Dépôts / Retraits' },
  { value: 'fiscalite',        label: 'Fiscalité' },
  { value: 'mon_compte',       label: 'Mon compte' },
  { value: 'parametres',       label: 'Paramètres' },
  { value: 'autres',           label: 'Autres' },
];
const TYPE_COLORS = {
  bug_bloquant:     { background: '#fee2e2', color: '#b91c1c' },
  bug_non_bloquant: { background: '#ffedd5', color: '#c2410c' },
  amelioration:     { background: '#dbeafe', color: '#1d4ed8' },
  question:         { background: '#d1fae5', color: '#065f46' },
};

// ── TagSelect ─────────────────────────────────────────────────────────────
function TagSelect({ label, options, value, onChange, multi = false }) {
  const [open, setOpen] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    const h = e => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', h);
    return () => document.removeEventListener('mousedown', h);
  }, []);

  const isSelected = v => multi ? (value || []).includes(v) : value === v;

  const toggle = v => {
    if (multi) {
      const arr = value || [];
      onChange(arr.includes(v) ? arr.filter(x => x !== v) : [...arr, v]);
    } else {
      onChange(value === v ? null : v);
      setOpen(false);
    }
  };

  const selectedCount = multi ? (value?.length || 0) : (value ? 1 : 0);
  const headerLabel = multi
    ? (selectedCount === 0 ? label : `${selectedCount} sélectionné${selectedCount > 1 ? 's' : ''}`)
    : (value ? options.find(o => o.value === value)?.label ?? label : label);

  return (
    <div className="tag-select" ref={ref}>
      <button
        type="button"
        className={`tag-select-trigger${selectedCount > 0 ? ' has-value' : ''}${open ? ' open' : ''}`}
        onClick={() => setOpen(o => !o)}
      >
        <span>{headerLabel}</span>
        <svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5">
          {open ? <polyline points="18 15 12 9 6 15"/> : <polyline points="6 9 12 15 18 9"/>}
        </svg>
      </button>
      {open && (
        <div className="tag-select-dropdown">
          {options.map(opt => (
            <label key={opt.value} className={`tag-select-option${isSelected(opt.value) ? ' selected' : ''}`}>
              <input
                type={multi ? 'checkbox' : 'radio'}
                checked={isSelected(opt.value)}
                onChange={() => toggle(opt.value)}
              />
              <span>{opt.label}</span>
              {isSelected(opt.value) && (
                <svg className="tag-select-check" width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5"><polyline points="20 6 9 17 4 12"/></svg>
              )}
            </label>
          ))}
        </div>
      )}
    </div>
  );
}

// ── TicketChips ───────────────────────────────────────────────────────────
function TicketChips({ ticketType, ticketPages }) {
  const type  = TICKET_TYPES.find(t => t.value === ticketType);
  const pages = ticketPages ? (Array.isArray(ticketPages) ? ticketPages : JSON.parse(ticketPages)) : [];
  if (!type && pages.length === 0) return null;
  return (
    <div className="ticket-chips">
      {type && (
        <span className="ticket-chip ticket-chip-type" style={TYPE_COLORS[ticketType] || {}}>
          {type.label}
        </span>
      )}
      {pages.map(p => {
        const page = TICKET_PAGES.find(x => x.value === p);
        return page ? <span key={p} className="ticket-chip ticket-chip-page">{page.label}</span> : null;
      })}
    </div>
  );
}

// ── MessageBody — rendu HTML avec overlay CSS sur images inline ────────────
const ICON_EXPAND_SVG = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="15 3 21 3 21 9"/><polyline points="9 21 3 21 3 15"/><line x1="21" y1="3" x2="14" y2="10"/><line x1="3" y1="21" x2="10" y2="14"/></svg>`;
const ICON_DOWNLOAD_SVG = `<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>`;

function MessageBody({ html, onExpand, ticketNumber, msgDate }) {
  const ref = useRef(null);

  useEffect(() => {
    const el = ref.current;
    if (!el) return;
    el.querySelectorAll('.msg-inline-img:not([data-hovered])').forEach(img => {
      img.setAttribute('data-hovered', '1');
      const wrap = document.createElement('span');
      wrap.className = 'msg-img-wrap';
      img.parentNode.insertBefore(wrap, img);
      wrap.appendChild(img);
      const overlay = document.createElement('span');
      overlay.className = 'msg-img-hover-overlay';
      overlay.innerHTML =
        `<button type="button" class="comm-attach-overlay-btn" title="Agrandir" data-img-action="expand">${ICON_EXPAND_SVG}</button>` +
        `<button type="button" class="comm-attach-overlay-btn" title="Télécharger" data-img-action="download">${ICON_DOWNLOAD_SVG}</button>`;
      wrap.appendChild(overlay);
    });
  }, [html]);

  const handleClick = useCallback((e) => {
    const btn = e.target.closest('[data-img-action]');
    if (btn) {
      const img = btn.closest('.msg-img-wrap')?.querySelector('img');
      if (!img) return;
      if (btn.dataset.imgAction === 'expand') {
        onExpand(img.src, img.alt || 'image');
      } else {
        const a = document.createElement('a');
        a.href = img.src; a.download = buildDownloadName(ticketNumber || 'TK', msgDate, img.alt || 'image.png'); a.click();
      }
      return;
    }
    if (e.target.tagName === 'IMG') onExpand(e.target.src, e.target.alt || 'image');
  }, [onExpand]);

  return (
    <div
      ref={ref}
      className="comm-message-body"
      dangerouslySetInnerHTML={{ __html: sanitizeHTML(html) }}
      onClick={handleClick}
    />
  );
}

// ── Sanitisation HTML (affichage messages) ────────────────────────────────
const ALLOWED_TAGS = new Set(['b','i','u','s','strong','em','ul','ol','li','a','br','p','div','span','img']);
function sanitizeHTML(html) {
  if (!html) return '';
  const doc = new DOMParser().parseFromString(html, 'text/html');
  function clean(node) {
    for (const child of [...node.childNodes]) {
      if (child.nodeType === 3) continue;
      if (child.nodeType === 1) {
        const tag = child.tagName.toLowerCase();
        if (!ALLOWED_TAGS.has(tag)) { child.replaceWith(...child.childNodes); continue; }
        if (tag === 'img') {
          const src = child.getAttribute('src') ?? '';
          if (!src.startsWith('data:image/')) { child.remove(); continue; }
          // Garde uniquement src et alt
          for (const attr of [...child.attributes]) {
            if (attr.name !== 'src' && attr.name !== 'alt') child.removeAttribute(attr.name);
          }
          child.setAttribute('class', 'msg-inline-img');
          continue; // pas d'enfants
        }
        for (const attr of [...child.attributes]) {
          if (tag === 'a' && attr.name === 'href') continue;
          child.removeAttribute(attr.name);
        }
        if (tag === 'a') { child.setAttribute('target','_blank'); child.setAttribute('rel','noreferrer'); }
        clean(child);
      } else { child.remove(); }
    }
  }
  clean(doc.body);
  return doc.body.innerHTML;
}

// ── Nettoie l'HTML de l'éditeur avant envoi (retire les wrappers rte-img-wrap) ──
function stripPreview(html) {
  if (!html) return '';
  return html.replace(/<[^>]+>/g, ' ').replace(/\s+/g, ' ').trim().slice(0, 120);
}

function stripEditorHTML(html) {
  if (!html) return '';
  const div = document.createElement('div');
  div.innerHTML = html;
  for (const wrap of [...div.querySelectorAll('.rte-img-wrap')]) {
    const img = wrap.querySelector('img');
    if (img) wrap.replaceWith(img); else wrap.remove();
  }
  return div.innerHTML;
}

// ── Éditeur de texte riche (contenteditable) ───────────────────────────────
function RichTextEditor({ value, onChange, onImgExpand, placeholder, minHeight = 100 }) {
  const ref = useRef(null);

  useEffect(() => {
    if (ref.current && (value === '' || value === null || value === undefined)) {
      ref.current.innerHTML = '';
    }
  }, [value]);

  const exec = (cmd, val = null) => {
    // Ne redonner le focus que si l'éditeur ne l'a pas déjà
    // (focus() détruirait la sélection active)
    if (document.activeElement !== ref.current) ref.current?.focus();
    document.execCommand(cmd, false, val);
    onChange(ref.current?.innerHTML ?? '');
  };

  const insertLink = () => {
    const url = window.prompt('URL du lien :');
    if (url?.trim()) exec('createLink', url.trim());
  };

  // Coller une image → insertion inline au curseur avec overlay Agrandir/Supprimer
  const handlePaste = (e) => {
    const items = Array.from(e.clipboardData?.items ?? []);
    const imageItems = items.filter(item => item.type.startsWith('image/'));
    if (!imageItems.length) return;
    e.preventDefault();
    imageItems.forEach(item => {
      const file = item.getAsFile();
      if (!file) return;
      const reader = new FileReader();
      reader.onload = ev => {
        const dataUrl = ev.target.result;
        const ICON_EXPAND = `<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'><polyline points='15 3 21 3 21 9'/><polyline points='9 21 3 21 3 15'/><line x1='21' y1='3' x2='14' y2='10'/><line x1='3' y1='21' x2='10' y2='14'/></svg>`;
        const ICON_DEL = `<svg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='currentColor' stroke-width='2'><polyline points='3 6 5 6 21 6'/><path d='M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6'/><path d='M10 11v6'/><path d='M14 11v6'/><path d='M9 6V4h6v2'/></svg>`;
        const html = `<span class="rte-img-wrap" contenteditable="false">`
          + `<img src="${dataUrl}" class="rte-inline-img" alt="image collée" />`
          + `<span class="rte-img-overlay">`
          + `<button type="button" class="rte-img-overlay-btn" data-rte-action="expand">${ICON_EXPAND} Agrandir</button>`
          + `<button type="button" class="rte-img-overlay-btn rte-img-del" data-rte-action="remove">${ICON_DEL} Supprimer</button>`
          + `</span></span>​`;
        if (document.activeElement !== ref.current) ref.current?.focus();
        document.execCommand('insertHTML', false, html);
        onChange(ref.current?.innerHTML ?? '');
      };
      reader.readAsDataURL(file);
    });
  };

  // Délégation de clics sur les boutons Agrandir/Supprimer dans l'éditeur
  const handleEditorClick = (e) => {
    const btn = e.target.closest('[data-rte-action]');
    if (!btn) return;
    e.preventDefault();
    const action = btn.dataset.rteAction;
    const wrap = btn.closest('.rte-img-wrap');
    if (action === 'remove' && wrap) {
      wrap.remove();
      onChange(ref.current?.innerHTML ?? '');
    } else if (action === 'expand' && wrap) {
      const img = wrap.querySelector('img');
      if (img) onImgExpand?.(img.src, 'image collée');
    }
  };

  return (
    <div className="rte-wrap">
      <div className="rte-toolbar">
        <button type="button" className="rte-btn" title="Gras (Ctrl+B)"
          onMouseDown={e => { e.preventDefault(); exec('bold'); }}><b>B</b></button>
        <button type="button" className="rte-btn" title="Italique (Ctrl+I)"
          onMouseDown={e => { e.preventDefault(); exec('italic'); }}><i>I</i></button>
        <button type="button" className="rte-btn" title="Souligné (Ctrl+U)"
          onMouseDown={e => { e.preventDefault(); exec('underline'); }}><u>U</u></button>
        <button type="button" className="rte-btn" title="Barré"
          onMouseDown={e => { e.preventDefault(); exec('strikeThrough'); }}><s>S</s></button>
        <div className="rte-sep" />
        <button type="button" className="rte-btn" title="Lien"
          onMouseDown={e => { e.preventDefault(); insertLink(); }}>
          <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71"/><path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71"/></svg>
          Lien
        </button>
        <div className="rte-sep" />
        <button type="button" className="rte-btn" title="Liste à puces"
          onMouseDown={e => { e.preventDefault(); exec('insertUnorderedList'); }}>
          <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="9" y1="6" x2="21" y2="6"/><line x1="9" y1="12" x2="21" y2="12"/><line x1="9" y1="18" x2="21" y2="18"/><circle cx="3.5" cy="6" r="1"/><circle cx="3.5" cy="12" r="1"/><circle cx="3.5" cy="18" r="1"/></svg>
        </button>
        <button type="button" className="rte-btn" title="Liste numérotée"
          onMouseDown={e => { e.preventDefault(); exec('insertOrderedList'); }}>
          <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><line x1="10" y1="6" x2="21" y2="6"/><line x1="10" y1="12" x2="21" y2="12"/><line x1="10" y1="18" x2="21" y2="18"/><path d="M4 6h1v4"/><path d="M4 10h2"/><path d="M6 18H4c0-1 2-2 2-3s-1-2-2-2"/></svg>
        </button>
      </div>
      <div
        ref={ref}
        className="rte-editor"
        contentEditable
        suppressContentEditableWarning
        onInput={e => onChange(e.currentTarget.innerHTML)}
        onPaste={handlePaste}
        onClick={handleEditorClick}
        data-placeholder={placeholder}
        style={{ minHeight }}
      />
    </div>
  );
}

// ── Sélecteur de type de notification ─────────────────────────────────────
function TypeSelector({ value, onChange }) {
  return (
    <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
      {Object.entries(TYPE_META).map(([k, m]) => (
        <button
          key={k}
          type="button"
          onClick={() => onChange(k)}
          style={{
            display: 'flex', alignItems: 'center', gap: 6,
            padding: '5px 10px', borderRadius: 8, cursor: 'pointer', fontSize: 12,
            border: value === k ? `2px solid ${m.color}` : '2px solid var(--border)',
            background: value === k ? m.bg : 'transparent',
            color: value === k ? m.color : 'var(--text-muted)',
            fontWeight: value === k ? 600 : 400,
          }}
        >
          <NotifTypeAvatar type={k} size={22} />
          {m.label}
        </button>
      ))}
    </div>
  );
}

// ══════════════════════════════════════════════════════════════════════════
// COMPOSANT PRINCIPAL
// ══════════════════════════════════════════════════════════════════════════
export default function Communication() {
  const { user, isAdmin }           = useAuth();
  const [searchParams, setSearchParams] = useSearchParams();
  const navigate                    = useNavigate();

  const [tab, setTab]               = useState('support'); // 'support' | 'notifications'
  const [showBroadcastForm, setShowBroadcastForm] = useState(false);
  const [lightbox, setLightbox] = useState(null); // { url, name }
  const [editingMsg, setEditingMsg] = useState(null); // { id, body }
  const [editSaving, setEditSaving] = useState(false);
  // Tick toutes les 10s pour rafraîchir les boutons "Modifier" (fenêtre 5 min)
  const [now, setNow] = useState(Date.now());
  useEffect(() => {
    const id = setInterval(() => setNow(Date.now()), 10_000);
    return () => clearInterval(id);
  }, []);
  const [tickets, setTickets]       = useState([]);
  const [ticketSearch, setTicketSearch]   = useState('');
  const [ticketFilter, setTicketFilter]     = useState('all'); // 'all'|'open'|'resolved'
  const [notifFilter, setNotifFilter]       = useState('all'); // 'all'|'unread'
  const [selectedTicket, setSelectedTicket] = useState(null);
  const [thread, setThread]         = useState(null); // { ticket, messages }
  const [notifs, setNotifs]         = useState([]);
  const [notifPage, setNotifPage]    = useState(0);
  const [notifsTotal, setNotifsTotal] = useState(0);
  const [selectedNotif, setSelectedNotif] = useState(null);
  const [loading, setLoading]       = useState(false);

  // ── Nouveau ticket ───────────────────────────────────────────────────
  const [showCompose, setShowCompose] = useState(false);
  const [composeSubject, setComposeSubject] = useState('');
  const [composeBody, setComposeBody] = useState('');
  const [composeFiles, setComposeFiles] = useState([]);
  const [composeType, setComposeType]   = useState(null);
  const [composePages, setComposePages] = useState([]);
  const [composeSending, setComposeSending] = useState('');
  const fileInputRef = useRef(null);

  // ── Répondre ─────────────────────────────────────────────────────────
  const [replyBody, setReplyBody]   = useState('');
  const [replyFiles, setReplyFiles] = useState([]);
  const replyFileRef                = useRef(null);
  const [replySending, setReplySending] = useState(false);

  // ── Broadcast (admin) ────────────────────────────────────────────────
  const [bcType, setBcType]         = useState('announcement');
  const [bcTitle, setBcTitle]       = useState('');
  const [bcBody, setBcBody]         = useState('');
  const [bcUserId, setBcUserId]     = useState('');
  const [bcSending, setBcSending]   = useState(false);
  const [bcResult, setBcResult]     = useState(null);
  const [users, setUsers]           = useState([]);

  // ── Charger tickets ──────────────────────────────────────────────────
  const fetchTickets = useCallback(async () => {
    setLoading(true);
    try {
      const data = await api.get('/tickets');
      setTickets(data.tickets ?? []);
    } catch { /* silencieux */ }
    setLoading(false);
  }, []);

  // ── Charger notifications ────────────────────────────────────────────
  const fetchNotifs = useCallback(async () => {
    try {
      const data = await api.get('/notifications', { limit: 50 });
      const list = data.notifications ?? [];
      setNotifs(list);
      setNotifsTotal(data.total ?? 0);
      setNotifPage(0);
      // Auto-sélectionner la première notif si aucune n'est sélectionnée
      setSelectedNotif(prev => prev ?? (list.length > 0 ? list[0] : null));
    } catch { /* silencieux */ }
  }, []);

  // ── Charger thread ───────────────────────────────────────────────────
  const fetchThread = useCallback(async (id) => {
    try {
      const data = await api.get(`/tickets/${id}`);
      setThread(data);
    } catch { /* silencieux */ }
  }, []);

  // ── Charger users (admin) ────────────────────────────────────────────
  const fetchUsers = useCallback(async () => {
    if (!isAdmin) return;
    try {
      const data = await api.get('/admin/users');
      setUsers(data.users ?? data ?? []);
    } catch { /* silencieux */ }
  }, [isAdmin]);

  useEffect(() => {
    fetchTickets();
    fetchNotifs();
    if (isAdmin) fetchUsers();
  }, [fetchTickets, fetchNotifs, fetchUsers]);

  // ── Restaurer ticket depuis URL ──────────────────────────────────────
  useEffect(() => {
    const ticketId = searchParams.get('ticket');
    if (ticketId) {
      setTab('support');
      setSelectedTicket(Number(ticketId));
      fetchThread(ticketId);
      // Nettoyer le param URL sans recharger
      setSearchParams({}, { replace: true });
    }
  }, [searchParams]); // eslint-disable-line

  // ── Ouvrir un ticket ─────────────────────────────────────────────────
  const openTicket = (id) => {
    setSelectedTicket(id);
    setSelectedNotif(null);
    fetchThread(id);
    setSearchParams({ ticket: id });
    setReplyBody('');
    setReplyFiles([]);
  };

  // ── Ouvrir une notification ──────────────────────────────────────────
  const openNotif = async (n) => {
    setSelectedNotif(n);
    setSelectedTicket(null);
    setShowBroadcastForm(false);
    if (!n.read) {
      try {
        await api.patch(`/notifications/${n.id}/read`);
        setNotifs(prev => prev.map(x => x.id === n.id ? { ...x, read: 1 } : x));
        window.dispatchEvent(new CustomEvent('notif:refresh'));
      } catch { /* silencieux */ }
    }
  };

  // ── Créer ticket ─────────────────────────────────────────────────────
  const submitTicket = async (e) => {
    e.preventDefault();
    if (!composeSubject.trim() || !composeBody.replace(/<[^>]*>/g,'').trim()) return;
    setComposeSending('sending');
    try {
      const fd = new FormData();
      fd.append('subject', composeSubject.trim());
      fd.append('body', stripEditorHTML(composeBody).trim());
      if (composeType) fd.append('ticket_type', composeType);
      if (composePages.length) fd.append('ticket_pages', JSON.stringify(composePages));
      for (const f of composeFiles) fd.append('attachments', f);
      const data = await api.postForm('/tickets', fd);
      setShowCompose(false);
      setComposeSubject('');
      setComposeBody('');
      setComposeType(null);
      setComposePages([]);
      setComposeFiles([]);
      await fetchTickets();
      if (data.ticketId) openTicket(data.ticketId);
    } catch { /* silencieux */ }
    setComposeSending('');
  };

  // ── Répondre ─────────────────────────────────────────────────────────
  const submitReply = async (e) => {
    e.preventDefault();
    if (!replyBody.trim() || !thread) return;
    setReplySending(true);
    try {
      const fd = new FormData();
      fd.append('body', stripEditorHTML(replyBody).trim());
      for (const f of replyFiles) fd.append('attachments', f);
      await api.postForm(`/tickets/${thread.ticket.id}/messages`, fd);
      setReplyBody('');
      setReplyFiles([]);
      await fetchThread(thread.ticket.id);
      await fetchTickets();
    } catch { /* silencieux */ }
    setReplySending(false);
  };

  // ── Résoudre / rouvrir ───────────────────────────────────────────────
  const toggleStatus = async () => {
    if (!thread) return;
    const newStatus = thread.ticket.status === 'open' ? 'resolved' : 'open';
    try {
      await api.patch(`/tickets/${thread.ticket.id}/status`, { status: newStatus });
      await fetchThread(thread.ticket.id);
      await fetchTickets();
    } catch { /* silencieux */ }
  };

  // ── Broadcast ────────────────────────────────────────────────────────
  const submitBroadcast = async (e) => {
    e.preventDefault();
    if (!bcTitle.trim()) return;
    setBcSending(true);
    setBcResult(null);
    try {
      const payload = { type: bcType, title: bcTitle.trim(), body: bcBody.trim() || undefined };
      if (bcUserId) payload.user_id = Number(bcUserId);
      const data = await api.post('/notifications/broadcast', payload);
      setBcResult({ ok: true, msg: `Envoyé à ${data.sent} utilisateur${data.sent > 1 ? 's' : ''}` });
      setBcTitle('');
      setBcBody('');
      setBcUserId('');
      window.dispatchEvent(new CustomEvent('notif:refresh'));
      await fetchNotifs(); // rafraîchir la liste locale immédiatement
      setShowBroadcastForm(false);
    } catch {
      setBcResult({ ok: false, msg: 'Erreur lors de l\'envoi' });
    }
    setBcSending(false);
  };

  // ── Marquer toutes notifs lues ───────────────────────────────────────
  const markAllRead = async () => {
    try {
      await api.patch('/notifications/read-all');
      setNotifs(prev => prev.map(n => ({ ...n, read: 1 })));
      window.dispatchEvent(new CustomEvent('notif:refresh'));
    } catch { /* silencieux */ }
  };

  // ── Édition message (fenêtre 5 min) ────────────────────────────────────
  const EDIT_WINDOW_MS = 5 * 60 * 1000;
  const canEditMsg = (msg) =>
    msg.user_id === user?.id &&
    (now - new Date(msg.created_at + 'Z').getTime()) < EDIT_WINDOW_MS;

  const fmtRemaining = (msg) => {
    const ms = EDIT_WINDOW_MS - (now - new Date(msg.created_at + 'Z').getTime());
    if (ms <= 0) return null;
    const mins = Math.ceil(ms / 60_000);
    return mins <= 1 ? '< 1 min' : `${mins} min`;
  };

  const saveEdit = async (msg) => {
    if (!editingMsg?.body?.replace(/<[^>]*>/g, '').trim()) return;
    setEditSaving(true);
    try {
      await api.put(`/tickets/${msg.ticket_id}/messages/${msg.id}`, { body: editingMsg.body });
      // Mettre à jour localement sans recharger tout le thread
      setThread(prev => ({
        ...prev,
        messages: prev.messages.map(m =>
          m.id === msg.id ? { ...m, body: editingMsg.body, updated_at: new Date().toISOString() } : m
        ),
      }));
      setEditingMsg(null);
    } catch (e) {
      alert(e.message || 'Erreur lors de la sauvegarde');
    }
    setEditSaving(false);
  };

  const unreadCount = notifs.filter(n => !n.read).length;

  // ────────────────────────────────────────────────────────────────────
  // RENDER
  // ────────────────────────────────────────────────────────────────────
  return (
    <>
      <div className="topbar" style={{ display: 'none' }} aria-hidden />
      <div className="comm-page">
        {/* ── Topbar 3 colonnes ── */}
        <div className="comm-topbar">
          {/* Colonne 1 — Titre page */}
          <div className="comm-topbar-1">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" style={{ flexShrink: 0 }}><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
            <span>Communication</span>
          </div>
          {/* Colonne 2 — Dossier + filtres */}
          <div className="comm-topbar-2">
            <span className="comm-topbar-folder">{tab === 'support' ? 'Support' : 'Notifications'}</span>
            <div className="comm-topbar-filters">
              {tab === 'support' && (
                <>
                  {[['all','Tous'],['open','Ouverts'],['resolved','Résolus']].map(([v,l]) => (
                    <button key={v} className={`comm-topbar-filter-btn${ticketFilter === v ? ' active' : ''}`} onClick={() => setTicketFilter(v)}>{l}</button>
                  ))}
                </>
              )}
              {tab === 'notifications' && (
                <>
                  {[['all','Tous'],['unread','Non lus']].map(([v,l]) => (
                    <button key={v} className={`comm-topbar-filter-btn${notifFilter === v ? ' active' : ''}`} onClick={() => { setNotifFilter(v); setNotifPage(0); }}>{l}</button>
                  ))}
                  {unreadCount > 0 && (
                    <button className="comm-topbar-action-btn" onClick={markAllRead}>Tout marquer lu</button>
                  )}
                </>
              )}
            </div>
          </div>
          {/* Colonne 3 — Toolbar contextuelle */}
          <div className="comm-topbar-3">
            {tab === 'support' && thread && (
              <>
                <span className="comm-topbar-ticket-ref">{thread.ticket.ticket_number} — {thread.ticket.subject}</span>
                <div style={{ display: 'flex', gap: 6, marginLeft: 'auto', flexShrink: 0 }}>
                  {isAdmin && (
                    <button className="btn btn-sm" onClick={toggleStatus}>
                      {thread.ticket.status === 'open' ? 'Résoudre' : 'Rouvrir'}
                    </button>
                  )}
                </div>
              </>
            )}
            {tab === 'notifications' && selectedNotif && (
              <div style={{ display: 'flex', gap: 6, marginLeft: 'auto' }}>
                {!selectedNotif.read && (
                  <button className="btn btn-sm btn-ghost" onClick={async () => {
                    await api.patch(`/notifications/${selectedNotif.id}/read`);
                    setNotifs(prev => prev.map(n => n.id === selectedNotif.id ? { ...n, read: 1 } : n));
                    setSelectedNotif(prev => ({ ...prev, read: 1 }));
                  }}>Marquer lu</button>
                )}
                {selectedNotif.link && (
                  <button className="btn btn-sm btn-primary" onClick={() => navigate(selectedNotif.link)}>
                    Voir le détail →
                  </button>
                )}
              </div>
            )}
          </div>
        </div>
        {/* ── Corps 3 panneaux ── */}
        <div className="comm-wrap">

        {/* ── Sidebar gauche ── */}
        <aside className="comm-sidebar">
          {tab === 'support' ? (
            <button
              className="comm-compose-btn"
              onClick={() => setShowCompose(true)}
            >
              + Nouveau ticket
            </button>
          ) : isAdmin ? (
            <button
              className="comm-compose-btn"
              onClick={() => { setShowBroadcastForm(true); setSelectedNotif(null); }}
            >
              + Nouvelle notification
            </button>
          ) : null}

          <nav className="comm-nav">
            <button
              className={`comm-nav-item${tab === 'support' ? ' active' : ''}`}
              onClick={() => setTab('support')}
            >
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/>
              </svg>
              Support
              {tickets.filter(t => t.status === 'open').length > 0 && (
                <span className="comm-nav-badge">
                  {tickets.filter(t => t.status === 'open').length}
                </span>
              )}
            </button>
            <button
              className={`comm-nav-item${tab === 'notifications' ? ' active' : ''}`}
              onClick={() => {
                setTab('notifications');
                if (!selectedNotif && notifs.length > 0) setSelectedNotif(notifs[0]);
              }}
            >
              <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
                <path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/>
                <path d="M13.73 21a2 2 0 0 1-3.46 0"/>
              </svg>
              Notifications
              {unreadCount > 0 && <span className="comm-nav-badge">{unreadCount}</span>}
            </button>
          </nav>

          {/* Filtres statut tickets (sidebar) */}
          {tab === 'support' && (
            <div className="comm-sidebar-section">
              <div className="comm-sidebar-label">Tickets ouverts : {tickets.filter(t => t.status === 'open').length}</div>
              <div className="comm-sidebar-label">Tickets résolus : {tickets.filter(t => t.status === 'resolved').length}</div>
            </div>
          )}
        </aside>

        {/* ── Liste centrale ── */}
        <div className="comm-list">
          {tab === 'support' && (
            <>
              <div className="comm-list-header">
                <input
                  className="comm-search"
                  placeholder="Rechercher un ticket…"
                  style={{ flex: 1 }}
                  value={ticketSearch}
                  onChange={e => setTicketSearch(e.target.value)}
                />
              </div>
              <div className="comm-list-scroll">
                {loading && <div className="comm-empty">Chargement…</div>}
                {!loading && tickets.length === 0 && (
                  <div className="comm-empty">
                    <div style={{ fontSize: 32, marginBottom: 8 }}>💬</div>
                    <div style={{ fontWeight: 600 }}>Aucun ticket</div>
                    <div style={{ fontSize: 13, color: 'var(--text-muted)', marginTop: 4 }}>Créez votre premier ticket de support</div>
                  </div>
                )}
                {tickets
                  .filter(t => {
                    if (!ticketSearch.trim()) return true;
                    const q = ticketSearch.toLowerCase();
                    return (
                      t.subject?.toLowerCase().includes(q) ||
                      t.ticket_number?.toLowerCase().includes(q) ||
                      t.user_name?.toLowerCase().includes(q)
                    );
                  })
                  .map(t => (
                  <div
                    key={t.id}
                    className={`comm-list-row${selectedTicket === t.id ? ' selected' : ''}`}
                    onClick={() => openTicket(t.id)}
                  >
                    <UserAvatar name={t.user_name ?? t.user_email ?? user?.displayName ?? user?.email} size={38} />
                    <div className="comm-list-row-body">
                      <div className="comm-list-row-top">
                        <span className="comm-list-row-name">{t.user_name ?? 'Moi'}</span>
                        <StatusBadge status={t.status} />
                      </div>
                      <div className="comm-list-row-subject">{t.ticket_number} — {t.subject}</div>
                      <TicketChips ticketType={t.ticket_type} ticketPages={t.ticket_pages} />
                      {t.last_body && (
                        <div className="comm-list-row-preview">{stripPreview(t.last_body)}</div>
                      )}
                      <div className="comm-list-row-meta">
                        <span>{t.message_count} message{t.message_count > 1 ? 's' : ''}</span>
                        <span>{timeAgo(t.last_message_at ?? t.created_at)}</span>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            </>
          )}

          {tab === 'notifications' && (() => {
            const NOTIF_PER_PAGE = 10;
            const filteredNotifs = notifFilter === 'unread' ? notifs.filter(n => !n.read) : notifs;
            const totalNotifs    = filteredNotifs.length;
            const lastPage       = Math.max(0, Math.ceil(totalNotifs / NOTIF_PER_PAGE) - 1);
            const pagedNotifs    = filteredNotifs.slice(notifPage * NOTIF_PER_PAGE, (notifPage + 1) * NOTIF_PER_PAGE);
            const start          = totalNotifs === 0 ? 0 : notifPage * NOTIF_PER_PAGE + 1;
            const end            = Math.min((notifPage + 1) * NOTIF_PER_PAGE, totalNotifs);
            return (
              <>
                <div className="comm-list-header">
                  <span style={{ fontWeight: 600, fontSize: 14 }}>Notifications</span>
                </div>
                <div className="comm-list-scroll">
                  {notifs.length === 0 && (
                    <div className="comm-empty">
                      <div style={{ fontSize: 32, marginBottom: 8 }}>🔔</div>
                      <div style={{ fontWeight: 600 }}>Aucune notification</div>
                    </div>
                  )}
                  {pagedNotifs.map(n => (
                    <div
                      key={n.id}
                      className={`comm-list-row${selectedNotif?.id === n.id ? ' selected' : ''}${!n.read ? ' unread' : ''}`}
                      onClick={() => openNotif(n)}
                    >
                      <NotifTypeAvatar type={n.type} size={38} />
                      <div className="comm-list-row-body">
                        <div className="comm-list-row-top">
                          <span className="comm-list-row-name">{n.title}</span>
                          <span style={{
                            fontSize: 11, padding: '2px 7px', borderRadius: 99, fontWeight: 600,
                            background: (TYPE_META[n.type] ?? TYPE_META.info).bg,
                            color: (TYPE_META[n.type] ?? TYPE_META.info).color,
                          }}>
                            {(TYPE_META[n.type] ?? TYPE_META.info).label}
                          </span>
                        </div>
                        {n.body && <div className="comm-list-row-preview">{stripPreview(n.body)}</div>}
                        <div className="comm-list-row-meta">
                          <span>{timeAgo(n.created_at)}</span>
                        </div>
                      </div>
                      {!n.read && <span className="comm-unread-dot" />}
                    </div>
                  ))}
                </div>
                {totalNotifs > NOTIF_PER_PAGE && (
                  <div className="comm-list-pagination">
                    <span className="comm-list-pagination-info">
                      {start}–{end} sur {totalNotifs}
                    </span>
                    <div className="comm-list-pagination-btns">
                      <button disabled={notifPage === 0} onClick={() => setNotifPage(p => p - 1)}>
                        Précédent
                      </button>
                      <button disabled={notifPage >= lastPage} onClick={() => setNotifPage(p => p + 1)}>
                        Suivant
                      </button>
                    </div>
                  </div>
                )}
              </>
            );
          })()}
        </div>

        {/* ── Panneau détail droite ── */}
        <div className="comm-detail">

          {/* Détail ticket */}
          {tab === 'support' && thread && (
            <>
              <div className="comm-detail-header">
                <div>
                  <div style={{ fontWeight: 700, fontSize: 15, marginBottom: 2 }}>
                    {thread.ticket.ticket_number} — {thread.ticket.subject}
                  </div>
                  <div style={{ fontSize: 12, color: 'var(--text-muted)' }}>
                    Ouvert par {thread.ticket.user_name} · {fmtDate(thread.ticket.created_at)}
                  </div>
                  <TicketChips ticketType={thread.ticket.ticket_type} ticketPages={thread.ticket.ticket_pages} />
                </div>
                <div style={{ display: 'flex', gap: 8, alignItems: 'center' }}>
                  <StatusBadge status={thread.ticket.status} />
                </div>
              </div>

              <div className="comm-thread">
                {thread.messages.map(msg => (
                  <div key={msg.id} className={`comm-message${msg.is_admin ? ' from-admin' : ''}`}>
                    <UserAvatar name={msg.author_name} isAdmin={!!msg.is_admin} size={34} />
                    <div className="comm-message-content">
                      <div className="comm-message-meta">
                        <span style={{ fontWeight: 600, fontSize: 13 }}>{msg.author_name}</span>
                        {msg.is_admin && (
                          <span style={{ fontSize: 11, color: 'var(--primary)', fontWeight: 600 }}>Support</span>
                        )}
                        <span style={{ fontSize: 12, color: 'var(--text-muted)', marginLeft: 'auto', display: 'flex', alignItems: 'center', gap: 8 }}>
                          {msg.updated_at && (
                            <span style={{ fontSize: 11, color: 'var(--text-muted)', fontStyle: 'italic' }}>modifié</span>
                          )}
                          {timeAgo(msg.created_at)}
                          {canEditMsg(msg) && editingMsg?.id !== msg.id && (
                            <button
                              type="button"
                              className="comm-edit-btn"
                              title={`Modifier (encore ${fmtRemaining(msg)})`}
                              onClick={() => setEditingMsg({ id: msg.id, body: msg.body })}
                            >
                              <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"/><path d="M18.5 2.5a2.121 2.121 0 0 1 3 3L12 15l-4 1 1-4 9.5-9.5z"/></svg>
                            </button>
                          )}
                        </span>
                      </div>
                      {editingMsg?.id === msg.id ? (
                        <div className="comm-edit-block">
                          <RichTextEditor
                            value={editingMsg.body}
                            onChange={body => setEditingMsg(e => ({ ...e, body }))}
                            placeholder="Modifiez votre message…"
                            minHeight={80}
                          />
                          <div className="comm-edit-actions">
                            <span className="comm-edit-timer">
                              ⏱ {fmtRemaining(msg) ?? 'Délai expiré'}
                            </span>
                            <button type="button" className="btn btn-ghost btn-sm"
                              onClick={() => setEditingMsg(null)}>Annuler</button>
                            <button type="button" className="btn btn-primary btn-sm"
                              disabled={editSaving || !editingMsg.body.replace(/<[^>]*>/g,'').trim() || !canEditMsg(msg)}
                              onClick={() => saveEdit(msg)}>
                              {editSaving ? 'Sauvegarde…' : 'Sauvegarder'}
                            </button>
                          </div>
                        </div>
                      ) : (
                        <MessageBody
                          html={msg.body}
                          onExpand={(src, name) => setLightbox({ url: src, name })}
                          ticketNumber={thread.ticket.ticket_number}
                          msgDate={msg.created_at}
                        />
                      )}
                      {msg.attachments?.length > 0 && (() => {
                        const images = msg.attachments.filter(a => a.mime_type?.startsWith('image/'));
                        const files  = msg.attachments.filter(a => !a.mime_type?.startsWith('image/'));
                        return (
                          <>
                            {images.length > 0 && (
                              <div className="comm-attach-images">
                                {images.map(a => (
                                  <AttachmentImage
                                    key={a.id}
                                    filename={a.filename}
                                    originalName={a.original_name}
                                    size={a.size}
                                    onExpand={(url, name) => setLightbox({ url, name })}
                                    ticketNumber={thread.ticket.ticket_number}
                                    msgDate={msg.created_at}
                                  />
                                ))}
                              </div>
                            )}
                            {files.length > 0 && (
                              <div className="comm-attachments">
                                {files.map(a => (
                                  <a
                                    key={a.id}
                                    href={`/api/tickets/attachments/${a.filename}`}
                                    target="_blank"
                                    rel="noreferrer"
                                    className="comm-attach-chip"
                                  >
                                    <span style={{ fontSize: 16 }}><FileIcon mime={a.mime_type} /></span>
                                    <span className="comm-attach-name">{a.original_name}</span>
                                    <span className="comm-attach-size">{fmtSize(a.size)}</span>
                                  </a>
                                ))}
                              </div>
                            )}
                          </>
                        );
                      })()}
                    </div>
                  </div>
                ))}
              </div>

              {/* Zone réponse */}
              {(thread.ticket.status === 'open' || isAdmin) && (
                <form className="comm-reply-form" onSubmit={submitReply}>
                  <RichTextEditor
                    value={replyBody}
                    onChange={setReplyBody}
                    onImgExpand={(url, name) => setLightbox({ url, name })}
                    placeholder="Votre réponse… (Ctrl+V pour coller une image)"
                    minHeight={90}
                  />
                  {replyFiles.length > 0 && (
                    <div className="comm-files-preview">
                      {replyFiles.map((f, i) => (
                        <FileChip key={i} file={f} onRemove={() => setReplyFiles(prev => prev.filter((_, j) => j !== i))} />
                      ))}
                    </div>
                  )}
                  <div className="comm-reply-actions">
                    <button
                      type="button"
                      className="btn btn-ghost btn-sm"
                      onClick={() => replyFileRef.current?.click()}
                    >
                      <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
                      Joindre
                    </button>
                    <input
                      ref={replyFileRef}
                      type="file"
                      multiple
                      style={{ display: 'none' }}
                      onChange={e => setReplyFiles(prev => [...prev, ...Array.from(e.target.files)])}
                    />
                    <button type="submit" className="btn btn-primary btn-sm" disabled={replySending || !replyBody.replace(/<[^>]*>/g,'').trim()}>
                      {replySending ? 'Envoi…' : 'Répondre'}
                    </button>
                  </div>
                </form>
              )}
              {thread.ticket.status === 'resolved' && !isAdmin && (
                <div style={{ padding: '12px 20px', color: 'var(--text-muted)', fontSize: 13, textAlign: 'center', borderTop: '1px solid var(--border)' }}>
                  Ce ticket est résolu. Créez un nouveau ticket si besoin.
                </div>
              )}
            </>
          )}

          {/* Détail notification */}
          {tab === 'notifications' && selectedNotif && (
            <div className="comm-notif-detail">
              <div className="comm-detail-header">
                <NotifTypeAvatar type={selectedNotif.type} size={42} />
                <div style={{ flex: 1 }}>
                  <div style={{ fontWeight: 700, fontSize: 15 }}>{selectedNotif.title}</div>
                  <div style={{ fontSize: 12, color: 'var(--text-muted)', marginTop: 2 }}>
                    {timeAgo(selectedNotif.created_at)} · {(TYPE_META[selectedNotif.type] ?? TYPE_META.info).label}
                  </div>
                </div>
              </div>
              {selectedNotif.body && (
                <div style={{ padding: '20px 24px', color: 'var(--text)', lineHeight: 1.6 }}>
                  {selectedNotif.body}
                </div>
              )}
              {selectedNotif.link && (
                <div style={{ padding: '0 24px 20px' }}>
                  <button className="btn btn-primary btn-sm" onClick={() => navigate(selectedNotif.link)}>
                    Voir le détail →
                  </button>
                </div>
              )}
            </div>
          )}

          {/* Bloc broadcast admin (onglet notifications) */}
          {tab === 'notifications' && isAdmin && showBroadcastForm && (
            <div className="comm-broadcast-panel">
              <div className="comm-broadcast-title">Envoyer une notification</div>
              <form onSubmit={submitBroadcast} className="comm-broadcast-form">
                <div className="comm-form-group">
                  <label className="comm-label">Type</label>
                  <TypeSelector value={bcType} onChange={setBcType} />
                </div>
                <div className="comm-form-group">
                  <label className="comm-label">Destinataire</label>
                  <select
                    className="comm-select"
                    value={bcUserId}
                    onChange={e => setBcUserId(e.target.value)}
                  >
                    <option value="">Tous les utilisateurs</option>
                    {users.map(u => (
                      <option key={u.id} value={u.id}>{u.name} ({u.email})</option>
                    ))}
                  </select>
                </div>
                <div className="comm-form-group">
                  <label className="comm-label">Titre *</label>
                  <input
                    className="comm-input"
                    value={bcTitle}
                    onChange={e => setBcTitle(e.target.value)}
                    placeholder="Titre de la notification"
                    required
                  />
                </div>
                <div className="comm-form-group">
                  <label className="comm-label">Message (optionnel)</label>
                  <textarea
                    className="comm-reply-textarea"
                    value={bcBody}
                    onChange={e => setBcBody(e.target.value)}
                    placeholder="Corps du message…"
                    rows={3}
                  />
                </div>
                {bcResult && (
                  <div style={{ fontSize: 13, color: bcResult.ok ? 'var(--success)' : 'var(--danger)', marginBottom: 8 }}>
                    {bcResult.msg}
                  </div>
                )}
                <button type="submit" className="btn btn-primary btn-sm" disabled={bcSending || !bcTitle.trim()}>
                  {bcSending ? 'Envoi…' : 'Envoyer'}
                </button>
              </form>
            </div>
          )}

          {/* Placeholder vide */}
          {tab === 'support' && !thread && (
            <div className="comm-detail-empty">
              <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="var(--border)" strokeWidth="1.5"><path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"/></svg>
              <div style={{ marginTop: 12, color: 'var(--text-muted)', fontSize: 14 }}>Sélectionnez un ticket</div>
            </div>
          )}
          {tab === 'notifications' && !selectedNotif && !showBroadcastForm && (
            <div className="comm-detail-empty">
              <svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="var(--border)" strokeWidth="1.5"><path d="M18 8A6 6 0 0 0 6 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 0 1-3.46 0"/></svg>
              <div style={{ marginTop: 12, color: 'var(--text-muted)', fontSize: 14 }}>Sélectionnez une notification</div>
            </div>
          )}
        </div>

      </div>{/* end comm-wrap */}
      </div>{/* end comm-page */}

      {/* ── Lightbox image ── */}
      {lightbox && (
        <div className="comm-lightbox" onClick={() => setLightbox(null)}>
          <button className="comm-lightbox-close" onClick={() => setLightbox(null)}>×</button>
          <button
            className="comm-lightbox-download"
            onClick={e => {
              e.stopPropagation();
              const a = document.createElement('a');
              a.href = lightbox.url; a.download = lightbox.name; a.click();
            }}
          >
            <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
            Télécharger
          </button>
          <img
            src={lightbox.url}
            alt={lightbox.name}
            className="comm-lightbox-img"
            onClick={e => e.stopPropagation()}
          />
        </div>
      )}

      {/* ── Modale nouveau ticket ── */}
      {showCompose && (
        <div className="comm-modal-backdrop" onClick={() => setShowCompose(false)}>
          <div className="comm-modal" onClick={e => e.stopPropagation()}>
            <div className="comm-modal-header">
              <span style={{ fontWeight: 700, fontSize: 15 }}>Nouveau ticket de support</span>
              <button className="comm-modal-close" onClick={() => setShowCompose(false)}>×</button>
            </div>
            <form onSubmit={submitTicket} className="comm-modal-body">
              <div className="comm-form-group">
                <label className="comm-label">Sujet *</label>
                <input
                  className="comm-input"
                  value={composeSubject}
                  onChange={e => setComposeSubject(e.target.value)}
                  placeholder="Décrivez votre problème en quelques mots"
                  required
                  autoFocus
                />
              </div>
              <div className="comm-form-group">
                <label className="comm-label">Message *</label>
                <RichTextEditor
                  value={composeBody}
                  onChange={setComposeBody}
                  onImgExpand={(url, name) => setLightbox({ url, name })}
                  placeholder="Décrivez votre problème en détail… (Ctrl+V pour coller une image)"
                  minHeight={150}
                />
              </div>
              {composeFiles.length > 0 && (
                <div className="comm-files-preview">
                  {composeFiles.map((f, i) => (
                    <FileChip key={i} file={f} onRemove={() => setComposeFiles(prev => prev.filter((_, j) => j !== i))} />
                  ))}
                </div>
              )}
              <div className="comm-reply-actions">
                <button
                  type="button"
                  className="btn btn-ghost btn-sm"
                  onClick={() => fileInputRef.current?.click()}
                >
                  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"/></svg>
                  Joindre un fichier
                </button>
                <input
                  ref={fileInputRef}
                  type="file"
                  multiple
                  style={{ display: 'none' }}
                  onChange={e => setComposeFiles(prev => [...prev, ...Array.from(e.target.files)])}
                />
                <div style={{ display: 'flex', gap: 6, alignItems: 'center', marginLeft: 'auto' }}>
                  <TagSelect label="Type" options={TICKET_TYPES} value={composeType} onChange={setComposeType} />
                  <TagSelect label="Page" options={TICKET_PAGES} value={composePages} onChange={setComposePages} multi />
                  <button type="submit" className="btn btn-primary btn-sm" disabled={composeSending === 'sending'}>
                    {composeSending === 'sending' ? 'Création…' : 'Créer le ticket'}
                  </button>
                </div>
              </div>
            </form>
          </div>
        </div>
      )}
    </>
  );
}
