/* NOTES space — preview cards + folder system, wired to the real Supabase backend.
   Real schema: notes(id,title,content[html],folder_id,pinned?,updated_at?,created_at),
   folders(id,name,type='note'). The canonical folder key is folder_id. */

function esc(s) { return String(s).replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;'); }

// Sanitize note HTML before rendering it into the contentEditable body. Note
// content is stored in Supabase; without this a crafted row could run script
// in the vault origin. Uses DOMPurify (loaded in index.html) with a
// conservative DOM-walk fallback if it somehow failed to load.
function sanitizeHtml(html) {
  if (window.DOMPurify) return window.DOMPurify.sanitize(html || '', { USE_PROFILES: { html: true }, FORBID_TAGS: ['style'] });
  const d = document.createElement('div'); d.innerHTML = html || '';
  d.querySelectorAll('script,iframe,object,embed,link,meta,style,svg').forEach((n) => n.remove());
  d.querySelectorAll('*').forEach((el) => {
    [...el.attributes].forEach((a) => {
      const n = a.name.toLowerCase(), v = (a.value || '').replace(/\s+/g, '').toLowerCase();
      if (n.startsWith('on') || (['href', 'src', 'xlink:href'].includes(n) && (v.startsWith('javascript:') || v.startsWith('data:text/html')))) el.removeAttribute(a.name);
    });
  });
  return d.innerHTML;
}
function previewOf(n) {
  const raw = (n.html != null ? n.html.replace(/<[^>]*>/g, ' ') : (n.body || ''));
  return raw.replace(/\s+/g, ' ').trim().slice(0, 90) || 'Empty note';
}
function rowTs(r) { return new Date(r.updated_at || r.created_at || Date.now()).getTime(); }

function SpaceTopbar({ children, tabs, utils }) {
  return React.createElement('header', { className: 'cal-topbar' },
    React.createElement('div', { className: 'cal-brand', style: tabs ? { minWidth: 'auto' } : null },
      React.createElement('span', { className: 'mark' }, React.createElement(Icons.shield, { size: 17 })),
      tabs ? null : React.createElement('span', { className: 'wordmark' }, 'VAULT')),
    tabs || null,
    children,
    utils || null);
}

function NotesSpace({ tabs, utils }) {
  const DB = window.VaultDB;
  const [folders, setFolders] = React.useState([]);   // [{id,name}]
  const [notes, setNotes] = React.useState([]);        // [{id, folder_id, title, html, pinned, ts}]
  const [loaded, setLoaded] = React.useState(false);
  const [activeId, setActiveId] = React.useState(null);
  const [query, setQuery] = React.useState('');
  const [filter, setFilter] = React.useState('all'); // 'all' | 'uncat' | folder_id
  const [status, setStatus] = React.useState('saved');
  const [adding, setAdding] = React.useState(false);
  const [newName, setNewName] = React.useState('');
  const [dragId, setDragId] = React.useState(null);
  const [dropTarget, setDropTarget] = React.useState(null);
  const [moveOpen, setMoveOpen] = React.useState(false);
  const bodyRef = React.useRef(null);
  const saveTimer = React.useRef(null);
  const titleTimer = React.useRef(null);
  const aiBusy = React.useRef(false);

  const fromRow = (r) => ({ id: r.id, folder_id: r.folder_id || null, title: r.title || '', html: r.content || '', pinned: !!r.pinned, ts: rowTs(r) });

  React.useEffect(() => {
    let alive = true;
    (async () => {
      try {
        const [fs, ns] = await Promise.all([DB.folders.list('note'), DB.notes.list()]);
        if (!alive) return;
        setFolders(fs.map(f => ({ id: f.id, name: f.name })));
        setNotes(ns.map(fromRow));
        setLoaded(true);
      } catch (e) { setLoaded(true); }
    })();
    const unsub = DB.subscribe((table) => {
      if (table === 'notes') DB.notes.list().then(ns => setNotes(ns.map(fromRow))).catch(() => {});
      if (table === 'folders') DB.folders.list('note').then(fs => setFolders(fs.map(f => ({ id: f.id, name: f.name })))).catch(() => {});
    });
    return () => { alive = false; unsub(); };
  }, []);

  const active = notes.find(n => n.id === activeId) || null;
  const folderName = (id) => (folders.find(f => f.id === id) || {}).name || '';

  React.useEffect(() => {
    if (active && bodyRef.current) bodyRef.current.innerHTML = sanitizeHtml(active.html || '');
  }, [activeId]);

  const onBodyInput = () => {
    if (!bodyRef.current || !active) return;
    const html = bodyRef.current.innerHTML;
    setNotes(prev => prev.map(n => n.id === activeId ? { ...n, html, ts: Date.now() } : n));
    setStatus('saving');
    clearTimeout(saveTimer.current);
    saveTimer.current = setTimeout(async () => {
      try { await DB.notes.update(activeId, { content: html, updated_at: new Date().toISOString() }); setStatus('saved'); }
      catch (e) { setStatus('saved'); }
    }, 600);
  };
  const fmt = (cmd, val) => { document.execCommand(cmd, false, val); if (bodyRef.current) { bodyRef.current.focus(); onBodyInput(); } };

  // ── SECRET in-note AI assistant ───────────────────────────────────
  // Write a line that begins with `myai` followed by your question, then
  // press Cmd/Ctrl+Enter. The answer is inserted directly below. No visible
  // button — the trigger is the keyword + hotkey, known only to you.
  const currentBlock = () => {
    const root = bodyRef.current;
    const sel = window.getSelection();
    if (!root || !sel || !sel.rangeCount) return root;
    let n = sel.anchorNode;
    if (n && n.nodeType === 3) n = n.parentNode;
    while (n && n !== root && n.parentNode !== root) n = n.parentNode;
    return (n && n !== root) ? n : root;
  };
  const MYAI_RE = /\bmyai\b[\s:>\-]*(.+)/i;
  const runMyAI = async () => {
    const root = bodyRef.current;
    if (!root || aiBusy.current) return;

    // 1) Prefer the block the caret is on. 2) Otherwise find the LAST block
    // that contains a `myai …` line anywhere in the note. So it works no
    // matter where your cursor is.
    let block = currentBlock();
    let question = '';
    let lineText = (block === root ? '' : (block.textContent || ''));
    let m = lineText.match(MYAI_RE);
    if (m) { question = m[1].trim(); }
    else {
      const blocks = root === block ? [root] : Array.from(root.children);
      for (let i = blocks.length - 1; i >= 0; i--) {
        const mm = (blocks[i].textContent || '').match(MYAI_RE);
        if (mm && mm[1].trim()) { block = blocks[i]; question = mm[1].trim(); break; }
      }
      // last resort: scan the whole note's plain text line-by-line
      if (!question) {
        const lines = (root.innerText || '').split('\n');
        for (let i = lines.length - 1; i >= 0; i--) {
          const mm = lines[i].match(MYAI_RE);
          if (mm && mm[1].trim()) { question = mm[1].trim(); block = root; break; }
        }
      }
    }
    if (!question) return;                  // no `myai …` found — stay silent

    aiBusy.current = true;
    const ans = document.createElement('p');
    ans.className = 'myai-answer';
    ans.textContent = 'Thinking…';
    if (block === root) root.appendChild(ans);
    else block.insertAdjacentElement('afterend', ans);
    onBodyInput();
    try {
      const ctx = (root.innerText || '').slice(0, 4000);
      const r = await fetch('/api/ai', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json', ...(window.VaultDB.sessionHeaders ? window.VaultDB.sessionHeaders() : {}) },
        body: JSON.stringify({ prompt: question, context: ctx }),
      });
      const d = await r.json().catch(() => ({}));
      ans.textContent = d.text || d.error || 'No answer.';
    } catch (e) {
      ans.textContent = 'AI unavailable — check your connection.';
    }
    aiBusy.current = false;
    onBodyInput();
  };
  // Trigger: Cmd/Ctrl+Enter anywhere in the note body.
  const onBodyKeyDown = (e) => {
    if ((e.metaKey || e.ctrlKey) && e.key === 'Enter') { e.preventDefault(); runMyAI(); }
  };

  const setTitle = (title) => {
    setNotes(prev => prev.map(n => n.id === activeId ? { ...n, title } : n));
    setStatus('saving');
    clearTimeout(titleTimer.current);
    titleTimer.current = setTimeout(async () => {
      try { await DB.notes.update(activeId, { title, updated_at: new Date().toISOString() }); setStatus('saved'); }
      catch (e) { setStatus('saved'); }
    }, 400);
  };

  const newNote = async () => {
    const folder_id = (filter !== 'all' && filter !== 'uncat') ? filter : null;
    try {
      const row = await DB.notes.create({ folder_id, title: '', content: '' });
      const n = fromRow(row);
      setNotes(prev => [n, ...prev]); setActiveId(n.id);
    } catch (e) {}
  };
  const deleteNote = async () => {
    if (!active) return;
    if (!confirm('Delete "' + (active.title || 'Untitled') + '"? This cannot be undone.')) return;
    const id = active.id;
    setNotes(prev => prev.filter(n => n.id !== id)); setActiveId(null);
    try { await DB.notes.remove(id); } catch (e) {}
  };
  const togglePin = async (id, e) => {
    e.stopPropagation();
    const n = notes.find(x => x.id === id); if (!n) return;
    const pinned = !n.pinned;
    setNotes(prev => prev.map(x => x.id === id ? { ...x, pinned } : x));
    try { await DB.notes.update(id, { pinned }); } catch (e2) {}
  };
  const moveNote = async (id, folder_id) => {
    setNotes(prev => prev.map(n => n.id === id ? { ...n, folder_id, ts: Date.now() } : n));
    try { await DB.notes.update(id, { folder_id, updated_at: new Date().toISOString() }); } catch (e) {}
  };

  const addFolder = async () => {
    const name = newName.trim();
    if (!name) { setAdding(false); setNewName(''); return; }
    setNewName(''); setAdding(false);
    if (folders.some(f => f.name.toLowerCase() === name.toLowerCase())) return;
    try {
      const f = await DB.folders.create(name, 'note');
      setFolders(prev => [...prev, { id: f.id, name: f.name }]); setFilter(f.id);
    } catch (e) {}
  };
  const renameFolder = async (f) => {
    const next = prompt('Rename folder', f.name);
    if (next && next.trim() && next.trim() !== f.name) {
      const nm = next.trim();
      setFolders(prev => prev.map(x => x.id === f.id ? { ...x, name: nm } : x));
      try { await DB.folders.rename(f.id, nm); } catch (e) {}
    }
  };
  const deleteFolder = async (f, e) => {
    e.stopPropagation();
    if (!confirm('Delete folder "' + f.name + '"? Notes inside move to Inbox.')) return;
    setFolders(prev => prev.filter(x => x.id !== f.id));
    setNotes(prev => prev.map(n => n.folder_id === f.id ? { ...n, folder_id: null } : n));
    if (filter === f.id) setFilter('all');
    try { await DB.folders.remove(f.id); } catch (e2) {}
  };

  const q = query.trim().toLowerCase();
  const shown = notes
    .filter(n => filter === 'all' ? true : filter === 'uncat' ? !n.folder_id : n.folder_id === filter)
    .filter(n => !q || (n.title || '').toLowerCase().includes(q) || previewOf(n).toLowerCase().includes(q))
    .sort((a, b) => (b.pinned - a.pinned) || (b.ts - a.ts));

  const countFor = (id) => id === 'all' ? notes.length : id === 'uncat' ? notes.filter(n => !n.folder_id).length : notes.filter(n => n.folder_id === id).length;
  const text = active ? (active.html || '').replace(/<[^>]*>/g, ' ') : '';
  const wordCount = text.trim() ? text.trim().split(/\s+/).filter(Boolean).length : 0;
  const charCount = text.replace(/\s/g, '').length;

  const tbBtn = (title, icon, cmd, val) => React.createElement('button', {
    className: 'tb-btn', title, onMouseDown: (e) => { e.preventDefault(); fmt(cmd, val); },
  }, React.createElement(Icons[icon], { size: 14 }));

  const chip = (id, label, removable, folderObj) => React.createElement('button', {
    key: id,
    className: 'fchip' + (filter === id ? ' on' : '') + (dropTarget === id ? ' drop' : ''),
    onClick: () => setFilter(id),
    onDoubleClick: folderObj ? () => renameFolder(folderObj) : null,
    onDragOver: (e) => { if (dragId) { e.preventDefault(); setDropTarget(id); } },
    onDragLeave: () => setDropTarget(t => t === id ? null : t),
    onDrop: (e) => { e.preventDefault(); if (dragId) moveNote(dragId, (id === 'uncat' || id === 'all') ? null : id); setDragId(null); setDropTarget(null); },
  },
    folderObj ? React.createElement(Icons.folder, { size: 12 }) : null,
    label,
    React.createElement('span', { className: 'fchip-n' }, countFor(id)),
    removable ? React.createElement('span', { className: 'fchip-x', onClick: (e) => deleteFolder(folderObj, e), title: 'Delete folder' }, React.createElement(Icons.x, { size: 11 })) : null);

  return React.createElement('div', { className: 'space-root' },
    React.createElement(SpaceTopbar, { tabs, utils },
      React.createElement('div', { className: 'cal-spacer' }),
      React.createElement('button', { className: 'btn-primary', onClick: newNote },
        React.createElement(Icons.plus, { size: 15, sw: 2.5 }), 'New note')),
    React.createElement('div', { className: 'space-body notes-body' },
      React.createElement('aside', { className: 'notes-side' },
        React.createElement('div', { className: 'side-search' },
          React.createElement(Icons.search, { size: 14 }),
          React.createElement('input', { placeholder: 'Search notes', value: query, onChange: (e) => setQuery(e.target.value) })),
        React.createElement('div', { className: 'folder-head' },
          React.createElement('span', { className: 'vault-label' }, 'Folders'),
          React.createElement('button', { className: 'folder-add-btn', title: 'New folder', onClick: () => setAdding(true) },
            React.createElement(Icons.folderPlus, { size: 14 }))),
        React.createElement('div', { className: 'folder-chips' },
          chip('all', 'All', false, null),
          chip('uncat', 'Inbox', false, null),
          folders.map(f => chip(f.id, f.name, true, f)),
          adding
            ? React.createElement('input', {
                className: 'folder-new-input', autoFocus: true, placeholder: 'Folder name…', value: newName,
                onChange: (e) => setNewName(e.target.value),
                onKeyDown: (e) => { if (e.key === 'Enter') addFolder(); if (e.key === 'Escape') { setAdding(false); setNewName(''); } },
                onBlur: addFolder,
              })
            : null),
        React.createElement('div', { className: 'note-cards' },
          !loaded
            ? React.createElement('div', { className: 'note-cards-empty' }, 'Loading…')
            : shown.length === 0
            ? React.createElement('div', { className: 'note-cards-empty' }, q ? 'No matches' : 'No notes here yet')
            : shown.map(n => React.createElement('div', {
                key: n.id, className: 'note-card' + (n.id === activeId ? ' on' : '') + (dragId === n.id ? ' dragging' : ''),
                draggable: true,
                onDragStart: () => setDragId(n.id),
                onDragEnd: () => { setDragId(null); setDropTarget(null); },
                onClick: () => setActiveId(n.id),
              },
                React.createElement('div', { className: 'note-card-hd' },
                  n.pinned ? React.createElement('span', { className: 'note-pin-dot', title: 'Pinned' }) : null,
                  React.createElement('span', { className: 'note-card-title' }, n.title || 'Untitled'),
                  React.createElement('button', { className: 'note-pin-btn' + (n.pinned ? ' on' : ''), onClick: (e) => togglePin(n.id, e), title: n.pinned ? 'Unpin' : 'Pin' },
                    React.createElement(Icons.pin, { size: 12 })),
                  React.createElement('span', { className: 'note-card-time' }, timeAgo(n.ts))),
                React.createElement('div', { className: 'note-card-prev' }, previewOf(n)),
                n.folder_id ? React.createElement('span', { className: 'note-card-folder' }, folderName(n.folder_id)) : null)))),
      active
        ? React.createElement('main', { className: 'writer' },
            React.createElement('div', { className: 'writer-tools' },
              React.createElement('div', { className: 'writer-toolbar' },
                tbBtn('Bold', 'bold', 'bold'),
                tbBtn('Italic', 'italic', 'italic'),
                tbBtn('Underline', 'underline', 'underline'),
                tbBtn('Strikethrough', 'strike', 'strikeThrough'),
                React.createElement('span', { className: 'tb-sep' }),
                tbBtn('Heading 1', 'h1', 'formatBlock', 'h1'),
                tbBtn('Heading 2', 'h2', 'formatBlock', 'h2'),
                tbBtn('Quote', 'quote', 'formatBlock', 'blockquote'),
                tbBtn('Bullet list', 'listUl', 'insertUnorderedList')),
              React.createElement('div', { style: { display: 'flex', gap: 4 } },
                React.createElement('button', { className: 'writer-del', title: active.pinned ? 'Unpin' : 'Pin', onClick: (e) => togglePin(active.id, e), style: active.pinned ? { color: 'var(--text-strong)' } : null },
                  React.createElement(Icons.pin, { size: 14 })),
                React.createElement('button', { className: 'writer-del', title: 'Delete note', onClick: deleteNote },
                  React.createElement(Icons.trash, { size: 14 })))),
            React.createElement('div', { className: 'writer-scroll' },
              React.createElement('div', { className: 'writer-page' },
                React.createElement('input', {
                  className: 'writer-title', placeholder: 'Untitled note',
                  value: active.title, onChange: (e) => setTitle(e.target.value),
                }),
                React.createElement('div', { className: 'writer-meta' },
                  React.createElement('div', { className: 'move-wrap' },
                    React.createElement('button', { className: 'writer-folder', onClick: () => setMoveOpen(o => !o), title: 'Move to folder' },
                      React.createElement(Icons.folder, { size: 11 }), active.folder_id ? folderName(active.folder_id) : 'Inbox',
                      React.createElement(Icons.chevR, { size: 11, style: { transform: 'rotate(90deg)', opacity: 0.6 } })),
                    moveOpen ? React.createElement('div', { className: 'move-menu', onMouseLeave: () => setMoveOpen(false) },
                      React.createElement('button', { className: 'move-item' + (!active.folder_id ? ' on' : ''), onClick: () => { moveNote(active.id, null); setMoveOpen(false); } }, 'Inbox'),
                      folders.map(f => React.createElement('button', {
                        key: f.id, className: 'move-item' + (active.folder_id === f.id ? ' on' : ''),
                        onClick: () => { moveNote(active.id, f.id); setMoveOpen(false); },
                      }, React.createElement('span', { className: 'mi-dot' }), f.name))) : null),
                  React.createElement('span', { className: 'dotsep' }, '·'),
                  React.createElement('span', null, 'Edited ' + timeAgo(active.ts) + ' ago'),
                  React.createElement('span', { className: 'dotsep' }, '·'),
                  React.createElement('span', null, wordCount + ' words · ' + charCount + ' chars · ' + Math.max(1, Math.round(wordCount / 200)) + ' min'),
                  React.createElement('span', { className: 'writer-save' + (status === 'saving' ? ' busy' : '') },
                    React.createElement(Icons.check, { size: 11, sw: 3 }),
                    status === 'saving' ? 'Saving' : 'Saved')),
                React.createElement('div', {
                  className: 'writer-body', contentEditable: true, suppressContentEditableWarning: true,
                  ref: bodyRef, 'data-placeholder': 'Start writing...', onInput: onBodyInput, onKeyDown: onBodyKeyDown,
                }))))
        : React.createElement('main', { className: 'empty-pane' },
            React.createElement(Icons.fileText, { size: 40, sw: 1.5 }),
            React.createElement('div', { className: 'et' }, 'Select a note or create a new one'),
            React.createElement('div', { className: 'es' }, 'Your ideas are waiting'),
            React.createElement('button', { className: 'btn-primary', style: { marginTop: 8 }, onClick: newNote },
              React.createElement(Icons.plus, { size: 15, sw: 2.5 }), 'New note'))));
}

Object.assign(window, { NotesSpace, SpaceTopbar });
