// ── SSE hook (replaces legacy WebSocket) ────────────────────────────────────
function useCallSocket(onEvent) {
  const esRef = React.useRef(null);
  const reconnectRef = React.useRef(null);

  React.useEffect(() => {
    let dead = false;

    function connect() {
      if (dead) return;
      const es = new EventSource(`${CLARA_API}/events`);
      esRef.current = es;

      es.onopen = () => {
        onEvent({ type: "ws.connected" });
      };

      es.onmessage = (e) => {
        try {
          const msg = JSON.parse(e.data);
          onEvent(msg);
        } catch {}
      };

      es.onerror = () => {
        onEvent({ type: "ws.disconnected" });
        es.close();
        if (!dead) {
          reconnectRef.current = setTimeout(connect, 2000);
        }
      };
    }

    connect();

    return () => {
      dead = true;
      if (reconnectRef.current) clearTimeout(reconnectRef.current);
      if (esRef.current) esRef.current.close();
    };
  }, []);

  return esRef;
}

// ── Call state manager ──────────────────────────────────────────────────────
function useCallManager() {
  const [calls, setCalls] = React.useState({}); // { [call_id]: callState }
  const [notifications, setNotifications] = React.useState([]);
  const [wsConnected, setWsConnected] = React.useState(false);

  const callsRef = React.useRef(calls);
  callsRef.current = calls;

  const handleEvent = React.useCallback((msg) => {
    const event = msg.event || msg.type;
    if (!event) return;

    if (event === "ws.connected") {
      setWsConnected(true);
      return;
    }
    if (event === "ws.disconnected") {
      setWsConnected(false);
      return;
    }

    // Skip server-level events and events without a call_id (except call.started)
    if (event === "server.connected" || event === "server.disconnected") return;
    if (event === "channel.added" || event === "channel.removed") return;
    if (event === "agent.reloaded" || event === "agent.reload_error") return;

    const callId = msg.call_id;
    if (!callId) return;

    if (event === "call.started") {
      setCalls((prev) => {
        // Prevent duplicate windows for same call_id
        if (prev[callId]) return prev;
        return {
          ...prev,
          [callId]: {
            id: callId,
            from: msg.from || "—",
            to: msg.to || "—",
            direction: msg.direction || "inbound",
            transport: msg.transport || "phone",
            agentId: msg.agent_id,
            startedAt: new Date(),
            endedAt: null,
            reason: null,
            transcript: [],
            status: "active",
            minimized: false,
          },
        };
      });
      // Push notification for inbound
      if (msg.direction !== "outbound") {
        const nid = "notif_" + Date.now();
        setNotifications((prev) => [...prev, {
          id: nid,
          callId,
          from: msg.from || "Número desconocido",
          direction: msg.direction || "inbound",
          time: new Date(),
        }]);
        // Auto-dismiss after 8s
        setTimeout(() => {
          setNotifications((prev) => prev.filter((n) => n.id !== nid));
        }, 8000);
      }
      return;
    }

    if (event === "call.ended") {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        return {
          ...prev,
          [callId]: { ...existing, status: "ended", endedAt: new Date(), reason: msg.reason || "hangup" },
        };
      });
      return;
    }

    // ── User interim STT (partial speech, updates in real-time) ──
    if (event === "user.speaking" && callId && msg.text) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        // Find last interim user entry and update it, or create new
        const lastInterimIdx = existing.transcript.findLastIndex(
          (t) => t.speaker === "user" && t.isInterim
        );
        if (lastInterimIdx >= 0) {
          const updated = [...existing.transcript];
          updated[lastInterimIdx] = { ...updated[lastInterimIdx], text: msg.text };
          return { ...prev, [callId]: { ...existing, transcript: updated, userSpeaking: true } };
        }
        return {
          ...prev,
          [callId]: {
            ...existing,
            userSpeaking: true,
            transcript: [...existing.transcript, {
              speaker: "user",
              text: msg.text,
              isInterim: true,
              time: new Date(),
            }],
          },
        };
      });
      return;
    }

    // ── Speech start/end (VAD indicators) ──
    if (event === "speech.started" && callId) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        return { ...prev, [callId]: { ...existing, userSpeaking: true } };
      });
      return;
    }
    if (event === "speech.ended" && callId) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        return { ...prev, [callId]: { ...existing, userSpeaking: false } };
      });
      return;
    }

    // ── User final transcript (replaces interim) ──
    if (event === "user.message" && callId) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        const text = msg.text || msg.transcript || "";
        // Replace last interim entry if exists
        const lastInterimIdx = existing.transcript.findLastIndex(
          (t) => t.speaker === "user" && t.isInterim
        );
        let transcript;
        if (lastInterimIdx >= 0) {
          transcript = [...existing.transcript];
          transcript[lastInterimIdx] = { ...transcript[lastInterimIdx], text, isInterim: false };
        } else {
          transcript = [...existing.transcript, {
            speaker: "user",
            text,
            isInterim: false,
            time: new Date(),
          }];
        }
        return {
          ...prev,
          [callId]: { ...existing, transcript, userSpeaking: false },
        };
      });
      return;
    }

    // ── Bot streaming: bot.speaking creates entry, bot.word appends, bot.finished finalizes ──
    if (event === "bot.speaking" && callId) {
      // Greeting messages have no message_id — generate a fallback
      const messageId = msg.message_id || ("greeting_" + Date.now());
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        return {
          ...prev,
          [callId]: {
            ...existing,
            botSpeaking: true,
            _lastBotMsgId: messageId,
            transcript: [...existing.transcript, {
              speaker: "bot",
              text: msg.text || "",
              messageId,
              words: [],
              speaking: true,
              time: new Date(),
            }],
          },
        };
      });
      return;
    }

    if (event === "bot.word" && callId && msg.word) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        // Match by message_id, or fall back to last speaking bot entry
        const targetId = msg.message_id || existing._lastBotMsgId;
        const transcript = existing.transcript.map((t) => {
          if (t.speaker !== "bot") return t;
          if (targetId && t.messageId === targetId) {
            const words = [...(t.words || [])];
            const idx = msg.word_index ?? words.length;
            if (idx >= words.length) words.push(msg.word);
            return { ...t, words, text: words.join(" ") };
          }
          return t;
        });
        return { ...prev, [callId]: { ...existing, transcript } };
      });
      return;
    }

    if (event === "bot.finished" && callId) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        const targetId = msg.message_id || existing._lastBotMsgId;
        // Try to match existing streaming entry
        const hasMatch = targetId && existing.transcript.some((t) => t.messageId === targetId);
        if (hasMatch) {
          const transcript = existing.transcript.map((t) => {
            if (t.messageId !== targetId) return t;
            return { ...t, speaking: false, ...(msg.text ? { text: msg.text } : {}) };
          });
          return { ...prev, [callId]: { ...existing, botSpeaking: false, transcript } };
        }
        // No streaming entry found — create one (greeting that never got bot.speaking)
        if (msg.text) {
          return {
            ...prev,
            [callId]: {
              ...existing,
              botSpeaking: false,
              transcript: [...existing.transcript, {
                speaker: "bot",
                text: msg.text,
                messageId: targetId || ("finished_" + Date.now()),
                time: new Date(),
              }],
            },
          };
        }
        return { ...prev, [callId]: { ...existing, botSpeaking: false } };
      });
      return;
    }

    if (event === "bot.interrupted" && callId) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        const targetId = msg.message_id || existing._lastBotMsgId;
        const transcript = existing.transcript.map((t) => {
          if (targetId && t.messageId === targetId) {
            return { ...t, speaking: false, interrupted: true };
          }
          return t;
        });
        return { ...prev, [callId]: { ...existing, botSpeaking: false, transcript } };
      });
      return;
    }

    // ── Tool calls — show compact system entries ──
    if (event === "llm.tool_call" && callId) {
      const toolCalls = msg.tool_calls || [];
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        const newEntries = toolCalls.map((tc) => ({
          speaker: "system",
          type: "tool_call",
          toolName: tc.name,
          text: tc.name,
          time: new Date(),
        }));
        return {
          ...prev,
          [callId]: {
            ...existing,
            // Remove any empty bot streaming entry that was just a placeholder for the tool call
            transcript: [
              ...existing.transcript.filter((t) => !(t.speaking && !t.text)),
              ...newEntries,
            ],
          },
        };
      });
      return;
    }

    if (event === "llm.tool_result" && callId) {
      // We just mark the last tool_call entry as resolved (optional visual)
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        const transcript = [...existing.transcript];
        // Find last tool_call entry that isn't resolved yet
        for (let i = transcript.length - 1; i >= 0; i--) {
          if (transcript[i].type === "tool_call" && !transcript[i].resolved) {
            transcript[i] = { ...transcript[i], resolved: true };
            break;
          }
        }
        return { ...prev, [callId]: { ...existing, transcript } };
      });
      return;
    }

    // ── message.confirmed — fallback: if streaming entry exists, finalize text; if not, create entry ──
    if (event === "message.confirmed" && callId) {
      const text = msg.text || msg.content || "";
      if (!text) return;
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        // Check if we already have a streaming entry for this message
        const targetId = msg.message_id;
        const hasStreaming = targetId && existing.transcript.some((t) => t.messageId === targetId);
        if (hasStreaming) {
          // Update existing streaming entry with final text
          const transcript = existing.transcript.map((t) => {
            if (t.messageId !== targetId) return t;
            return { ...t, text, speaking: false };
          });
          return { ...prev, [callId]: { ...existing, transcript } };
        }
        // Check if the last bot entry already has this text (avoid duplicates)
        const lastBot = [...existing.transcript].reverse().find((t) => t.speaker === "bot");
        if (lastBot && lastBot.text === text) return prev;
        // No streaming entry — create new one (bot.speaking never fired)
        return {
          ...prev,
          [callId]: {
            ...existing,
            transcript: [...existing.transcript, {
              speaker: "bot",
              text,
              messageId: targetId,
              time: new Date(),
            }],
          },
        };
      });
      return;
    }

    // ── User speaking indicator ──
    if (event === "user.speaking" && callId) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        return { ...prev, [callId]: { ...existing, userSpeaking: true } };
      });
      return;
    }
    if ((event === "speech.ended" || event === "turn.end") && callId) {
      setCalls((prev) => {
        const existing = prev[callId];
        if (!existing) return prev;
        return { ...prev, [callId]: { ...existing, userSpeaking: false } };
      });
      return;
    }
  }, []);

  const wsRef = useCallSocket(handleEvent);

  const toggleMinimize = (callId) => {
    setCalls((prev) => {
      const existing = prev[callId];
      if (!existing) return prev;
      return { ...prev, [callId]: { ...existing, minimized: !existing.minimized, maximized: false } };
    });
  };

  const toggleMaximize = (callId) => {
    setCalls((prev) => {
      const existing = prev[callId];
      if (!existing) return prev;
      return { ...prev, [callId]: { ...existing, maximized: !existing.maximized, minimized: false } };
    });
  };

  const closeCall = (callId) => {
    setCalls((prev) => {
      const next = { ...prev };
      delete next[callId];
      return next;
    });
  };

  const hangupCall = async (callId) => {
    // Optimistically update UI immediately
    setCalls((prev) => {
      const existing = prev[callId];
      if (!existing) return prev;
      return {
        ...prev,
        [callId]: {
          ...existing,
          status: "ended",
          endedAt: Date.now(),
          reason: "hangup",
          maximized: false,
          userSpeaking: false,
          botSpeaking: false,
        },
      };
    });
    try {
      await fetch(`${CLARA_API}/api/calls/${callId}/hangup`, { method: "POST" });
    } catch (err) {
      console.error("Hangup failed:", err);
    }
  };

  const dismissNotification = (nid) => {
    setNotifications((prev) => prev.filter((n) => n.id !== nid));
  };

  const focusCall = (callId) => {
    setCalls((prev) => {
      const existing = prev[callId];
      if (!existing) return prev;
      return { ...prev, [callId]: { ...existing, minimized: false, maximized: true } };
    });
  };

  return {
    calls,
    notifications,
    wsConnected,
    toggleMinimize,
    toggleMaximize,
    closeCall,
    hangupCall,
    dismissNotification,
    focusCall,
    wsRef,
  };
}

