// FOOTPASS Messenger v2 · Appels V2 (hub restructuré)
// Pas de WhatsApp à Footpass : tous les appels du foot business passent ici.
//  • Bandeau « prochain appel » imminent · toujours visible, Rejoindre en 1 tap
//  • Segments Journal · Salons · À venir · une chose à la fois, rien de perdu
//  • JOURNAL (défaut) · le cœur : historique filtrable, fiche appel par ligne
//  • SALONS · groupes habituels dépliables : agenda commun, résumés, rappels
//  • PASSI · réduit à un bouton d'en-tête (pastille quand actif)
const { useState: useStateC2, useRef: useRefC2, useEffect: useEffectC2 } = React;

// Teinte du rôle · reprend le code couleur des Messages (m4RoleColor)
const CL2_FAM_BY_ROLE = { Joueur: 'joueur', Agent: 'agent', Juriste: 'reseau' };
function cl2Color(p) {
  const fam = (p && (p.fam || CL2_FAM_BY_ROLE[p.role])) || 'staff';
  return window.m4RoleColor ? window.m4RoleColor(fam) : '#8E8E96';
}
const cl2First = (pid) => { const p = FP_PEERS[pid]; return p ? p.name.split(' ')[0] : ''; };

// ── Salons d'appel · groupes habituels, avec suivi ──
const CL2_ROOMS = [
  { id: 'r1', name: 'Dossier Lucas × Real', pids: ['david', 'sira', 'lucas'], calls: 5,
    last: 'Hier · 31:08', next: "Aujourd'hui · 17:30", video: true,
    recap: [
      'Le Real confirme l\u2019offre : 120 M€ + bonus, contrat 6 ans',
      'Visite médicale à caler avant la signature (mar. 24)',
      'Sira relit l\u2019avenant v2.1 · annotations vendredi',
    ],
    slots: [
      { when: "Auj. · 17:30", booked: true },
      { when: 'Demain · 09:30', d: 20, dow: 'Ven', time: '09:30' },
      { when: 'Lun. · 14:00', d: 23, dow: 'Lun', time: '14:00' },
    ],
    hist: [
      { dir: 'out', video: true, when: 'Hier · 15:00', dur: '31:08' },
      { dir: 'in', video: false, when: '12 mai · 09:30', dur: '02:44', sum: 'Point rapide · disponibilités de Lucas' },
      { dir: 'out', video: true, when: '5 mai · 11:00', dur: '18:22', sum: 'Cadrage initial du dossier de transfert' },
    ] },
  { id: 'r2', name: 'Classico Sport · interne', pids: ['amadou', 'sira'], calls: 12,
    last: 'Lundi · 12:20', next: 'Vendredi · 09:30 · hebdo',
    recap: [
      'Budget mercato été validé par la direction',
      'Sira prépare les autorisations de co-représentation',
      'Point RH reporté à la prochaine réunion hebdo',
    ],
    slots: [
      { when: 'Ven. · 09:30 · hebdo', booked: true },
      { when: 'Mar. · 10:00', d: 24, dow: 'Mar', time: '10:00' },
      { when: 'Mer. · 15:30', d: 25, dow: 'Mer', time: '15:30' },
    ],
    hist: [
      { dir: 'out', video: false, when: 'Lundi · 09:30', dur: '12:20' },
      { dir: 'out', video: false, when: '6 juin · 09:30', dur: '22:05', sum: 'Revue des dossiers en cours' },
    ] },
  { id: 'r3', name: 'FC Nantes · U19', pids: ['karim', 'mathis'], calls: 3,
    last: '28 mai · 09:12', next: null,
    recap: [
      'Protocole médical validé avant la visite de Mathis',
      'Rapport IRM du genou attendu avant jeudi',
      'Convocation officielle envoyée mercredi',
    ],
    slots: [
      { when: 'Jeu. · 10:30', d: 19, dow: 'Jeu', time: '10:30' },
      { when: 'Ven. · 09:00', d: 20, dow: 'Ven', time: '09:00' },
    ],
    hist: [
      { dir: 'in', video: false, when: '28 mai · 09:12', dur: '09:12' },
    ] },
];

