// app.jsx — top-level App (Phase 2 Read-Only).
// Wires Login -> Inbox, lädt Chats + Messages aus Supabase, abonniert Realtime.
// Schreib-Operationen (Send, KI-Toggle, Resume) sind in Phase 2 deaktiviert
// und kommen in Phase 3.
const { useState: useS, useEffect: useE, useRef: useR, useMemo: useM } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "fontUI": "Inter",
  "fontDisplay": "Cinzel",
  "aiHandover": "auto-pause-manual-resume"
}/*EDITMODE-END*/;

const FONT_UI_OPTIONS = ['Inter', 'IBM Plex Sans', 'Space Grotesk', 'Manrope'];
const FONT_DISPLAY_OPTIONS = ['Cinzel', 'Space Grotesk', 'IBM Plex Sans', 'Manrope'];

const HANDOVER_OPTIONS = [
  { value: 'auto-pause-manual-resume', label: 'Auto-Pause + manuelles Wiederaktivieren', desc: 'Sobald du antwortest, KI pausiert dauerhaft. Du musst sie pro Chat wieder einschalten.' },
  { value: 'auto-pause-1h', label: 'Auto-Pause für 1 Stunde', desc: 'KI pausiert für 60 Min nach deiner Antwort, dann wieder aktiv.' },
  { value: 'auto-pause-24h', label: 'Auto-Pause für 24 Stunden', desc: 'KI pausiert einen Tag, dann wieder aktiv.' },
  { value: 'always-off-after-staff', label: 'Permanent aus nach erster Antwort', desc: 'Sobald ein Mitarbeiter einmal antwortet, KI für diesen Chat dauerhaft aus.' },
];