// ── Call Notification Toast ─────────────────────────────────────────────────
function CallNotification({ notification, onDismiss, onFocus }) {
  return (
    <div className="card rounded-xl p-4 fade-in pointer-events-auto ring-1 ring-sky-500/30 bg-ink-900/95 backdrop-blur-md shadow-2xl">
      <div className="flex items-start gap-3">
        <span className="w-10 h-10 rounded-full bg-sky-500/15 text-sky-300 inline-flex items-center justify-center shrink-0 calling">
          <I.PhoneIncoming size={18} />
        </span>
        <div className="flex-1 min-w-0">
          <div className="text-sm font-semibold text-ink-100">Llamada entrante</div>
          <div className="text-xs text-ink-300 font-mono mt-0.5 truncate">{notification.from}</div>
          <div className="text-[10px] text-ink-500 mt-1">{fmtClock(notification.time)}</div>
        </div>
        <div className="flex flex-col gap-1.5 shrink-0">
          <button
            onClick={() => { onFocus(notification.callId); onDismiss(notification.id); }}
            className="text-xs bg-sky-500 text-ink-950 font-semibold px-3 py-1.5 rounded-lg hover:bg-sky-400 transition-colors"
          >
            Ver
          </button>
          <button
            onClick={() => onDismiss(notification.id)}
            className="text-xs text-ink-400 hover:text-ink-200 px-3 py-1 rounded-lg hover:bg-white/5 transition-colors"
          >
            Ignorar
          </button>
        </div>
      </div>
    </div>
  );
}