// ── Journal · ordre chronologique. hist = tous les appels avec ce contact. ──
const CL2_LOGS = [
  { id: 1, pid: 'mufc', dir: 'missed', video: false, when: '11:26', sect: 'today',
    hist: [
      { dir: 'missed', video: false, when: "Aujourd'hui · 11:26" },
      { dir: 'out', video: false, when: '2 juin · 17:40', dur: '08:12' },
    ] },
  { id: 2, pid: 'karim', dir: 'in', video: true, when: 'Hier', dur: '08:47', sect: 'week',
    hist: [
      { dir: 'in', video: true, when: 'Hier · 18:05', dur: '08:47' },
      { dir: 'out', video: false, when: '3 juin · 10:15', dur: '12:03' },
      { dir: 'missed', video: false, when: '28 mai · 09:42' },
    ] },
  { id: 3, pid: 'amadou', dir: 'missed', video: false, when: 'Hier', sect: 'week',
    hist: [
      { dir: 'missed', video: false, when: 'Hier · 21:12' },
      { dir: 'out', video: false, when: 'Lundi · 14:30', dur: '04:40' },
      { dir: 'in', video: false, when: '11 juin · 19:08', dur: '22:18' },
    ] },
  { id: 4, pid: 'sira', dir: 'out', video: false, when: 'Lundi', dur: '03:05', sect: 'week',
    hist: [
      { dir: 'out', video: false, when: 'Lundi · 16:20', dur: '03:05' },
      { dir: 'in', video: false, when: '5 juin · 11:02', dur: '07:12' },
    ] },
  { id: 5, pid: 'david', dir: 'out', video: true, when: '28 mai', dur: '31:08', sect: 'older',
    hist: [
      { dir: 'out', video: true, when: '28 mai · 15:00', dur: '31:08' },
      { dir: 'out', video: false, when: '12 mai · 09:30', dur: '02:44' },
    ] },
  { id: 6, pid: 'lucas', dir: 'out', video: false, when: '12 mars', dur: '24:12', sect: 'older',
    hist: [
      { dir: 'out', video: false, when: '12 mars · 09:20', dur: '24:12' },
      { dir: 'in', video: true, when: '2 mars · 20:11', dur: '15:20' },
      { dir: 'missed', video: false, when: '27 févr · 08:05' },
    ] },
  { id: 7, pid: 'marco', dir: 'in', video: false, when: '14 févr', dur: '12:44', sect: 'older',
    hist: [
      { dir: 'in', video: false, when: '14 févr · 11:02', dur: '12:44' },
    ] },
  { id: 8, pid: 'mathis', dir: 'in', video: false, when: '17 déc', dur: '06:51', sect: 'older',
    hist: [
      { dir: 'in', video: false, when: '17 déc · 17:45', dur: '06:51' },
      { dir: 'out', video: false, when: '10 déc · 13:12', dur: '03:15' },
    ] },
  { id: 9, pid: 'david', dir: 'in', video: false, when: '09:14', dur: '06:02', sect: 'today',
    hist: [
      { dir: 'in', video: false, when: "Aujourd'hui · 09:14", dur: '06:02' },
      { dir: 'out', video: true, when: '28 mai · 15:00', dur: '31:08' },
    ] },
  { id: 10, pid: 'mathis', dir: 'in', video: true, when: 'Mercredi', dur: '14:21', sect: 'week',
    hist: [
      { dir: 'in', video: true, when: 'Mercredi · 19:04', dur: '14:21' },
    ] },
  { id: 11, pid: 'lucas', dir: 'out', video: false, when: 'Mardi', dur: '09:37', sect: 'week',
    hist: [
      { dir: 'out', video: false, when: 'Mardi · 14:22', dur: '09:37' },
      { dir: 'out', video: false, when: '12 mars · 09:20', dur: '24:12' },
    ] },
  { id: 12, pid: 'mufc', dir: 'out', video: true, when: 'Lundi', dur: '22:48', sect: 'week',
    hist: [
      { dir: 'out', video: true, when: 'Lundi · 18:40', dur: '22:48' },
    ] },
  { id: 13, pid: 'karim', dir: 'missed', video: false, when: '26 mai', sect: 'older',
    hist: [
      { dir: 'missed', video: false, when: '26 mai · 08:31' },
      { dir: 'out', video: false, when: '26 mai · 10:02', dur: '05:44' },
    ] },
  { id: 14, pid: 'amadou', dir: 'out', video: false, when: '21 mai', dur: '11:19', sect: 'older',
    hist: [
      { dir: 'out', video: false, when: '21 mai · 09:12', dur: '11:19' },
    ] },
  { id: 15, pid: 'sira', dir: 'in', video: false, when: '15 mai', dur: '04:47', sect: 'older',
    hist: [
      { dir: 'in', video: false, when: '15 mai · 16:55', dur: '04:47' },
    ] },
  { id: 16, pid: 'marco', dir: 'out', video: true, when: '2 mai', dur: '27:30', sect: 'older',
    hist: [
      { dir: 'out', video: true, when: '2 mai · 11:30', dur: '27:30' },
      { dir: 'in', video: false, when: '14 févr · 11:02', dur: '12:44' },
    ] },
  { id: 17, pid: 'david', dir: 'in', video: false, when: '25 avr', dur: '08:26', sect: 'older',
    hist: [
      { dir: 'in', video: false, when: '25 avr · 10:44', dur: '08:26' },
    ] },
  { id: 18, pid: 'mathis', dir: 'out', video: false, when: '9 avr', dur: '02:58', sect: 'older',
    hist: [
      { dir: 'out', video: false, when: '9 avr · 08:15', dur: '02:58' },
    ] },
];
const CL2_SECTS = [['today', "Aujourd'hui"], ['week', 'Cette semaine'], ['older', 'Plus ancien']];

