// Main Wiki Agent app
const { useState, useEffect, useCallback, useRef, useMemo } = React;

function findEntry(book, entryId) {
  for (const ch of book.chapters) {
    const e = ch.entries.find(x => x.id === entryId);
    if (e) return { entry: e, chapter: ch };
  }
  return { entry: book.chapters[0].entries[0], chapter: book.chapters[0] };
}

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "bodyWidth": 720
}/*EDITMODE-END*/;

function App() {
  const book0 = window.WIKI_BOOK;

  const [customChapters, setCustomChapters] = useState([]);
  const [extraEntries, setExtraEntries] = useState({});
  const [titleOverrides, setTitleOverrides] = useState({}); // entryId -> {title, titleEn}

  // Composite book: merge user-created chapters and entries with the base
  const book = useMemo(() => {
    const baseChapters = book0.chapters.map(ch => {
      const extra = (extraEntries[ch.id] || []).map(e => ({ ...e }));
      const merged = [...ch.entries, ...extra].map(e =>
        titleOverrides[e.id] ? { ...e, ...titleOverrides[e.id] } : e
      );
      return { ...ch, entries: merged };
    });
    const custom = customChapters.map(ch => ({
      ...ch,
      entries: (extraEntries[ch.id] || []).map(e =>
        titleOverrides[e.id] ? { ...e, ...titleOverrides[e.id] } : e
      ),
    }));
    return { ...book0, chapters: [...baseChapters, ...custom] };
  }, [book0, customChapters, extraEntries, titleOverrides]);

  const [activeId, setActiveId] = useState(() => {
    return localStorage.getItem('wa:activeId') || book0.chapters[0].entries[0].id;
  });
  const [overrides, setOverrides] = useState({}); // id -> content
  const [chatsByEntry, setChatsByEntry] = useState({}); // id -> messages[]
  const [isEditing, setIsEditing] = useState(false);
  const [quote, setQuote] = useState('');
  const [aiFocusEntryId, setAIFocus] = useState(null);
  const [saveState, setSaveState] = useState('idle'); // idle | saving | saved
  const [openChapters, setOpenChapters] = useState(() => {
    const o = {};
    book0.chapters.forEach((c, i) => { o[c.id] = i < 3; });
    return o;
  });
  const [newlyCreatedId, setNewlyCreatedId] = useState(null);
  const [tweaksOpen, setTweaksOpen] = useState(false);
  const [tweaks, setTweaks] = useState(TWEAK_DEFAULTS);

  // Column widths (persisted)
  const [colLeft, setColLeft] = useState(() => parseInt(localStorage.getItem('wa:colLeft')) || 264);
  const [colRight, setColRight] = useState(() => parseInt(localStorage.getItem('wa:colRight')) || 380);
  const [sbCollapsed, setSbCollapsed] = useState(() => localStorage.getItem('wa:sbCollapsed') === '1');

  useEffect(() => { localStorage.setItem('wa:sbCollapsed', sbCollapsed ? '1' : '0'); }, [sbCollapsed]);

  useEffect(() => {
    document.documentElement.style.setProperty('--col-left', colLeft + 'px');
    localStorage.setItem('wa:colLeft', colLeft);
  }, [colLeft]);
  useEffect(() => {
    document.documentElement.style.setProperty('--col-right', colRight + 'px');
    localStorage.setItem('wa:colRight', colRight);
  }, [colRight]);

  const startResize = (which) => (e) => {
    e.preventDefault();
    const startX = e.clientX;
    const startLeft = colLeft;
    const startRight = colRight;
    const vw = window.innerWidth;
    document.body.classList.add('resizing');
    const handle = e.currentTarget;
    handle.classList.add('dragging');

    const onMove = (ev) => {
      const dx = ev.clientX - startX;
      if (which === 'left') {
        // middle min 320, left min 200 max 480
        const min = 200, max = Math.min(480, vw - startRight - 320 - 12);
        setColLeft(Math.max(min, Math.min(max, startLeft + dx)));
      } else {
        // dragging right handle — right col grows when dx negative
        const min = 300, max = Math.min(620, vw - startLeft - 320 - 12);
        setColRight(Math.max(min, Math.min(max, startRight - dx)));
      }
    };
    const onUp = () => {
      document.removeEventListener('mousemove', onMove);
      document.removeEventListener('mouseup', onUp);
      document.body.classList.remove('resizing');
      handle.classList.remove('dragging');
    };
    document.addEventListener('mousemove', onMove);
    document.addEventListener('mouseup', onUp);
  };

  // Hydrate from IndexedDB
  useEffect(() => {
    (async () => {
      const struct = await WikiDB.getStructure();
      if (struct?.chapters) setCustomChapters(struct.chapters);
      if (struct?.extraEntries) setExtraEntries(struct.extraEntries);
      if (struct?.titleOverrides) setTitleOverrides(struct.titleOverrides);

      // gather all entry ids (including custom)
      const allIds = [
        ...book0.chapters.flatMap(c => c.entries.map(e => e.id)),
        ...Object.values(struct?.extraEntries || {}).flat().map(e => e.id),
      ];
      const ov = {};
      for (const id of allIds) {
        const saved = await WikiDB.getEntry(id);
        if (saved?.content) ov[id] = saved.content;
      }
      setOverrides(ov);

      const chats = await WikiDB.allChats();
      const m = {};
      chats.forEach(c => { m[c.entryId] = c.messages; });
      setChatsByEntry(m);
    })();
  }, []);

  // Persist structure changes
  const structRef = useRef({ customChapters: [], extraEntries: {}, titleOverrides: {} });
  useEffect(() => { structRef.current.customChapters = customChapters; }, [customChapters]);
  useEffect(() => { structRef.current.extraEntries = extraEntries; }, [extraEntries]);
  useEffect(() => { structRef.current.titleOverrides = titleOverrides; }, [titleOverrides]);
  const saveStructure = useCallback(() => {
    WikiDB.putStructure({
      chapters: structRef.current.customChapters,
      extraEntries: structRef.current.extraEntries,
      titleOverrides: structRef.current.titleOverrides,
    });
  }, []);

  // Persist activeId
  useEffect(() => { localStorage.setItem('wa:activeId', activeId); }, [activeId]);

  // Apply body width
  useEffect(() => {
    document.documentElement.style.setProperty('--body-width', tweaks.bodyWidth + 'px');
  }, [tweaks.bodyWidth]);

  // ---- Tweaks protocol ----
  useEffect(() => {
    const onMsg = (e) => {
      if (e.data?.type === '__activate_edit_mode') setTweaksOpen(true);
      if (e.data?.type === '__deactivate_edit_mode') setTweaksOpen(false);
    };
    window.addEventListener('message', onMsg);
    window.parent.postMessage({ type: '__edit_mode_available' }, '*');
    return () => window.removeEventListener('message', onMsg);
  }, []);

  const setTweak = (k, v) => {
    setTweaks(prev => {
      const next = { ...prev, [k]: v };
      window.parent.postMessage({ type: '__edit_mode_set_keys', edits: { [k]: v } }, '*');
      return next;
    });
  };

  // Current entry object (with overrides applied)
  const { entry: rawEntry, chapter } = findEntry(book, activeId);
  const entry = overrides[activeId]
    ? { ...rawEntry, content: overrides[activeId] }
    : rawEntry;

  // Switch entry: clear quote + close edit mode
  const selectEntry = (id) => {
    setActiveId(id);
    setQuote('');
    setIsEditing(false);
  };

  // Save content changes (debounced)
  const saveTimer = useRef(null);
  const onContentChange = useCallback((html) => {
    setOverrides(prev => ({ ...prev, [activeId]: html }));
    setSaveState('saving');
    clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(async () => {
      await WikiDB.putEntry({ id: activeId, content: html, updatedAt: Date.now() });
      setSaveState('saved');
      setTimeout(() => setSaveState('idle'), 1500);
    }, 500);
  }, [activeId]);

  // Chat state for current entry
  const messages = chatsByEntry[activeId] || [];
  const setMessages = useCallback((newMsgs) => {
    setChatsByEntry(prev => {
      const next = { ...prev, [activeId]: newMsgs };
      WikiDB.putChat(activeId, newMsgs);
      return next;
    });
  }, [activeId]);

  // ---- Create new chapter / entry ----
  const addChapter = useCallback(() => {
    const emojis = ['📘', '📕', '📗', '📙', '📔', '📓', '🌱', '🔍', '🎯', '✨'];
    const id = 'ch_' + Date.now().toString(36);
    const num = (book0.chapters.length + customChapters.length + 1);
    const chapter = {
      id,
      title: `新章节 ${num}`,
      titleEn: `Chapter ${num}`,
      emoji: emojis[(num - 1) % emojis.length],
      entries: [],
    };
    setCustomChapters(prev => [...prev, chapter]);
    setExtraEntries(prev => ({ ...prev, [id]: [] }));
    setOpenChapters(prev => ({ ...prev, [id]: true }));
    setTimeout(saveStructure, 50);
    // immediately create a first entry inside + enter edit mode
    setTimeout(() => addEntry(id, { enterEdit: true, defaultTitle: '新条目' }), 100);
  }, [book0, customChapters, saveStructure]);

  const addEntry = useCallback((chapterId, opts = {}) => {
    const { enterEdit = true, defaultTitle = '新条目' } = opts;
    const id = 'e_' + Date.now().toString(36) + '_' + Math.random().toString(36).slice(2, 6);
    const existingCount = (extraEntries[chapterId]?.length || 0)
      + (book0.chapters.find(c => c.id === chapterId)?.entries.length || 0);
    const entry = {
      id,
      title: `${defaultTitle} ${existingCount + 1}`,
      titleEn: 'Untitled',
      content: `<h1>${defaultTitle} ${existingCount + 1}<span class="zh-en">Untitled</span></h1><p class="lede">点击这里开始写你的定义、故事或比喻。</p><p>正文从这里开始。选中任意文字，右下的悬浮工具栏会出现；也可以在右侧直接问 AI。</p>`,
    };
    setExtraEntries(prev => ({
      ...prev,
      [chapterId]: [...(prev[chapterId] || []), entry],
    }));
    setOpenChapters(prev => ({ ...prev, [chapterId]: true }));
    setActiveId(id);
    setQuote('');
    setNewlyCreatedId(id);
    setTimeout(() => setNewlyCreatedId(null), 2000);
    if (enterEdit) setIsEditing(true);
    setTimeout(saveStructure, 50);
  }, [book0, extraEntries, saveStructure]);

  // Selection from article → quote chip
  const handleSelectionQuote = useCallback((text) => {
    setQuote(text);
  }, []);

  // Floating toolbar actions → send a message with intent
  const chatSendRef = useRef(null);
  const handleAskAIAboutSelection = useCallback((text, intent) => {
    // inject quote then emit a synthetic send by updating state
    setQuote(text);
    // small delay to let quote render, then send via Chat's imperative handler
    setTimeout(() => {
      if (chatSendRef.current) chatSendRef.current(intent);
    }, 50);
  }, []);

  // Apply a suggestion to the current article
  const applySuggestion = useCallback((sugg) => {
    const current = overrides[activeId] || rawEntry.content;
    let updated = current;
    if (sugg.apply === 'replace-selection' && sugg.original) {
      // escape HTML-ish chars in the needle for a simple .replace
      const idx = current.indexOf(sugg.original);
      if (idx >= 0) {
        updated = current.slice(0, idx) + sugg.replacement + current.slice(idx + sugg.original.length);
      } else {
        // fallback: append
        updated = current + '\n' + `<p>${sugg.replacement}</p>`;
      }
    } else if (sugg.apply === 'insert-after-selection' && sugg.original) {
      const idx = current.indexOf(sugg.original);
      if (idx >= 0) {
        const end = idx + sugg.original.length;
        updated = current.slice(0, end) + `<p><i>${sugg.replacement}</i></p>` + current.slice(end);
      } else {
        updated = current + `<p><i>${sugg.replacement}</i></p>`;
      }
    } else if (sugg.apply === 'append-to-end') {
      updated = current + sugg.replacement;
    }
    setOverrides(prev => ({ ...prev, [activeId]: updated }));
    WikiDB.putEntry({ id: activeId, content: updated, updatedAt: Date.now() });
    setSaveState('saved');
    setTimeout(() => setSaveState('idle'), 1500);
    // Flash the content
    setTimeout(() => window.dispatchEvent(new CustomEvent('ai-applied')), 30);
  }, [activeId, overrides, rawEntry.content]);

  // Build chat history index (entries with saved chats)
  const allChatsIndex = useMemo(() => {
    const out = [];
    for (const ch of book.chapters) {
      for (const e of ch.entries) {
        const msgs = chatsByEntry[e.id];
        if (msgs && msgs.length > 0) {
          const last = msgs[msgs.length - 1];
          const preview = last.text.replace(/<[^>]*>/g, '').slice(0, 30);
          out.push({
            entryId: e.id,
            title: e.title,
            count: msgs.length,
            preview: preview || '（无预览）',
            ts: last.ts || 0,
          });
        }
      }
    }
    out.sort((a, b) => b.ts - a.ts);
    return out;
  }, [chatsByEntry, book]);

  return (
    <div className={'app' + (sbCollapsed ? ' sb-collapsed' : '')}>
      {sbCollapsed && (
        <button className="sb-expand-btn" onClick={() => setSbCollapsed(false)} title="展开侧栏">
          <Icon.PanelLeft size={15} />
        </button>
      )}
      <Sidebar
        book={book}
        activeEntryId={activeId}
        onSelect={selectEntry}
        aiFocusEntryId={aiFocusEntryId}
        openChapters={openChapters}
        setOpenChapters={setOpenChapters}
        onCollapse={() => setSbCollapsed(true)}
        onAddChapter={addChapter}
        onAddEntry={addEntry}
        newlyCreatedId={newlyCreatedId}
      />
      <div className="resizer resizer-left" onMouseDown={startResize('left')} onDoubleClick={() => setColLeft(264)} title="拖动调整 · 双击恢复"></div>
      <Article
        entry={entry}
        chapter={chapter}
        isEditing={isEditing}
        onToggleEdit={() => setIsEditing(v => !v)}
        onContentChange={onContentChange}
        onSelectionQuote={handleSelectionQuote}
        onAskAIAboutSelection={handleAskAIAboutSelection}
        saveState={saveState}
      />
      <div className="resizer" onMouseDown={startResize('right')} onDoubleClick={() => setColRight(380)} title="拖动调整 · 双击恢复"></div>
      <Chat
        entry={entry}
        messages={messages}
        onMessagesChange={setMessages}
        quote={quote}
        onClearQuote={() => setQuote('')}
        allChatsIndex={allChatsIndex}
        onJumpToEntry={selectEntry}
        onApplySuggestion={applySuggestion}
        onAIFocus={setAIFocus}
        registerSender={(fn) => { chatSendRef.current = fn; }}
      />

      {tweaksOpen && (
        <div className="tweaks-panel">
          <h4>
            Tweaks
            <button onClick={() => setTweaksOpen(false)}>×</button>
          </h4>
          <div className="tweaks-row">
            <span className="tweaks-label">正文宽度</span>
            <input
              type="range"
              className="tweaks-slider"
              min="560" max="920" step="20"
              value={tweaks.bodyWidth}
              onChange={e => setTweak('bodyWidth', parseInt(e.target.value))}
            />
            <span className="tweaks-value">{tweaks.bodyWidth}px</span>
          </div>
        </div>
      )}
    </div>
  );
}

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