// ── Floating Call Window ────────────────────────────────────────────────────
function CallWindow({ call, onMinimize, onClose, onMaximize, onHangup, index }) {
  const scrollRef = React.useRef(null);

  // Auto-scroll transcript (on any change, including word streaming)
  const lastText = call.transcript.length > 0 ? call.transcript[call.transcript.length - 1].text : "";
  React.useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
    }
  }, [call.transcript.length, lastText]);

  if (call.minimized) return null;

  const isActive = call.status === "active";
  const isMaximized = call.maximized;
  const elapsed = call.endedAt
    ? Math.floor((call.endedAt - call.startedAt) / 1000)
    : null;

  const dirLabel = call.direction === "outbound" ? "Saliente" : "Entrante";
  const dirIcon = call.direction === "outbound" ? <I.ArrowUpRight size={12} /> : <I.ArrowDownLeft size={12} />;

  // Transcript renderer (shared between modes)
  const renderTranscript = (maxH) => (
    <div
      ref={scrollRef}
      className={`flex-1 overflow-y-auto px-4 py-3 flex flex-col gap-2 bg-ink-950/60 ${maxH}`}
    >
      {call.transcript.length === 0 && (
        <div className="flex items-center justify-center py-8 text-ink-500 text-xs">
          {isActive ? (
            <span className="inline-flex items-center gap-2">
              <I.Refresh size={12} className="animate-spin" /> Esperando audio…
            </span>
          ) : "Sin transcripción"}
        </div>
      )}
      {call.transcript.map((turn, idx) => {
        if (turn.type === "tool_call") {
          const TOOL_LABELS = {
            getPatientInfo: "Buscar paciente",
            checkWaitlistStatus: "Consultar lista",
            escalateToHuman: "Escalar a humano",
            updateContactPreference: "Actualizar preferencia",
            forwardToCoordinator: "Transferir llamada",
          };
          const label = TOOL_LABELS[turn.toolName] || turn.toolName;
          return (
            <div key={idx} className="flex justify-center my-1">
              <span className={`inline-flex items-center gap-1.5 text-[10px] font-mono px-2.5 py-1 rounded-full ${
                turn.resolved
                  ? "bg-emerald-500/8 text-emerald-300 ring-1 ring-emerald-500/15"
                  : "bg-violet-500/8 text-violet-300 ring-1 ring-violet-500/15"
              }`}>
                {turn.resolved ? "✓" : <I.Refresh size={9} className="animate-spin" />}
                {label}
              </span>
            </div>
          );
        }
        if (turn.speaker === "bot" && !turn.text && !turn.speaking) return null;
        const isInterim = turn.speaker === "user" && turn.isInterim;
        return (
          <div key={idx} className={`flex ${turn.speaker === "bot" ? "justify-start" : "justify-end"}`}>
            <div className={`bubble ${
              turn.speaker === "bot"
                ? "bg-sky-500/10 text-ink-100 ring-1 ring-sky-500/20 rounded-tl-sm"
                : isInterim
                  ? "bg-white/3 text-ink-300 ring-1 ring-white/5 rounded-tr-sm italic"
                  : "bg-white/5 text-ink-100 ring-1 ring-white/10 rounded-tr-sm"
            }`}>
              <div className="text-[10px] uppercase tracking-wider opacity-60 mb-0.5 flex items-center gap-1">
                {turn.speaker === "bot" ? "🤖 Clara" : "👤 Paciente"}
                {isInterim && <span className="text-amber-400 normal-case not-italic">escuchando…</span>}
                <span className="font-mono">{fmtClock(turn.time)}</span>
              </div>
              {turn.text || (turn.speaking ? "" : "…")}
              {isInterim && <span className="inline-block w-1 h-3 bg-amber-400/60 ml-0.5 animate-pulse rounded-sm" />}
              {turn.speaking && <span className="inline-block w-1.5 h-3.5 bg-sky-300 ml-0.5 animate-pulse rounded-sm" />}
              {turn.interrupted && <span className="text-[10px] text-amber-400 ml-1">— interrumpido</span>}
            </div>
          </div>
        );
      })}
      {/* User speaking indicator (when no interim text yet) */}
      {call.userSpeaking && !call.transcript.some(t => t.isInterim) && (
        <div className="flex justify-end">
          <div className="bubble bg-white/3 text-ink-400 ring-1 ring-white/5 rounded-tr-sm italic">
            <div className="flex items-center gap-1.5 text-[10px]">
              <span className="w-1.5 h-1.5 rounded-full bg-amber-400 animate-pulse" />
              Paciente hablando…
            </div>
          </div>
        </div>
      )}
    </div>
  );

  // Title bar (shared)
  const titleBar = (
    <div className="px-4 py-3 bg-ink-800/80 border-b border-white/5 flex items-center gap-3 no-select cursor-default shrink-0">
      <div className="flex items-center gap-1.5">
        <button
          onClick={onClose}
          className="w-3 h-3 rounded-full bg-rose-500 hover:bg-rose-400 transition-colors"
          title="Cerrar"
        />
        <button
          onClick={onMinimize}
          className="w-3 h-3 rounded-full bg-amber-500 hover:bg-amber-400 transition-colors"
          title="Minimizar"
        />
        <button
          onClick={onMaximize}
          className="w-3 h-3 rounded-full bg-emerald-500 hover:bg-emerald-400 transition-colors"
          title={isMaximized ? "Restaurar" : "Maximizar"}
        />
      </div>

      <div className="flex-1 flex items-center justify-center gap-2">
        {isActive && (
          <span className="w-2 h-2 rounded-full bg-emerald-400 pulse-dot" />
        )}
        <span className="text-xs font-medium text-ink-200 truncate">
          {call.direction === "outbound" ? call.to : call.from}
        </span>
      </div>

      <div className="flex items-center gap-1.5 text-ink-400">
        {dirIcon}
        <span className="text-[10px]">{dirLabel}</span>
      </div>
    </div>
  );

  // Status bar (shared)
  const statusBar = (
    <div className="px-4 py-2 bg-ink-850 border-b border-white/5 flex items-center justify-between shrink-0">
      <div className="flex items-center gap-2 text-[11px]">
        {isActive ? (
          <>
            <span className="text-emerald-300 font-medium">En curso</span>
            <CallTimer startedAt={call.startedAt} />
          </>
        ) : (
          <>
            <span className="text-ink-400">Finalizada</span>
            {elapsed != null && (
              <span className="text-ink-500 font-mono">{fmtDuration(elapsed)}</span>
            )}
            {call.reason && (
              <span className="text-ink-500">· {call.reason}</span>
            )}
          </>
        )}
      </div>
      <div className="flex items-center gap-1.5">
        {call.userSpeaking && (
          <span className="badge bg-sky-500/10 text-sky-300 border-sky-500/20 text-[10px] py-0">
            <span className="w-1.5 h-1.5 rounded-full bg-sky-400 pulse-dot" /> Hablando
          </span>
        )}
        {call.botSpeaking && (
          <span className="badge bg-violet-500/10 text-violet-300 border-violet-500/20 text-[10px] py-0">
            <span className="w-1.5 h-1.5 rounded-full bg-violet-400 pulse-dot" /> Clara
          </span>
        )}
      </div>
    </div>
  );

  const footer = (
    <div className="px-4 py-2.5 bg-ink-800/50 border-t border-white/5 flex items-center justify-between shrink-0">
      <div>
        {isActive && (
          <button
            onClick={onHangup}
            className="inline-flex items-center gap-1.5 text-xs font-medium text-rose-300 bg-rose-500/10 hover:bg-rose-500/20 px-3 py-1.5 rounded-lg ring-1 ring-rose-500/20 hover:ring-rose-500/40 transition-all"
          >
            <I.PhoneOff size={12} /> Colgar
          </button>
        )}
      </div>
      <button
        onClick={onClose}
        className="text-xs text-ink-400 hover:text-ink-200 px-3 py-1.5 rounded-lg hover:bg-white/5 transition-colors"
      >
        {isActive ? "Ocultar" : "Cerrar"}
      </button>
    </div>
  );

  // ── Maximized mode: centered overlay ──
  if (isMaximized) {
    return (
      <div className="fixed inset-0 z-[60] flex items-center justify-center fade-in">
        <div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={onMaximize} />
        <div className="relative w-full max-w-2xl mx-4 flex flex-col card rounded-2xl overflow-hidden shadow-2xl border border-white/10" style={{ maxHeight: "85vh" }}>
          {titleBar}
          {statusBar}
          {renderTranscript("min-h-[300px]")}
          {footer}
        </div>
      </div>
    );
  }

  // ── Normal mode: floating window ──
  const style = {
    position: "fixed",
    bottom: `${80 + index * 20}px`,
    right: `${24 + index * 20}px`,
    zIndex: 50 + index,
  };

  return (
    <div style={style} className="w-[400px] flex flex-col slide-in">
      <div className="card rounded-xl overflow-hidden shadow-2xl border border-white/10 flex flex-col max-h-[500px]">
        {titleBar}
        {statusBar}
        {renderTranscript("min-h-[120px] max-h-[340px]")}
        {footer}
      </div>
    </div>
  );
}