function App() {
  const [user, setUser] = useS(null);
  const [bootstrapping, setBootstrapping] = useS(true);
  const [account, setAccount] = useS(null);
  const [chats, setChats] = useS([]);
  const [activeId, setActiveId] = useS(null);
  const [query, setQuery] = useS('');
  const [filter, setFilter] = useS('all');
  const [view, setView] = useS('inbox'); // 'inbox' | 'archive'
  const [profileOpen, setProfileOpen] = useS(false);
  const [globalAI, setGlobalAI] = useS(true);
  const [showChat, setShowChat] = useS(false);
  const [t, setTweak] = window.useTweaks ? window.useTweaks(TWEAK_DEFAULTS) : [TWEAK_DEFAULTS, () => {}];

  const db = window.TF_DB;

  // --- Bootstrap Session bei Mount ---
  useE(() => {
    let cancelled = false;
    (async () => {
      if (!db) {
        console.error('[app] TF_DB nicht verfuegbar');
        setBootstrapping(false);
        return;
      }
      const u = await db.auth.getCurrentUser();
      if (cancelled) return;
      if (u) {
        if (u._orphan) {
          console.warn('[app] Auth-User ohne public.users-Eintrag — Setup unvollstaendig');
        }
        setUser(u);
      }
      setBootstrapping(false);
    })();
    return () => { cancelled = true; };
  }, []);

  // --- Account + Chats laden nach Login ---
  useE(() => {
    if (!user || !user.account_id) return;
    let cancelled = false;
    (async () => {
      const acc = await db.loadAccount(user.account_id);
      if (cancelled) return;
      if (acc) {
        setAccount(acc);
        setGlobalAI(acc.ki_global_on !== false);
      }
      const cs = await db.loadChats({ limit: 500, archived: view === 'archive' });
      if (cancelled) return;
      setChats(cs);
    })();
    return () => { cancelled = true; };
  }, [user?.account_id, view]);

  // --- Realtime: chats ---
  useE(() => {
    if (!user || !user.account_id) return;
    const off = db.subscribeChats((payload) => {
      const evt = payload.eventType || payload.event;
      if (evt === 'INSERT') {
        // neuer Chat
        setChats((cs) => {
          if (cs.find((c) => c.id === payload.new.id)) return cs;
          const mapped = db.mapDbChat(payload.new, []);
          return [mapped, ...cs];
        });
      } else if (evt === 'UPDATE') {
        setChats((cs) => cs.map((c) => {
          if (c.id !== payload.new.id) return c;
          // Bestehende Messages behalten — Updates aendern nur Chat-Felder
          return { ...db.mapDbChat(payload.new, []), messages: c.messages };
        }));
      } else if (evt === 'DELETE') {
        setChats((cs) => cs.filter((c) => c.id !== payload.old.id));
      }
    });
    return off;
  }, [user?.account_id]);

  // --- Realtime: messages des aktiven Chats (INSERT + UPDATE) ---
  // Outbound-Optimistic-Rows haben dieselbe id wie die spaetere DB-Row
  // (corr_id). Beim INSERT der Pre-Insert-Row (status='sending') bleibt der
  // Optimistic-Spinner sichtbar. Beim UPDATE auf 'sent' wird _sending
  // gecleart, beim UPDATE auf 'failed' wird _error aus status_error gesetzt.
  useE(() => {
    if (!activeId) return;
    const off = db.subscribeMessages(activeId, (row) => {
      setChats((cs) => cs.map((c) => {
        if (c.id !== row.chat_id) return c;
        const idx = c.messages.findIndex((m) => m.id === row.id);
        const mapped = db.mapDbMessage(row);
        if (idx >= 0) {
          const prev = c.messages[idx];
          const merged = { ...mapped };
          if (row.status === 'sending') {
            if (prev._sending) merged._sending = true;
            if (prev._error)   merged._error   = prev._error;
          } else if (row.status === 'failed') {
            merged._sending = false;
            merged._error   = row.status_error || prev._error || 'Senden fehlgeschlagen';
          } else if (row.status === 'sent') {
            merged._sending = false;
            // _error wird bewusst nicht uebernommen — Send war erfolgreich.
          }
          const next = c.messages.slice();
          next[idx] = merged;
          return { ...c, messages: next };
        }
        return { ...c, messages: [...c.messages, mapped] };
      }));
    });
    return off;
  }, [activeId]);

  // Apply font tweaks
  useE(() => {
    document.documentElement.style.setProperty('--font-ui', `'${t.fontUI}', system-ui, sans-serif`);
    document.documentElement.style.setProperty('--font-display', `'${t.fontDisplay}', serif`);
  }, [t.fontUI, t.fontDisplay]);

  // Filter chats
  const filteredChats = useM(() => {
    let list = chats;
    if (query.trim()) {
      const q = query.toLowerCase();
      list = list.filter((c) =>
        c.name.toLowerCase().includes(q) ||
        c.handle.toLowerCase().includes(q) ||
        c.messages.some((m) => (m.text || '').toLowerCase().includes(q))
      );
    }
    if (filter === 'unread') list = list.filter((c) => c.unread > 0);
    if (filter === 'ki')     list = list.filter((c) => c.aiEnabled && !c.aiPaused);
    if (filter === 'human')  list = list.filter((c) => c.aiPaused || !c.aiEnabled);
    if (filter === 'requests') list = list.filter((c) => c.inMessageRequests);
    // Sort: pinned first (by pinnedAt desc), then by last message
    list = list.slice().sort((a, b) => {
      if (a.pinned && !b.pinned) return -1;
      if (!a.pinned && b.pinned) return 1;
      return (b.lastMessageAt || 0) - (a.lastMessageAt || 0);
    });
    return list;
  }, [chats, query, filter]);

  const activeChat = chats.find((c) => c.id === activeId);

  const selectChat = (id) => {
    // unread lokal sofort auf 0 (UI feedback) + DB-Update fire-and-forget
    setChats((cs) => cs.map((c) => c.id === id ? { ...c, unread: 0 } : c));
    setActiveId(id);
    setShowChat(true);
    db.markChatRead(id).catch(() => {});
  };

  // --- Phase 3: echte Schreib-Operationen ----------------------------------

  // Composer-Send mit Optimistic UI.
  // Pattern: id = corr_id, n8n schreibt INSERT mit dieser id, Realtime-INSERT
  // matched per id → optimistic-Row wird ueberschrieben.
  const sendMessage = async (text) => {
    if (!activeId || !text || !text.trim()) return;
    const corrId = db.newCorrId();
    const optimistic = {
      id:       corrId,
      from:     'us',
      author:   'staff',
      text,
      t:        Date.now(),
      _sending: true,
    };
    setChats((cs) => cs.map((c) => c.id === activeId
      ? { ...c, messages: [...c.messages, optimistic] }
      : c));

    try {
      await db.sendOutbound(activeId, text, corrId, user.id);
      // Erfolg: warten auf Realtime-UPDATE auf status='sent', der den
      // _sending-Flag im Subscribe-Handler clearet.
    } catch (err) {
      console.error('[app] sendOutbound failed:', err);
      setChats((cs) => cs.map((c) => {
        if (c.id !== activeId) return c;
        return {
          ...c,
          messages: c.messages.map((m) =>
            m.id === corrId
              ? { ...m, _sending: false, _error: err?.message || 'Senden fehlgeschlagen' }
              : m
          ),
        };
      }));
    }
  };

  const toggleAI = async (enabled) => {
    if (!activeId || !user) return;
    setChats((cs) => cs.map((c) => c.id === activeId ? { ...c, aiEnabled: !!enabled } : c));
    try {
      await db.setChatAIEnabled(activeId, enabled, user.id, user.account_id);
    } catch (err) {
      console.error('[app] toggleAI failed:', err);
      // Revert
      setChats((cs) => cs.map((c) => c.id === activeId ? { ...c, aiEnabled: !enabled } : c));
    }
  };

  const resumeAI = async () => {
    if (!activeId || !user) return;
    setChats((cs) => cs.map((c) => c.id === activeId
      ? { ...c, aiPaused: false, pauseReason: null }
      : c));
    try {
      await db.resumeChatAIManual(activeId, user.id, user.account_id);
    } catch (err) {
      console.error('[app] resumeAI failed:', err);
    }
  };

  const toggleGlobalAI = async (on) => {
    if (!user || !user.account_id) return;
    setGlobalAI(!!on);
    try {
      await db.setGlobalAI(user.account_id, on, user.id);
    } catch (err) {
      console.error('[app] toggleGlobalAI failed:', err);
      setGlobalAI(!on);
    }
  };

  const togglePinChat = async (chatId, pinned) => {
    setChats((cs) => cs.map((c) => c.id === chatId ? { ...c, pinned, pinnedAt: pinned ? Date.now() : null } : c));
    const ok = await db.pinChat(chatId, pinned);
    if (!ok) {
      setChats((cs) => cs.map((c) => c.id === chatId ? { ...c, pinned: !pinned } : c));
    }
  };

  const toggleArchiveChat = async (chatId, archived) => {
    // optimistic remove from current view
    setChats((cs) => cs.filter((c) => c.id !== chatId));
    if (activeId === chatId) {
      setActiveId(null);
      setShowChat(false);
    }
    const ok = await db.archiveChat(chatId, archived);
    if (!ok) {
      // rollback: reload list
      const cs = await db.loadChats({ limit: 500, archived: view === 'archive' });
      setChats(cs);
    }
  };

  const handleDeleteChat = async (chatId) => {
    if (!window.confirm('Chat endgültig löschen? Alle Nachrichten gehen verloren.')) return;
    setChats((cs) => cs.filter((c) => c.id !== chatId));
    if (activeId === chatId) {
      setActiveId(null);
      setShowChat(false);
    }
    const ok = await db.deleteChat(chatId);
    if (!ok) {
      // rollback
      const cs = await db.loadChats({ limit: 500, archived: view === 'archive' });
      setChats(cs);
    }
  };

  const handleTagsChange = async (chatId, tags) => {
    setChats((cs) => cs.map((c) => c.id === chatId ? { ...c, tags } : c));
    const ok = await db.updateChatTags(chatId, tags);
    if (!ok) console.warn('[app] tags persist failed — UI weiter optimistisch');
  };

  const handleNoteChange = async (chatId, note) => {
    setChats((cs) => cs.map((c) => c.id === chatId ? { ...c, staffNote: note || null } : c));
    const ok = await db.updateChatNote(chatId, note);
    if (!ok) console.warn('[app] staff_note persist failed — Migration 0011 noetig');
  };

  // --- Day-Stats (for sidebar banner) -------------------------------------
  const todayStats = useM(() => {
    const todayStart = new Date(); todayStart.setHours(0, 0, 0, 0);
    const ts = todayStart.getTime();
    const today = chats.filter((c) => (c.lastMessageAt || 0) >= ts);
    const aiAnswered = today.filter((c) => c.aiEnabled && !c.aiPaused).length;
    const waiting = today.filter((c) => (c.unread || 0) > 0).length;
    return { total: today.length, aiAnswered, waiting };
  }, [chats]);

  // --- Hot-Keys ----------------------------------------------------------
  useE(() => {
    const handler = (e) => {
      const tag = (e.target?.tagName || '').toLowerCase();
      const inField = tag === 'input' || tag === 'textarea' || e.target?.isContentEditable;
      // Cmd+K / Ctrl+K — focus search
      if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'k') {
        e.preventDefault();
        const el = document.querySelector('.search-input');
        if (el) el.focus();
        return;
      }
      if (inField) return;
      // Esc — close chat
      if (e.key === 'Escape' && activeId) {
        setActiveId(null);
        setShowChat(false);
        return;
      }
      if (!activeChat) return;
      if (e.key.toLowerCase() === 'p') {
        e.preventDefault();
        togglePinChat(activeChat.id, !activeChat.pinned);
      } else if (e.key.toLowerCase() === 'e') {
        e.preventDefault();
        toggleArchiveChat(activeChat.id, !activeChat.archived);
      } else if (e.key.toLowerCase() === 'i') {
        e.preventDefault();
        setProfileOpen((v) => !v);
      }
    };
    document.addEventListener('keydown', handler);
    return () => document.removeEventListener('keydown', handler);
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [activeId, activeChat?.id, activeChat?.pinned, activeChat?.archived]);

  const toggleView = () => {
    setView((v) => v === 'inbox' ? 'archive' : 'inbox');
    setActiveId(null);
    setShowChat(false);
  };

  const logout = async () => {
    await db.auth.signOut();
    setUser(null);
    setAccount(null);
    setChats([]);
    setActiveId(null);
  };
  // expose for rail click handler (V2 will give it a real button)
  window.TF_LOGOUT = logout;

  if (bootstrapping) {
    return (
      <div style={{
        height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center',
        color: 'var(--text-dim)', fontSize: 13,
      }}>
        Sitzung wird geladen …
      </div>
    );
  }

  if (!user) {
    return <LoginScreen onLogin={(u) => setUser(u)} />;
  }

  if (user._orphan) {
    return (
      <div style={{
        height: '100vh', display: 'flex', alignItems: 'center', justifyContent: 'center',
        flexDirection: 'column', gap: 16, padding: 32, textAlign: 'center',
      }}>
        <div style={{ color: 'var(--gold)', fontSize: 16, fontWeight: 600 }}>Setup unvollstaendig</div>
        <div style={{ color: 'var(--text-mid)', fontSize: 13, maxWidth: 480, lineHeight: 1.6 }}>
          Auth-User <code>{user.email}</code> hat keinen Eintrag in <code>public.users</code>.
          Migration <code>0003_seed_test_user.sql</code> mit der Auth-User-ID applien (siehe README.md im Repo).
        </div>
        <button className="btn-primary" onClick={logout} style={{ maxWidth: 200 }}>Abmelden</button>
      </div>
    );
  }

  const { Rail, Sidebar, ChatView, EmptyState, CustomerProfile } = window.Inbox;

  return (
    <div className={'app' + (showChat ? ' show-chat' : '') + (profileOpen && activeChat ? ' has-details' : '')}>
      <Rail user={user} globalAI={globalAI} onToggleGlobalAI={toggleGlobalAI} onLogout={logout} view={view} onToggleView={toggleView} />
      <Sidebar
        chats={filteredChats}
        allChats={chats}
        activeId={activeId}
        onSelect={selectChat}
        query={query}
        setQuery={setQuery}
        filter={filter}
        setFilter={setFilter}
        globalAI={globalAI}
        onToggleGlobalAI={toggleGlobalAI}
        accountName={account?.display_name || ''}
        user={user}
        onLogout={logout}
        view={view}
        todayStats={todayStats}
      />
      {activeChat ? (
        <ChatView
          chat={activeChat}
          onSend={sendMessage}
          onToggleAI={toggleAI}
          onResumeAI={resumeAI}
          onBack={() => setShowChat(false)}
          mode={t.aiHandover}
          readOnly={false}
          onPin={(p) => togglePinChat(activeChat.id, p)}
          onArchive={(a) => toggleArchiveChat(activeChat.id, a)}
          onDelete={() => handleDeleteChat(activeChat.id)}
          onTagsChange={(tags) => handleTagsChange(activeChat.id, tags)}
          onNoteChange={(note) => handleNoteChange(activeChat.id, note)}
          profileOpen={profileOpen}
          onToggleProfile={() => setProfileOpen((v) => !v)}
        />
      ) : (
        <EmptyState empty={chats.length === 0} archived={view === 'archive'} />
      )}
      {activeChat && profileOpen && (
        <CustomerProfile
          chat={activeChat}
          onTagsChange={(tags) => handleTagsChange(activeChat.id, tags)}
          onNoteChange={(note) => handleNoteChange(activeChat.id, note)}
          onClose={() => setProfileOpen(false)}
        />
      )}

      {window.TweaksPanel && /(?:^|[?&])tweaks=1(?:&|$)/.test(window.location.search) && (
        <window.TweaksPanel title="Tweaks">
          <window.TweakSection label="Typografie">
            <window.TweakSelect
              label="UI-Schrift"
              value={t.fontUI}
              options={FONT_UI_OPTIONS}
              onChange={(v) => setTweak('fontUI', v)}
            />
            <window.TweakSelect
              label="Display-Schrift"
              value={t.fontDisplay}
              options={FONT_DISPLAY_OPTIONS}
              onChange={(v) => setTweak('fontDisplay', v)}
            />
          </window.TweakSection>
          <window.TweakSection label="KI-Übergabe">
            <window.TweakSelect
              label="Verhalten wenn du antwortest"
              value={t.aiHandover}
              options={HANDOVER_OPTIONS.map((o) => ({ value: o.value, label: o.label }))}
              onChange={(v) => setTweak('aiHandover', v)}
            />
            <div style={{ fontSize: 11, color: 'var(--text-dim, #888)', marginTop: 6, lineHeight: 1.4 }}>
              {HANDOVER_OPTIONS.find((o) => o.value === t.aiHandover)?.desc}
            </div>
          </window.TweakSection>
        </window.TweaksPanel>
      )}
    </div>
  );
}

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