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

function App() {
  // ── Theme (dark mode lives in localStorage) ────────────────────────
  const [dark, setDark] = useState(() => {
    try { return localStorage.getItem('relay.dark') === '1'; } catch { return false; }
  });
  useEffect(() => {
    document.body.classList.toggle('dark', dark);
    try { localStorage.setItem('relay.dark', dark ? '1' : '0'); } catch {}
  }, [dark]);

  // ── App state ──────────────────────────────────────────────────────
  const [chats, setChats] = useState([]);
  const [selectedId, setSelectedId] = useState(null);
  const [tab, setTab] = useState('chats');
  const [bridgeState, setBridgeState] = useState('disconnected');
  const [bridgeIp, setBridgeIp] = useState(null);
  const [log, setLog] = useState([]);
  const [responses, setResponses] = useState([]);
  const [contactModal, setContactModal] = useState(null);   // { mode, initial } | null

  const bridgeRef = useRef(null);

  // Build the bridge once and wire SSE callbacks into state setters.
  if (!bridgeRef.current) {
    bridgeRef.current = createBridge({
      onLog: (entry) => setLog(prev => trimTail([...prev, entry], 1000)),
      onState: ({ state, ip }) => {
        setBridgeState(state);
        setBridgeIp(ip);
      },
      onResp: (resp) => setResponses(prev => trimTail([...prev, resp], 80)),
      onReady: (boot) => {
        setBridgeState(boot.state.state);
        setBridgeIp(boot.state.ip);
        setChats(boot.chats);
        setResponses(boot.responses);
        setLog(boot.log);
      },
      onInboundSMS:    ({ chat, message }) => setChats(prev => mergeMessage(prev, chat, message)),
      onInboundCall:   ({ chat, message }) => setChats(prev => mergeMessage(prev, chat, message)),
      onSmsOut:        ({ chat, message }) => setChats(prev => mergeMessage(prev, chat, message)),
      onSmsOutStatus:  ({ chat_number, message_id, status }) => {
        setChats(prev => prev.map(c => c.number === chat_number
          ? { ...c, messages: c.messages.map(m =>
              m.id === message_id ? { ...m, status } : m) }
          : c));
      },
      onChatCreated:   (chat) => setChats(prev => mergeChat(prev, chat)),
      onCleared:       () => { setChats([]); setResponses([]); setLog([]); },
    });
  }
  const bridge = bridgeRef.current;

  // Bootstrap on mount.
  useEffect(() => {
    bridge.bootstrap().catch(console.error);
    const es = bridge.startStream();
    return () => es.close();
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, []);

  const sortedChats = useMemo(() => sortChats(chats), [chats]);

  const selectedChat = useMemo(
    () => chats.find(c => c.number === selectedId) || null,
    [chats, selectedId]
  );

  const handleSelect = (number) => {
    setSelectedId(number);
    setTab('chats');
  };

  const handleSend = async (chatNumber, body) => {
    // Server inserts the message + broadcasts sms_out via SSE — no local optimistic insert here.
    await bridge.sendSMS(chatNumber, body);
  };

  const handleSaveContact = async (number, name) => {
    const chat = await bridge.createChat(number, name);   // upsert
    setChats(prev => mergeChat(prev, chat));
    setSelectedId(chat.number);
    setContactModal(null);
    setTab('chats');
  };

  const settingsBadge = useMemo(() => {
    if (bridgeState === 'disconnected' || bridgeState === 'bridge_offline') return '!';
    return 0;
  }, [bridgeState]);

  return (
    <div className="app">
      <Sidebar
        chats={sortedChats}
        selectedId={selectedId}
        onSelect={handleSelect}
        tab={tab}
        onTab={setTab}
        unreadSettings={settingsBadge}
        bridgeState={bridgeState}
        onNewChat={() => setContactModal({ mode: 'create', initial: null })}
        dark={dark}
        onToggleDark={() => setDark(d => !d)}
        ownNumber={window.OWN_NUMBER || '—'}
      />
      <main className="stage">
        {tab === 'chats' ? (
          <Conversation
            chat={selectedChat}
            onSend={handleSend}
            bridgeReady={bridgeState === 'ready'}
            onEditContact={(c) => setContactModal({ mode: 'edit', initial: c })}
          />
        ) : (
          <Settings
            bridge={bridge}
            bridgeState={bridgeState}
            bridgeIp={bridgeIp}
            lastResp={responses}
            log={log}
            onClearLog={() => setLog([])}
            onClearResponses={() => setResponses([])}
          />
        )}
      </main>

      {contactModal && (
        <ContactModal
          mode={contactModal.mode}
          initial={contactModal.initial}
          onClose={() => setContactModal(null)}
          onSave={handleSaveContact}
        />
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────
// Helpers
// ─────────────────────────────────────────────────────────

function trimTail(arr, max) {
  return arr.length <= max ? arr : arr.slice(arr.length - max);
}

function mergeChat(chats, chat) {
  const idx = chats.findIndex(c => c.number === chat.number);
  if (idx >= 0) {
    const next = { ...chats[idx], ...chat,
                   messages: chat.messages ?? chats[idx].messages };
    const arr = [...chats]; arr[idx] = next; return arr;
  }
  return [...chats, { messages: [], ...chat }];
}

function mergeMessage(chats, chat, message) {
  const idx = chats.findIndex(c => c.number === chat.number);
  if (idx >= 0) {
    const c = chats[idx];
    // De-dupe by id (SSE could deliver something already in initial bootstrap).
    if (c.messages.some(m => m.id === message.id)) return chats;
    const next = { ...c, messages: [...c.messages, message] };
    const arr = [...chats]; arr[idx] = next; return arr;
  }
  return [...chats, { ...chat, messages: [message] }];
}

ReactDOM.createRoot(document.getElementById('root')).render(<App />);