// ── Live timer ──────────────────────────────────────────────────────────────
function CallTimer({ startedAt }) {
  const [elapsed, setElapsed] = React.useState(0);

  React.useEffect(() => {
    const id = setInterval(() => {
      setElapsed(Math.floor((Date.now() - startedAt.getTime()) / 1000));
    }, 1000);
    return () => clearInterval(id);
  }, [startedAt]);

  return <span className="text-emerald-200 font-mono tabular-nums">{fmtDuration(elapsed)}</span>;
}

function fmtDuration(secs) {
  const m = Math.floor(secs / 60);
  const s = secs % 60;
  return `${m}:${String(s).padStart(2, "0")}`;
}

// ── Call Bar (bottom dock) ──────────────────────────────────────────────────
function CallBar({ calls, onFocus, onClose, wsConnected }) {
  const callList = Object.values(calls);
  const active = callList.filter((c) => c.status === "active");
  const ended = callList.filter((c) => c.status === "ended");
  const minimized = callList.filter((c) => c.minimized);
  const [showHistory, setShowHistory] = React.useState(false);

  if (callList.length === 0 && !showHistory) {
    return (
      <div className="fixed bottom-4 right-4 z-40">
        <div className="flex items-center gap-2">
          <span className={`w-2 h-2 rounded-full ${wsConnected ? "bg-emerald-400 pulse-dot" : "bg-rose-400"}`} />
          <span className="text-[11px] text-ink-500 font-mono">
            {wsConnected ? "Conectado" : "Desconectado"}
          </span>
        </div>
      </div>
    );
  }

  return (
    <>
      {/* Minimized call pills */}
      {minimized.length > 0 && (
        <div className="fixed bottom-16 right-4 z-40 flex flex-col gap-2">
          {minimized.map((call) => (
            <button
              key={call.id}
              onClick={() => onFocus(call.id)}
              className="card rounded-xl px-4 py-2.5 flex items-center gap-3 hover:bg-white/5 transition-colors shadow-lg pointer-events-auto min-w-[220px]"
            >
              {call.status === "active" ? (
                <span className="w-2 h-2 rounded-full bg-emerald-400 pulse-dot shrink-0" />
              ) : (
                <span className="w-2 h-2 rounded-full bg-ink-500 shrink-0" />
              )}
              <div className="flex-1 min-w-0 text-left">
                <div className="text-xs text-ink-100 font-medium truncate">
                  {call.direction === "outbound" ? call.to : call.from}
                </div>
                <div className="text-[10px] text-ink-400">
                  {call.status === "active" ? (
                    <CallTimer startedAt={call.startedAt} />
                  ) : "Finalizada"}
                </div>
              </div>
              <div className="flex items-center gap-1 text-ink-500">
                <I.MessageSquare size={12} />
                <span className="text-[10px] font-mono">{call.transcript.length}</span>
              </div>
              <I.Maximize2 size={12} className="text-ink-500" />
            </button>
          ))}
        </div>
      )}

      {/* Bottom dock */}
      <div className="fixed bottom-4 right-4 z-40 flex items-center gap-2">
        <div className="flex items-center gap-2">
          <span className={`w-2 h-2 rounded-full ${wsConnected ? "bg-emerald-400 pulse-dot" : "bg-rose-400"}`} />
          <span className="text-[11px] text-ink-500 font-mono">
            {wsConnected ? "SSE" : "Desconectado"}
          </span>
        </div>

        {active.length > 0 && (
          <div className="card rounded-lg px-3 py-1.5 flex items-center gap-2 text-xs text-emerald-300 ring-1 ring-emerald-500/20">
            <span className="w-2 h-2 rounded-full bg-emerald-400 pulse-dot" />
            {active.length} llamada{active.length > 1 ? "s" : ""} activa{active.length > 1 ? "s" : ""}
          </div>
        )}

        {ended.length > 0 && (
          <button
            onClick={() => setShowHistory(!showHistory)}
            className="card rounded-lg px-3 py-1.5 flex items-center gap-2 text-xs text-ink-300 hover:text-ink-100 hover:bg-white/5 transition-colors"
          >
            <I.Clock size={12} /> {ended.length} finalizada{ended.length > 1 ? "s" : ""}
          </button>
        )}
      </div>

      {/* History popup */}
      {showHistory && ended.length > 0 && (
        <div className="fixed bottom-14 right-4 z-40 w-80 card rounded-xl shadow-2xl border border-white/10 fade-in">
          <div className="px-4 py-3 border-b border-white/5 flex items-center justify-between">
            <span className="text-xs font-semibold text-ink-200">Llamadas recientes</span>
            <button onClick={() => setShowHistory(false)} className="text-ink-500 hover:text-ink-200">
              <I.X size={14} />
            </button>
          </div>
          <div className="max-h-[300px] overflow-y-auto">
            {[...ended].reverse().map((call) => {
              const dur = call.endedAt ? Math.floor((call.endedAt - call.startedAt) / 1000) : 0;
              return (
                <button
                  key={call.id}
                  onClick={() => { onFocus(call.id); setShowHistory(false); }}
                  className="w-full text-left px-4 py-3 flex items-center gap-3 hover:bg-white/3 border-b border-white/5 last:border-0 transition-colors"
                >
                  <span className={`w-8 h-8 rounded-lg inline-flex items-center justify-center shrink-0 ${
                    call.direction === "outbound" ? "bg-sky-500/10 text-sky-300" : "bg-violet-500/10 text-violet-300"
                  }`}>
                    {call.direction === "outbound" ? <I.ArrowUpRight size={14} /> : <I.ArrowDownLeft size={14} />}
                  </span>
                  <div className="flex-1 min-w-0">
                    <div className="text-xs text-ink-100 font-medium truncate">
                      {call.direction === "outbound" ? call.to : call.from}
                    </div>
                    <div className="text-[10px] text-ink-400 mt-0.5 flex items-center gap-2">
                      <span>{fmtClock(call.startedAt)}</span>
                      <span className="text-ink-600">·</span>
                      <span className="font-mono">{fmtDuration(dur)}</span>
                      <span className="text-ink-600">·</span>
                      <span>{call.transcript.length} turnos</span>
                    </div>
                  </div>
                  <I.ChevronRight size={12} className="text-ink-500" />
                </button>
              );
            })}
          </div>
        </div>
      )}
    </>
  );
}