const Cl2Bubble = ({ s = 18 }) => (
  <svg width={s} height={s} viewBox="0 0 22 22" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
    <path d="M19 10.8a7.6 7.6 0 0 1-11 6.8L4 19l1.4-3.8A7.6 7.6 0 1 1 19 10.8z"></path>
  </svg>
);

// ── Pile d'avatars (salons) ──
function Cl2Stack({ pids, size = 32 }) {
  return (
    <span className="cl2-stack" style={{ width: size + (pids.length - 1) * (size - 10), height: size }}>
      {pids.slice(0, 3).map((pid, i) => {
        const p = FP_PEERS[pid];
        return (
          <span key={pid} className="cl2-stack-it" style={{ left: i * (size - 10), zIndex: 3 - i }}>
            <FPAvatar size={size} initials={p.initials} src={p.photo} />
          </span>
        );
      })}
    </span>
  );
}

// ── Ligne du journal ──
function Cl2Row({ item, onOpen, onVoice, onVideo }) {
  const p = FP_PEERS[item.pid];
  if (!p) return null;
  const missed = item.dir === 'missed';
  const kindLabel = missed ? 'Manqué' : (item.video ? 'Vidéo' : 'Vocal');
  return (
    <div className="cl2-row" onClick={() => onOpen(item)} role="button" tabIndex={0}
      onKeyDown={(e) => { if (e.key === 'Enter') onOpen(item); }}>
      <span className="cl2-avwrap">
        <FPAvatar size={44} initials={p.initials} src={p.photo} />
        <span className={`cl2-dir ${missed ? 'missed' : ''}`}>
          {item.dir === 'out' ? <HMI.ArrowUR s={9} /> : <HMI.ArrowDL s={9} />}
        </span>
      </span>
      <span className="cl2-main">
        <b className={`cl2-name ${missed ? 'missed' : ''}`}>{window.fpUpperLast(p.name)}</b>
        <span className="cl2-id">
          <span className="role" style={{ color: cl2Color(p) }}>{p.role}</span>
          {p.org ? <span> · {p.org}</span> : null}
        </span>
        <span className="cl2-meta">
          <span className="cl2-kindic">{item.video ? <HMI.VideoCam s={13} /> : <HMI.Phone s={11} />}</span>
          <span className={missed ? 'miss' : ''}>{kindLabel}</span>
          {item.dur ? <span>· {item.dur}</span> : null}
          <span>· {item.when}</span>
        </span>
      </span>
      <button className="hm-iconbtn plain" aria-label={item.video ? 'Rappeler en vidéo' : 'Rappeler'}
        onClick={(e) => { e.stopPropagation(); (item.video ? onVideo : onVoice)(item.pid); }}>
        {item.video ? <HMI.VideoCam s={18} /> : <HMI.Phone s={17} />}
      </button>
    </div>
  );
}

// ── Fiche appel · identité, actions, historique ──
function Cl2Sheet({ item, onClose, onVoice, onVideo, onOpenChat }) {
  const p = FP_PEERS[item.pid];
  const dirLabel = (h) => h.dir === 'missed'
    ? 'Appel manqué'
    : (h.video ? 'Vidéo' : 'Vocal') + (h.dir === 'in' ? ' · entrant' : ' · sortant');
  return (
    <PgSheet onClose={onClose}>
      <div className="cl2-peer" data-screen-label="Fiche appel">
        <FPAvatar size={56} initials={p.initials} src={p.photo} />
        <div className="cl2-peer-name">{window.fpUpperLast(p.name)}</div>
        <div className="cl2-peer-id">
          <span style={{ color: cl2Color(p) }}>{p.role}</span>{p.org ? ' · ' + p.org : ''}
        </div>
        <div className="cl2-peer-pass"><FPIcon.Shield s={10} /> Pass ID vérifié</div>
      </div>
      <div className="cl2-acts">
        <button className="cl2-act" onClick={() => { onClose(); onOpenChat(item.pid); }}>
          <span className="cl2-act-ic"><Cl2Bubble /></span><em>Message</em>
        </button>
        <button className="cl2-act pri" onClick={() => { onClose(); onVoice(item.pid); }}>
          <span className="cl2-act-ic"><FPIcon.Phone s={18} /></span><em>Appel</em>
        </button>
        <button className="cl2-act" onClick={() => { onClose(); onVideo(item.pid); }}>
          <span className="cl2-act-ic"><FPIcon.Video s={20} /></span><em>Vidéo</em>
        </button>
      </div>
      <div className="hm-sechead cl2-histhead"><span>Historique</span></div>
      <div className="hm-cardlist">
        {item.hist.map((h, i) => (
          <div key={i} className="cl2-histrow">
            <span className={`cl2-hdir ${h.dir === 'missed' ? 'missed' : ''}`}>
              {h.dir === 'out' ? <HMI.ArrowUR s={11} /> : <HMI.ArrowDL s={11} />}
            </span>
            <span className="cl2-hmain">
              <b className={h.dir === 'missed' ? 'missed' : ''}>{dirLabel(h)}</b>
              <em>{h.when}</em>
            </span>
            <span className="cl2-hdur">{h.dur || '-'}</span>
          </div>
        ))}
      </div>
      <div className="hm-listfoot"><FPIcon.Shield s={10} /> Appels chiffrés de bout en bout · identité vérifiée Pass ID</div>
    </PgSheet>
  );
}

