// Live call windows — SSE-driven (same pattern as Boni/Clara)

function useCallManager() {
  const [calls, setCalls] = React.useState({});
  const [notifications, setNotifications] = React.useState([]);
  const [wsConnected, setWsConnected] = React.useState(false);
  const esRef = React.useRef(null);

  React.useEffect(() => {
    const token = getAuthToken();
    if (!token) return;

    function connect() {
      const es = new EventSource(`${NAFFCO_API}/events?token=${token}`);
      esRef.current = es;

      es.onopen = () => setWsConnected(true);
      es.onerror = () => {
        setWsConnected(false);
        es.close();
        setTimeout(connect, 3000);
      };

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

    connect();
    return () => { if (esRef.current) esRef.current.close(); };
  }, []);

  function handleEvent(d) {
    const callId = d.call_id;
    if (!callId) return;

    if (d.event === "call.started") {
      setCalls(prev => ({
        ...prev,
        [callId]: {
          id: callId,
          from: d.from,
          to: d.to,
          direction: d.direction,
          transport: d.transport,
          status: "active",
          transcript: [],
          startedAt: Date.now(),
          minimized: false,
          maximized: false,
          userSpeaking: false,
          botSpeaking: false,
          _lastBotMsgId: null,
        },
      }));
      setNotifications(prev => [...prev, {
        id: `notif-${callId}`,
        callId,
        type: "call.started",
        text: `${d.direction === "outbound" ? "📞→" : "📞←"} ${d.to || d.from}`,
        time: Date.now(),
      }]);
      // Auto-dismiss notification after 8s
      setTimeout(() => {
        setNotifications(prev => prev.filter(n => n.id !== `notif-${callId}`));
      }, 8000);
    }

    if (d.event === "call.ended") {
      setCalls(prev => {
        const c = prev[callId];
        if (!c) return prev;
        return { ...prev, [callId]: { ...c, status: "ended", reason: d.reason, duration: d.duration } };
      });
    }

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

    // ── User interim STT (real-time partial text) ──
    if (d.event === "user.speaking" && d.text) {
      setCalls(prev => {
        const c = prev[callId];
        if (!c) return prev;
        const lastIdx = c.transcript.findLastIndex(t => t.speaker === "user" && t.isInterim);
        if (lastIdx >= 0) {
          const updated = [...c.transcript];
          updated[lastIdx] = { ...updated[lastIdx], text: d.text };
          return { ...prev, [callId]: { ...c, transcript: updated, userSpeaking: true } };
        }
        return { ...prev, [callId]: { ...c, userSpeaking: true, transcript: [...c.transcript, { speaker: "user", text: d.text, isInterim: true, time: new Date() }] } };
      });
    }

    // ── User final transcript ──
    if (d.event === "user.message" && d.text) {
      setCalls(prev => {
        const c = prev[callId];
        if (!c) return prev;
        const lastIdx = c.transcript.findLastIndex(t => t.speaker === "user" && t.isInterim);
        let transcript;
        if (lastIdx >= 0) {
          transcript = [...c.transcript];
          transcript[lastIdx] = { ...transcript[lastIdx], text: d.text, isInterim: false };
        } else {
          transcript = [...c.transcript, { speaker: "user", text: d.text, isInterim: false, time: new Date() }];
        }
        return { ...prev, [callId]: { ...c, transcript, userSpeaking: false } };
      });
    }

    // ── Bot speaking: create streaming entry ──
    if (d.event === "bot.speaking") {
      const messageId = d.message_id || ("greeting_" + Date.now());
      setCalls(prev => {
        const c = prev[callId];
        if (!c) return prev;
        return { ...prev, [callId]: {
          ...c,
          botSpeaking: true,
          _lastBotMsgId: messageId,
          transcript: [...c.transcript, { speaker: "bot", text: d.text || "", messageId, words: [], speaking: true, time: new Date() }],
        }};
      });
    }

    // ── Bot word: update streaming entry word by word ──
    if (d.event === "bot.word" && d.word) {
      setCalls(prev => {
        const c = prev[callId];
        if (!c) return prev;
        const targetId = d.message_id || c._lastBotMsgId;
        const transcript = c.transcript.map(t => {
          if (t.speaker !== "bot" || !t.speaking) return t;
          if (targetId && t.messageId === targetId) {
            const words = [...(t.words || [])];
            const idx = d.word_index ?? words.length;
            if (idx >= words.length) words.push(d.word);
            return { ...t, words, text: words.join(" ") };
          }
          return t;
        });
        return { ...prev, [callId]: { ...c, transcript } };
      });
    }

    // ── Bot finished: finalize streaming entry ──
    if (d.event === "bot.finished") {
      setCalls(prev => {
        const c = prev[callId];
        if (!c) return prev;
        const targetId = d.message_id || c._lastBotMsgId;
        const hasMatch = targetId && c.transcript.some(t => t.messageId === targetId);
        if (hasMatch) {
          const transcript = c.transcript.map(t => {
            if (t.messageId !== targetId) return t;
            return { ...t, speaking: false, ...(d.text ? { text: d.text } : {}) };
          });
          return { ...prev, [callId]: { ...c, botSpeaking: false, transcript } };
        }
        // No streaming entry found — create one
        if (d.text) {
          return { ...prev, [callId]: { ...c, botSpeaking: false, transcript: [...c.transcript, { speaker: "bot", text: d.text, messageId: targetId || ("fin_" + Date.now()), time: new Date() }] } };
        }
        return { ...prev, [callId]: { ...c, botSpeaking: false } };
      });
    }

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

    // ── message.confirmed: finalize or deduplicate ──
    if (d.event === "message.confirmed" && d.text) {
      setCalls(prev => {
        const c = prev[callId];
        if (!c) return prev;
        const targetId = d.message_id;
        const hasStreaming = targetId && c.transcript.some(t => t.messageId === targetId);
        if (hasStreaming) {
          const transcript = c.transcript.map(t => t.messageId !== targetId ? t : { ...t, text: d.text, speaking: false });
          return { ...prev, [callId]: { ...c, transcript } };
        }
        const lastBot = [...c.transcript].reverse().find(t => t.speaker === "bot");
        if (lastBot && lastBot.text === d.text) return prev;
        return { ...prev, [callId]: { ...c, transcript: [...c.transcript, { speaker: "bot", text: d.text, messageId: targetId, time: new Date() }] } };
      });
    }
  }

  function toggleMinimize(id) {
    setCalls(prev => ({ ...prev, [id]: { ...prev[id], minimized: !prev[id]?.minimized } }));
  }
  function toggleMaximize(id) {
    setCalls(prev => ({ ...prev, [id]: { ...prev[id], maximized: !prev[id]?.maximized } }));
  }
  function closeCall(id) {
    setCalls(prev => { const n = { ...prev }; delete n[id]; return n; });
  }
  function hangupCall(id) {
    authFetch(`${NAFFCO_API}/api/calls/${id}/hangup`, { method: "POST" }).catch(() => {});
  }
  function dismissNotification(id) {
    setNotifications(prev => prev.filter(n => n.id !== id));
  }
  function focusCall(id) {
    setCalls(prev => ({ ...prev, [id]: { ...prev[id], minimized: false, maximized: false } }));
  }

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

// ── Call Layer ───────────────────────────────────────────────────────────────

function CallLayer({ calls, notifications, wsConnected, toggleMinimize, toggleMaximize, closeCall, hangupCall, dismissNotification, focusCall }) {
  const activeCalls = Object.values(calls);
  const visibleCalls = activeCalls.filter(c => !c.minimized);
  const minimizedCalls = activeCalls.filter(c => c.minimized);

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

      {minimizedCalls.length > 0 && (
        <div className="fixed bottom-0 left-0 right-0 z-[60] bg-ink-900/95 backdrop-blur-md border-t border-white/10 px-4 py-2 flex items-center gap-2 flex-wrap">
          {minimizedCalls.map(c => (
            <button
              key={c.id}
              onClick={() => focusCall(c.id)}
              className={`inline-flex items-center gap-2 px-3 py-1.5 rounded-lg text-xs font-medium ${
                c.status === "active"
                  ? "bg-amber-500/15 text-amber-200 border border-amber-500/30"
                  : "bg-white/5 text-ink-300 border border-white/10"
              }`}
            >
              <span className={`w-1.5 h-1.5 rounded-full ${c.status === "active" ? "bg-amber-400 pulse-dot" : "bg-ink-500"}`}></span>
              {c.to || c.from}
              <span className="text-ink-500">{c.transcript.length} msgs</span>
            </button>
          ))}
        </div>
      )}

      {/* Notifications */}
      {notifications.length > 0 && (
        <div className="fixed top-4 right-4 z-[80] flex flex-col gap-2 pointer-events-none">
          {notifications.map(n => (
            <div key={n.id} className="card rounded-xl p-4 fade-in pointer-events-auto ring-1 ring-amber-500/30 bg-ink-900/95 backdrop-blur-md shadow-2xl">
              <div className="flex items-center gap-3">
                <span className="w-8 h-8 rounded-full bg-amber-500/15 text-amber-300 inline-flex items-center justify-center shrink-0 calling">
                  <I.Phone size={14} />
                </span>
                <div className="flex-1 min-w-0">
                  <div className="text-xs font-semibold text-ink-100">Incoming Call</div>
                  <div className="text-xs text-ink-300 font-mono mt-0.5 truncate">{n.text}</div>
                </div>
                <button
                  onClick={() => { focusCall(n.callId); dismissNotification(n.id); }}
                  className="text-xs bg-amber-500 text-ink-950 font-semibold px-3 py-1.5 rounded-lg hover:bg-amber-400 transition-colors"
                >View</button>
                <button
                  onClick={() => dismissNotification(n.id)}
                  className="text-xs text-ink-400 hover:text-ink-200 px-2 py-1 rounded-lg hover:bg-white/5"
                >✕</button>
              </div>
            </div>
          ))}
        </div>
      )}
    </>
  );
}

// ── Call Window ──────────────────────────────────────────────────────────────

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

  // Auto-scroll on any transcript change
  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]);

  const isActive = call.status === "active";
  const elapsed = call.duration || (isActive ? Math.floor((Date.now() - call.startedAt) / 1000) : 0);
  const mm = String(Math.floor(elapsed / 60)).padStart(2, "0");
  const ss = String(elapsed % 60).padStart(2, "0");

  const style = call.maximized
    ? { position: "fixed", inset: "16px", zIndex: 70 }
    : { position: "fixed", bottom: 60 + index * 10, right: 16 + index * 8, width: 400, zIndex: 70 - index };

  return (
    <div style={style} className="card rounded-2xl overflow-hidden shadow-2xl border border-white/10 flex flex-col fade-in">
      {/* Header */}
      <div className={`px-4 py-3 flex items-center gap-3 border-b border-white/5 ${isActive ? "bg-amber-500/5" : "bg-ink-800/80"}`}>
        <span className={`w-2 h-2 rounded-full ${isActive ? "bg-amber-400 pulse-dot" : "bg-ink-500"}`}></span>
        <div className="flex-1 min-w-0">
          <div className="text-sm font-medium text-ink-100 truncate">
            {call.direction === "outbound" ? "→" : "←"} {call.to || call.from}
          </div>
          <div className="text-[10px] text-ink-500 font-mono flex items-center gap-2">
            {isActive ? <CallTimer startedAt={call.startedAt} /> : <span>{mm}:{ss}</span>}
            · {call.status}
            {call.userSpeaking && <span className="text-sky-300">· 🎤 speaking</span>}
            {call.botSpeaking && <span className="text-amber-300">· 🤖 speaking</span>}
          </div>
        </div>
        <div className="flex items-center gap-1">
          {isActive && (
            <button onClick={onHangup} className="w-7 h-7 rounded-lg bg-rose-500/15 hover:bg-rose-500/25 text-rose-300 inline-flex items-center justify-center" title="Hang up">
              <I.PhoneOff size={13} />
            </button>
          )}
          <button onClick={onMinimize} className="w-7 h-7 rounded-lg hover:bg-white/10 text-ink-400 hover:text-ink-200 inline-flex items-center justify-center">—</button>
          <button onClick={onClose} className="w-7 h-7 rounded-lg hover:bg-white/10 text-ink-400 hover:text-ink-200 inline-flex items-center justify-center">
            <I.X size={14} />
          </button>
        </div>
      </div>

      {/* Transcript */}
      <div ref={scrollRef} className="flex-1 overflow-y-auto px-4 py-3 space-y-2 bg-ink-950/60" style={{ maxHeight: call.maximized ? "calc(100vh - 140px)" : 320 }}>
        {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" /> Waiting for audio…
              </span>
            ) : "No transcript"}
          </div>
        )}
        {call.transcript.map((turn, idx) => {
          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-amber-500/10 text-ink-100 ring-1 ring-amber-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" ? "🤖 Nova" : "👤 Contact"}
                  {isInterim && <span className="text-sky-400 normal-case not-italic">listening…</span>}
                </div>
                {turn.text || (turn.speaking ? "" : "…")}
                {turn.speaking && <span className="inline-block w-1.5 h-3.5 bg-amber-300 ml-0.5 animate-pulse rounded-sm"></span>}
                {turn.interrupted && <span className="text-[10px] text-amber-400 ml-1">— interrupted</span>}
                {isInterim && <span className="inline-block w-1 h-3 bg-sky-400/60 ml-0.5 animate-pulse rounded-sm"></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-sky-400 animate-pulse"></span>
                Contact speaking…
              </div>
            </div>
          </div>
        )}
      </div>

      {/* Footer */}
      <div className="px-4 py-2 bg-ink-800/50 border-t border-white/5 text-[10px] text-ink-500 font-mono">
        {call.id?.slice(0, 16)}… · {call.transport || "phone"}
      </div>
    </div>
  );
}

// ── Live timer ──────────────────────────────────────────────────────────────
function CallTimer({ startedAt }) {
  const [elapsed, setElapsed] = React.useState(0);
  React.useEffect(() => {
    const id = setInterval(() => setElapsed(Math.floor((Date.now() - startedAt) / 1000)), 1000);
    return () => clearInterval(id);
  }, [startedAt]);
  const m = Math.floor(elapsed / 60);
  const s = elapsed % 60;
  return <span className="text-amber-200 font-mono tabular-nums">{m}:{String(s).padStart(2, "0")}</span>;
}