// ── Notification Host ───────────────────────────────────────────────────────
function CallNotificationHost({ notifications, onDismiss, onFocus }) {
  return (
    <div className="fixed top-4 left-1/2 -translate-x-1/2 z-50 flex flex-col gap-2 w-96 pointer-events-none">
      {notifications.map((n) => (
        <CallNotification
          key={n.id}
          notification={n}
          onDismiss={onDismiss}
          onFocus={onFocus}
        />
      ))}
    </div>
  );
}

// ── Main Call Layer (renders on top of everything) ───────────────────────────
function CallLayer({ calls, notifications, wsConnected, toggleMinimize, toggleMaximize, closeCall, hangupCall, dismissNotification, focusCall }) {
  const callList = Object.values(calls);
  const openCalls = callList.filter((c) => !c.minimized);

  return (
    <>
      <CallNotificationHost
        notifications={notifications}
        onDismiss={dismissNotification}
        onFocus={focusCall}
      />

      {openCalls.map((call, idx) => (
        <CallWindow
          key={call.id}
          call={call}
          index={idx}
          onMinimize={() => toggleMinimize(call.id)}
          onClose={() => closeCall(call.id)}
          onMaximize={() => toggleMaximize(call.id)}
          onHangup={() => hangupCall(call.id)}
        />
      ))}

      <CallBar
        calls={calls}
        onFocus={focusCall}
        onClose={closeCall}
        wsConnected={wsConnected}
      />
    </>
  );
}

// Export to window
Object.assign(window, {
  useCallManager, CallLayer, fmtDuration,
});