// ── Petite coche (créneau proposé) ──
const Cl2Check = ({ s = 9 }) => (
  <svg width={s} height={s} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3.4" strokeLinecap="round" strokeLinejoin="round"><path d="M4.5 12.5l5 5L19.5 7"></path></svg>
);

// Roues façon alarme iOS · jours sur 6 mois, heures, minutes par 5
const CL2_W_DAYS = (() => {
  const dows = ['Ven', 'Sam', 'Dim', 'Lun', 'Mar', 'Mer', 'Jeu'];
  const months = [['juin', 30, 6], ['juil.', 31, 7], ['août', 31, 8], ['sept.', 30, 9], ['oct.', 31, 10], ['nov.', 30, 11], ['déc.', 31, 12]];
  const out = []; let mi = 0; let d = 20; let i = 0;
  while (mi < months.length) {
    out.push({ label: dows[i % 7] + '. ' + d + ' ' + months[mi][0], dow: dows[i % 7], d, m: months[mi][2] });
    i++; d++;
    if (d > months[mi][1]) { d = 1; mi++; }
  }
  return out;
})();
const CL2_W_HOURS = Array.from({ length: 24 }, (_, i) => String(i).padStart(2, '0'));
const CL2_W_MINS = Array.from({ length: 12 }, (_, i) => String(i * 5).padStart(2, '0'));
const CL2_WIH = 32; // hauteur d'un item de roue

function Cl2Wheel({ items, value, onChange, flex }) {
  const ref = useRefC2(null);
  useEffectC2(() => { if (ref.current) ref.current.scrollTop = value * CL2_WIH; }, []);
  const onScroll = (e) => {
    const i = Math.max(0, Math.min(items.length - 1, Math.round(e.target.scrollTop / CL2_WIH)));
    if (i !== value) onChange(i);
  };
  return (
    <div className="cl2-wheel" style={{ flex: flex || 1 }} ref={ref} onScroll={onScroll}>
      <div className="cl2-wheel-pad"></div>
      {items.map((it, i) => <div key={i} className={`cl2-wheel-it ${i === value ? 'on' : ''}`}>{it}</div>)}
      <div className="cl2-wheel-pad"></div>
    </div>
  );
}


// ── Salon d'appel · carte rétractable : programmé · proposer une date · dernier appel · notes privées ──
function Cl2RoomCard({ room, open, onToggle, onVoice, onVideo, onRoomCall, onOpenPassi, onSchedule, onUnschedule, proposed, showToast }) {
  const lead = room.pids[0];
  const [custom, setCustomRaw] = useStateC2(false);
  const [moveOpen, setMoveOpenRaw] = useStateC2(false);
  const setCustom = (v) => { setCustomRaw(v); if (v) setMoveOpenRaw(false); };
  const setMoveOpen = (v) => { setMoveOpenRaw(v); if (v) setCustomRaw(false); };
  const [bookedNew, setBookedNew] = useStateC2(null);
  const [bookedGone, setBookedGone] = useStateC2(false);
  const [wDay, setWDay] = useStateC2(0);
  const [wHour, setWHour] = useStateC2(9);
  const [wMin, setWMin] = useStateC2(6); // 09:30
  // Appel de salon = appel de groupe (tous les membres), pas un 1:1 avec le premier membre
  const call = (video) => (onRoomCall ? onRoomCall(room) : (video ? onVideo : onVoice)(lead));
  const booked = bookedGone ? null : (bookedNew || room.slots.find((s) => s.booked));
  const headNext = bookedGone ? null : (bookedNew ? bookedNew.when : room.next);
  const prop = proposed ? proposed[room.id] : null;
  const NB = window.NotesBlockV2;
  return (
    <div className={`cl2-roomc ${open ? 'open' : ''}`} data-screen-label="Salon d'appel">
      <div className="cl2-row room" onClick={onToggle} role="button" tabIndex={0} aria-expanded={open}
        onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); onToggle(); } }}>
        <Cl2Stack pids={room.pids} />
        <span className="cl2-main">
          <b className="cl2-name">{room.name}</b>
          <span className="cl2-meta"><span>{room.calls} appels · dernier {room.last}</span></span>
          {headNext ? <span className="cl2-meta next"><HMI.Calendar s={11} /><span>{headNext}</span></span> : null}
        </span>
        <button className="hm-iconbtn plain" aria-label={room.video ? 'Appeler le salon en vidéo' : 'Appeler le salon'}
          onClick={(e) => { e.stopPropagation(); call(room.video); }}>
          {room.video ? <HMI.VideoCam s={18} /> : <HMI.Phone s={17} />}
        </button>
        <span className="cl2-rchev" aria-hidden="true"><HMI.Chevron s={15} /></span>
      </div>

      <div className="cl2-rexp-wrap">
        <div className="cl2-rexp">
          <div className="cl2-rexp-in">
            {booked ? (
              <div className="cl2-rx">
                <span className="cl2-rx-ic"><HMI.Calendar s={13} /></span>
                <span className="cl2-rx-txt" role="button" tabIndex={0} aria-expanded={moveOpen}
                  title="Décaler ou annuler l'appel"
                  onClick={() => setMoveOpen(!moveOpen)}
                  onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); setMoveOpen(!moveOpen); } }}>
                  <b>{booked.when}</b><em>Appel programmé · toucher pour décaler</em>
                </span>
                <button className="cl2-join sm" onClick={() => call(room.video)}>Rejoindre</button>
              </div>
            ) : null}
            {booked && moveOpen ? (
              <div className="cl2-cust">
                <div className="cl2-cust-lbl">Décaler l&rsquo;appel</div>
                <div className="cl2-wheels" aria-label="Sélecteur de date et heure">
                  <div className="cl2-wheel-hl" aria-hidden="true"></div>
                  <Cl2Wheel items={CL2_W_DAYS.map((d) => d.label)} value={wDay} onChange={setWDay} flex={1.6} />
                  <Cl2Wheel items={CL2_W_HOURS} value={wHour} onChange={setWHour} />
                  <Cl2Wheel items={CL2_W_MINS} value={wMin} onChange={setWMin} />
                </div>
                <div className="cl2-cust-acts split">
                  <button className="cl2-cust-cancel danger"
                    onClick={() => { const was = booked.when; setBookedGone(true); setMoveOpen(false); if (showToast) showToast('Appel annulé · ' + was); }}>
                    Annuler l&rsquo;appel
                  </button>
                  <button className="cl2-rx-act fill"
                    onClick={() => {
                      const day = CL2_W_DAYS[wDay];
                      const time = CL2_W_HOURS[wHour] + ':' + CL2_W_MINS[wMin];
                      const when = `${day.label} · ${time}`;
                      setBookedNew({ when, booked: true });
                      setBookedGone(false);
                      setMoveOpen(false);
                      if (showToast) showToast('Appel décalé · ' + when + ' · membres prévenus');
                    }}>
                    Décaler · {CL2_W_DAYS[wDay].label} · {CL2_W_HOURS[wHour]}:{CL2_W_MINS[wMin]}
                  </button>
                </div>
              </div>
            ) : null}
            {prop ? (
              <button className="cl2-rx" title="Recliquer pour annuler la proposition"
                onClick={() => onUnschedule(room)}>
                <span className="cl2-rx-ic"><Cl2Check s={10} /></span>
                <span className="cl2-rx-txt">
                  <b>{prop.slot.when}</b>
                  <em>Proposé aux membres · à confirmer · toucher pour annuler</em>
                </span>
              </button>
            ) : (
              <button className="cl2-rx" aria-expanded={custom} onClick={() => setCustom(!custom)}>
                <span className="cl2-rx-ic dim"><FPIcon.Plus c="currentColor" /></span>
                <span className="cl2-rx-txt">
                  <b>Proposer une date</b>
                  <em>Choisir la date et l&rsquo;heure du prochain appel</em>
                </span>
              </button>
            )}
            {custom && !prop ? (
              <div className="cl2-cust">
                <div className="cl2-cust-lbl">Choisir la date et l&rsquo;heure</div>
                <div className="cl2-wheels" aria-label="Sélecteur de date et heure">
                  <div className="cl2-wheel-hl" aria-hidden="true"></div>
                  <Cl2Wheel items={CL2_W_DAYS.map((d) => d.label)} value={wDay} onChange={setWDay} flex={1.6} />
                  <Cl2Wheel items={CL2_W_HOURS} value={wHour} onChange={setWHour} />
                  <Cl2Wheel items={CL2_W_MINS} value={wMin} onChange={setWMin} />
                </div>
                <div className="cl2-cust-acts">
                  <button className="cl2-cust-cancel" onClick={() => setCustom(false)}>Annuler</button>
                  <button className="cl2-rx-act fill"
                    onClick={() => {
                      const day = CL2_W_DAYS[wDay];
                      const time = CL2_W_HOURS[wHour] + ':' + CL2_W_MINS[wMin];
                      onSchedule(room, { when: `${day.label} · ${time}`, d: day.d, m: day.m, dow: day.dow, time });
                      setCustom(false);
                    }}>
                    Proposer · {CL2_W_DAYS[wDay].label} · {CL2_W_HOURS[wHour]}:{CL2_W_MINS[wMin]}
                  </button>
                </div>
              </div>
            ) : null}
            <button className="cl2-rx teaser" onClick={() => onOpenPassi({ kind: 'room-recap', roomId: room.id })}>
              <span className="cl2-rx-ic dim"><PassiIcon s={12} /></span>
              <span className="cl2-rx-txt">
                <b>Vos appels, résumés par Passi</b>
                <em>Récaps et tâches automatiques après chaque appel</em>
              </span>
              <span className="cl2-soon">Bientôt</span>
            </button>
            {NB ? (
              <div className="cl2-rexp-notes">
                <NB ev={{ id: room.id, who: lead, whos: room.pids, theme: 'admin', kindLabel: 'Ce salon' }} showToast={showToast} dense />
              </div>
            ) : null}
          </div>
        </div>
      </div>
    </div>
  );
}

// ── ÉCRAN APPELS V2 ──
function HMCallsV2({ lang, onSheet, onVoice, onVideo, onRoomCall, onOpenChat, onOpenSub, showToast, events, onAddEvent, onRemoveEvent }) {
  const roomCall = (room) => (onRoomCall ? onRoomCall(room) : (room.video ? onVideo : onVoice)(room.pids[0]));
  const t = (k) => hmT(lang, k);
  const [flt, setFlt] = useStateC2('all');
  const [showSearch, setShowSearch] = useStateC2(false);
  const [q, setQ] = useStateC2('');
  const [detail, setDetail] = useStateC2(null);
  const [openRoom, setOpenRoom] = useStateC2(null);
  const [seg, setSegRaw] = useStateC2(() => { try { return localStorage.getItem('fp_cl2_seg') || 'journal'; } catch (e) { return 'journal'; } });
  const setSeg = (k) => { setSegRaw(k); try { localStorage.setItem('fp_cl2_seg', k); } catch (e) { /* ignore */ } };
  const [tight, setTight] = useStateC2(false);
  const [dockHidden, setDockHidden] = useStateC2(false);
  const scrollRef = useRefC2(null);
  const lastYRef = useRefC2(0);
  const inputRef = useRefC2(null);
  // Header resserré dès que la liste défile · dock masqué en descendant, revient en remontant
  const onScrollList = (e) => {
    const y = e.target.scrollTop;
    const s = y > 6;
    setTight((prev) => (prev === s ? prev : s));
    const last = lastYRef.current;
    if (y <= 6) setDockHidden(false);
    else if (y - last > 3) setDockHidden(true);
    else if (last - y > 3) setDockHidden(false);
    lastYRef.current = y;
  };
  const trans = { transition: 'all 0.22s ease' };
  const tSty = (compact) => (tight ? { ...compact, ...trans } : trans);
  // Changement de segment → retour en haut de liste
  useEffectC2(() => {
    if (scrollRef.current) scrollRef.current.scrollTop = 0;
    setTight(false);
    setDockHidden(false);
    lastYRef.current = 0;
  }, [seg]);
  useEffectC2(() => { if (showSearch && inputRef.current) inputRef.current.focus(); }, [showSearch]);

  const norm = (s) => s.toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, '');
  const missedN = CL2_LOGS.filter((l) => l.dir === 'missed').length;
  const pass = (l) => {
    if (flt === 'missed' && l.dir !== 'missed') return false;
    if (flt === 'voice' && l.video) return false;
    if (flt === 'video' && !l.video) return false;
    if (q.trim()) {
      const p = FP_PEERS[l.pid] || {};
      if (!norm(`${p.name || ''} ${p.role || ''} ${p.org || ''}`).includes(norm(q.trim()))) return false;
    }
    return true;
  };
  const visible = CL2_LOGS.filter(pass);
  // Appel imminent (aujourd'hui) · bandeau « Rejoindre » au-dessus des segments
  const todayRoom = CL2_ROOMS.find((r) => r.next && /aujourd/i.test(r.next)) || null;

  // À venir = prochains appels des salons + appels programmés du VRAI agenda
  const agendaCalls = (events || window.FP_EVENTS || [])
    .filter((e) => String(e.id).charAt(0) === 'e' && /appel/i.test(e.place || '') && e.status !== 'done')
    .sort((a, b) => ((a.m || 6) - (b.m || 6)) || ((a.d || 0) - (b.d || 0)) || String(a.time || '').localeCompare(String(b.time || '')))
    .slice(0, 4);
  // Passi · pas encore disponible (bientôt)
  const openPassi = () => {
    if (showToast) showToast('Passi arrive bientôt : récaps et tâches automatiques après vos appels');
  };
  // Proposer un créneau → ajoute au VRAI agenda (statut à confirmer) · une proposition par salon
  const [proposed, setProposed] = useStateC2({});
  const scheduleSlot = (room, slot) => {
    const evId = 'e-salon-' + Date.now();
    setProposed((x) => ({ ...x, [room.id]: { slot, evId } }));
    if (onAddEvent) onAddEvent({
      id: evId,
      d: slot.d, m: slot.m, dow: slot.dow, time: slot.time, dur: '30 min',
      title: (room.video ? 'Visio · ' : 'Appel · ') + room.name,
      place: 'Appel vidéo FOOTPASS', who: room.pids[0], whos: room.pids,
      theme: 'admin', status: 'pending', chat: room.pids[0],
    });
    if (showToast) showToast('Créneau proposé aux membres · ' + slot.when);
  };
  // Recliquer la proposition → annulation (retirée de l'agenda aussi)
  const unscheduleSlot = (room) => {
    const cur = proposed[room.id];
    setProposed((x) => { const n = { ...x }; delete n[room.id]; return n; });
    if (cur && cur.evId && onRemoveEvent) onRemoveEvent(cur.evId);
    if (showToast && cur) showToast('Proposition annulée · ' + cur.slot.when);
  };

  const chips = [
    { k: 'all', label: 'Tous' },
    { k: 'missed', label: 'Manqués', n: missedN },
  ];
  const segs = [
    { k: 'journal', label: 'Journal', n: missedN },
    { k: 'salons', label: 'Salons' },
    { k: 'upcoming', label: 'À venir', n: (todayRoom ? 1 : 0) + agendaCalls.length, ok: true },
  ];

  return (
    <div className="hm-page" data-screen-label="Appels">
      <div className="hm-pagehead">
        <h1 className="hm-h1">{t('calls')}</h1>
        <div className="cl2-headacts">
          <button className="hm-iconbtn" aria-label="Passi · bientôt disponible" onClick={() => openPassi(null)}>
            <PassiIcon s={14} />
          </button>
          <button className={`hm-iconbtn ${showSearch ? 'on' : ''}`} aria-label="Rechercher"
            onClick={() => { if (!showSearch) setSeg('journal'); setShowSearch((s) => { if (s) setQ(''); return !s; }); }}>
            <HMI.Search s={17} />
          </button>
          <button className="hm-iconbtn" onClick={() => onSheet({ kind: 'newcall' })} aria-label="Nouvel appel">
            <HMI.Phone s={16} />
          </button>
        </div>
      </div>

      {showSearch ? (
        <div className="hm-search">
          <HMI.Search s={15} />
          <input ref={inputRef} value={q} onChange={(e) => setQ(e.target.value)} placeholder="Nom, rôle, club…" />
        </div>
      ) : null}

      {/* ── ZONE FIXE · reste en place, se resserre au scroll ── */}
      <div className={`cl2-fixed ${tight ? 'tight' : ''}`}>
      {!showSearch ? (
        <div className="cl2-seg" role="tablist" aria-label="Sections des appels" style={tSty({ margin: '0 18px 8px', padding: 3 })}>
          {segs.map((s) => (
            <button key={s.k} role="tab" aria-selected={seg === s.k} className={seg === s.k ? 'on' : ''} onClick={() => setSeg(s.k)}
              style={tSty({ height: 27, fontSize: 11.5 })}>
              <span>{s.label}</span>
              {s.n ? <span className={`cl2-missbadge${s.ok ? ' ok' : ''}`}>{s.n}</span> : null}
            </button>
          ))}
        </div>
      ) : null}

      {seg === 'journal' || showSearch ? (
        <div className="hm-chips cl2-chips" style={tSty({ paddingTop: 0, paddingBottom: 7 })}>
          {chips.map((c) => (
            <button key={c.k} className={`hm-chip ${flt === c.k ? 'on' : ''}`} onClick={() => setFlt(c.k)}
              style={tSty({ height: 27, padding: '0 11px', fontSize: 11.5 })}>
              <span>{c.label}</span>
              {c.n ? <span className="cl2-missbadge">{c.n}</span> : null}
            </button>
          ))}
        </div>
      ) : null}
      </div>

      <div className="hm-scroll" ref={scrollRef} onScroll={onScrollList}
        style={todayRoom && !showSearch && seg !== 'upcoming' ? { paddingBottom: 86 } : undefined}>
        {/* ── SALONS D'APPEL · groupes habituels, dépliables & actionnables ── */}
        {seg === 'salons' && !showSearch ? (
          <div className="hm-cardlist">
            {CL2_ROOMS.map((r) => (
              <Cl2RoomCard key={r.id} room={r} open={openRoom === r.id} showToast={showToast} proposed={proposed}
                onToggle={() => setOpenRoom((cur) => (cur === r.id ? null : r.id))}
                onVoice={onVoice} onVideo={onVideo} onRoomCall={onRoomCall} onOpenPassi={openPassi}
                onSchedule={scheduleSlot} onUnschedule={unscheduleSlot} />
            ))}
          </div>
        ) : null}

        {/* ── À VENIR · appels planifiés (salons + vrai agenda) ── */}
        {seg === 'upcoming' && !showSearch ? (
          <React.Fragment>
            <div className="hm-sechead"><span>Appels planifiés</span></div>
            <div className="hm-cardlist">
              {todayRoom ? (
                <div className="cl2-plan today" onClick={() => { setSeg('salons'); setOpenRoom(todayRoom.id); }} role="button" tabIndex={0}
                  onKeyDown={(e) => { if (e.key === 'Enter') { setSeg('salons'); setOpenRoom(todayRoom.id); } }}>
                  <span className="cl2-plan-date"><em>Auj.</em><b>{todayRoom.next.split(' · ')[1] || ''}</b></span>
                  <span className="cl2-plan-main">
                    <b>{todayRoom.video ? 'Visio · ' : 'Appel · '}{todayRoom.name}</b>
                    <span className="cl2-plan-who">
                      <Cl2Stack pids={todayRoom.pids} size={18} />
                      <em>{todayRoom.pids.map(cl2First).join(', ')}</em>
                    </span>
                  </span>
                  <button className="cl2-join" onClick={(e) => { e.stopPropagation(); roomCall(todayRoom); }}>Rejoindre</button>
                </div>
              ) : null}
              {agendaCalls.map((ev) => {
                const pids = (ev.whos || [ev.who]).filter((x) => FP_PEERS[x]);
                return (
                  <div key={ev.id} className="cl2-plan" onClick={() => onSheet({ kind: 'event', ev })} role="button" tabIndex={0}
                    onKeyDown={(e) => { if (e.key === 'Enter') onSheet({ kind: 'event', ev }); }}>
                    <span className="cl2-plan-date"><em>{ev.dow} {ev.d}{ev.m && ev.m !== 6 ? '/' + String(ev.m).padStart(2, '0') : ''}</em><b>{ev.time}</b></span>
                    <span className="cl2-plan-main">
                      <b>{ev.title}</b>
                      <span className="cl2-plan-who">
                        {pids.length ? <Cl2Stack pids={pids} size={18} /> : null}
                        <em>{pids.length ? pids.map(cl2First).join(', ') : ev.who}</em>
                      </span>
                    </span>
                    {ev.status === 'pending'
                      ? <span className="cl2-plan-pill warn">À confirmer</span>
                      : <span className="cl2-plan-pill ok"><Cl2Check s={8} /> Confirmé</span>}
                  </div>
                );
              })}
              {!todayRoom && agendaCalls.length === 0 ? (
                <div className="hm-empty">Aucun appel planifié.</div>
              ) : null}
            </div>
            <button className="cl2-planbtn" onClick={() => onSheet({ kind: 'newevent' })}>
              <HMI.Calendar s={14} /> Planifier un appel
            </button>
          </React.Fragment>
        ) : null}

        {/* ── JOURNAL · l'historique d'appels, cœur de l'onglet ── */}
        {seg === 'journal' || showSearch ? (
          <React.Fragment>
        {CL2_SECTS.map(([key, label]) => {
          const items = visible.filter((l) => l.sect === key);
          if (!items.length) return null;
          return (
            <React.Fragment key={key}>
              <div className="hm-sechead sub"><span>{label}</span></div>
              <div className="hm-cardlist">
                {items.map((l) => <Cl2Row key={l.id} item={l} onOpen={setDetail} onVoice={onVoice} onVideo={onVideo} />)}
              </div>
            </React.Fragment>
          );
        })}
        {visible.length === 0 ? (
          <div className="hm-empty">
            {q.trim() ? `Aucun appel pour « ${q.trim()} ».`
              : flt === 'missed' ? 'Aucun appel manqué. Vous êtes à jour.'
              : 'Aucun appel dans ce filtre.'}
          </div>
        ) : null}
          </React.Fragment>
        ) : null}
        <div className="hm-listfoot"><FPIcon.Shield s={10} /> Appels chiffrés de bout en bout · identités vérifiées Pass ID</div>
      </div>

      {/* ── PROCHAIN APPEL · barre flottante en bas, Rejoindre en 1 tap ── */}
      {todayRoom && !showSearch && seg !== 'upcoming' && !detail ? (
        <div className="cl2-nextdock" onClick={() => { setSeg('salons'); setOpenRoom(todayRoom.id); }} role="button" tabIndex={0}
          style={dockHidden
            ? { transform: 'translateY(calc(100% + 18px))', opacity: 0, pointerEvents: 'none', transition: 'transform 0.28s ease, opacity 0.22s ease' }
            : { transition: 'transform 0.28s ease, opacity 0.22s ease' }}
          aria-hidden={dockHidden}
          onKeyDown={(e) => { if (e.key === 'Enter') { setSeg('salons'); setOpenRoom(todayRoom.id); } }}>
          <span className="cl2-dock-when">
            <b>{todayRoom.next.split(' · ')[1] || ''}</b>
            <em>Auj.</em>
          </span>
          <span className="hm-row-txt">
            <b>{todayRoom.video ? 'Visio' : 'Appel'} · {todayRoom.name}</b>
            <em>{todayRoom.pids.map(cl2First).join(' · ')}</em>
          </span>
          <button className="cl2-join" onClick={(e) => { e.stopPropagation(); roomCall(todayRoom); }}>Rejoindre</button>
        </div>
      ) : null}

      {detail ? (
        <Cl2Sheet item={detail} onClose={() => setDetail(null)}
          onVoice={onVoice} onVideo={onVideo} onOpenChat={onOpenChat} />
      ) : null}
    </div>
  );
}

Object.assign(window, { HMCallsV2, CL2_ROOMS });
