/* global React, Icon, Badge, IconBtn, useLiveQuery, useReceiveFrom, useSendTo, SendToButton, relTime, db */
const { useState, useEffect, useRef, useMemo, useCallback } = React;

// ============================================================
// Tiny markdown renderer — covers what users actually write:
//   # / ## / ###  headings
//   **bold** *italic* `code`
//   - / 1.  lists
//   ```code blocks```
//   > quotes
//   [text](url) and [[wiki-link]]
// ============================================================
function escHTML(s) {
  return s.replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
}

function renderMarkdown(src, { onWikiLinkClick } = {}) {
  if (!src) return '';
  // Extract code blocks first to keep them safe
  const codeBlocks = [];
  let s = src.replace(/```([\w-]*)\n([\s\S]*?)```/g, (_, lang, code) => {
    const i = codeBlocks.length;
    codeBlocks.push({ lang, code });
    return `\u0000CODE${i}\u0000`;
  });

  s = escHTML(s);

  // Inline code
  s = s.replace(/`([^`\n]+)`/g, '<code class="md-inline-code">$1</code>');
  // Bold/italic
  s = s.replace(/\*\*([^*\n]+)\*\*/g, '<strong>$1</strong>');
  s = s.replace(/(^|[^*])\*([^*\n]+)\*/g, '$1<em>$2</em>');
  // Wiki-links [[Title]]
  s = s.replace(/\[\[([^\]]+)\]\]/g, '<a class="md-wiki" data-wiki="$1">$1</a>');
  // Markdown links [text](url)
  s = s.replace(/\[([^\]]+)\]\(([^)\s]+)\)/g, '<a href="$2" target="_blank" rel="noopener">$1</a>');

  // Block-level: split by blank lines, then process each paragraph
  const blocks = s.split(/\n{2,}/);
  const out = [];

  for (let block of blocks) {
    if (block.startsWith('\u0000CODE')) {
      const i = parseInt(block.match(/\u0000CODE(\d+)\u0000/)[1], 10);
      const cb = codeBlocks[i];
      out.push(`<pre class="md-code"><code data-lang="${cb.lang || ''}">${escHTML(cb.code)}</code></pre>`);
      continue;
    }
    const lines = block.split('\n');
    // Heading?
    if (/^#{1,6} /.test(lines[0])) {
      const m = lines[0].match(/^(#{1,6}) (.*)/);
      out.push(`<h${m[1].length} class="md-h${m[1].length}">${m[2]}</h${m[1].length}>`);
      if (lines.length > 1) out.push(`<p>${lines.slice(1).join('<br>')}</p>`);
      continue;
    }
    // Quote
    if (lines.every(l => /^>\s?/.test(l))) {
      out.push(`<blockquote class="md-quote">${lines.map(l => l.replace(/^>\s?/, '')).join('<br>')}</blockquote>`);
      continue;
    }
    // Ordered list
    if (lines.every(l => /^\d+\.\s/.test(l))) {
      out.push('<ol class="md-list">' + lines.map(l => `<li>${l.replace(/^\d+\.\s/, '')}</li>`).join('') + '</ol>');
      continue;
    }
    // Unordered list
    if (lines.every(l => /^[-*]\s/.test(l))) {
      out.push('<ul class="md-list">' + lines.map(l => `<li>${l.replace(/^[-*]\s/, '')}</li>`).join('') + '</ul>');
      continue;
    }
    // HR
    if (lines.length === 1 && /^---+$/.test(lines[0])) { out.push('<hr class="md-hr">'); continue; }
    // Paragraph
    out.push(`<p>${lines.join('<br>')}</p>`);
  }

  return out.join('');
}

function MarkdownView({ src, onWikiLinkClick }) {
  const ref = useRef(null);
  useEffect(() => {
    if (!ref.current) return;
    const handler = (e) => {
      const a = e.target.closest('a.md-wiki');
      if (!a) return;
      e.preventDefault();
      onWikiLinkClick && onWikiLinkClick(a.getAttribute('data-wiki'));
    };
    ref.current.addEventListener('click', handler);
    return () => ref.current?.removeEventListener('click', handler);
  }, [onWikiLinkClick]);
  return (
    <div
      ref={ref}
      className="md-view"
      dangerouslySetInnerHTML={{ __html: renderMarkdown(src) }}
    />
  );
}

// ============================================================
// Backlink scanner
// ============================================================
function findBacklinks(notes, currentTitle) {
  if (!currentTitle) return [];
  return notes.filter(n => {
    if (!n.body) return false;
    const re = new RegExp('\\[\\[' + currentTitle.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') + '\\]\\]', 'i');
    return re.test(n.body);
  });
}

// ============================================================
// Note Tool
// ============================================================
function NoteTool() {
  const notes = useLiveQuery('notes', { sort: ['updatedAt', 'desc'] });
  const [selectedId, setSelectedId] = useState(null);
  const [folder, setFolder] = useState(null);
  const [search, setSearch] = useState('');
  const [mode, setMode] = useState('split'); // 'edit' | 'preview' | 'split'
  const [sidebarCollapsed, toggleSidebar] = useToolSidebar('note');
  const { send } = useSendTo();
  const editorRef = useRef(null);

  // Receive payload (e.g. send-from another tool: create a new note from text)
  useReceiveFrom('note', (payload) => {
    if (payload?.selectId) {
      setSelectedId(payload.selectId);
      return;
    }
    if (payload?.text || payload?.title) {
      const id = db.uid('notes_');
      db.notes.put({
        id,
        title: payload.title || 'Captured ' + new Date().toLocaleString(),
        body: payload.text || '',
        folder: 'Inbox',
        format: 'markdown',
        tags: [],
      });
      setSelectedId(id);
    }
  });

  // Auto-select first note
  useEffect(() => {
    if (notes && notes.length > 0 && !selectedId) setSelectedId(notes[0].id);
  }, [notes, selectedId]);

  const folders = useMemo(() => {
    const set = new Set();
    (notes || []).forEach(n => { if (n.folder) set.add(n.folder); });
    return [...set].sort();
  }, [notes]);

  const filtered = useMemo(() => {
    if (!notes) return [];
    return notes.filter(n => {
      if (folder && n.folder !== folder) return false;
      if (search) {
        const q = search.toLowerCase();
        if (!n.title.toLowerCase().includes(q) && !(n.body || '').toLowerCase().includes(q) &&
            !(n.tags || []).some(t => t.toLowerCase().includes(q))) return false;
      }
      return true;
    });
  }, [notes, folder, search]);

  const selected = useMemo(() => (notes || []).find(n => n.id === selectedId), [notes, selectedId]);
  const backlinks = useMemo(() => selected ? findBacklinks((notes || []).filter(n => n.id !== selected.id), selected.title) : [], [notes, selected]);

  // Local draft state so the cursor doesn't jump when the live
  // controlled value briefly lags behind keystrokes
  const [titleDraft, setTitleDraft] = useState('');
  const [bodyDraft, setBodyDraft] = useState('');
  const lastSyncedIdRef = useRef(null);
  useEffect(() => {
    if (!selected) { setTitleDraft(''); setBodyDraft(''); lastSyncedIdRef.current = null; return; }
    if (lastSyncedIdRef.current !== selected.id) {
      // Switched notes — reset drafts from db
      setTitleDraft(selected.title || '');
      setBodyDraft(selected.body || '');
      lastSyncedIdRef.current = selected.id;
    }
  }, [selected]);

  const create = useCallback(() => {
    const id = db.uid('notes_');
    db.notes.put({
      id, title: 'Untitled', body: '', folder: folder || 'Inbox',
      format: 'markdown', tags: [],
    });
    setSelectedId(id);
    setTimeout(() => editorRef.current?.focus(), 100);
  }, [folder]);

  const updateNote = (patch) => {
    if (!selected) return;
    db.notes.put({ ...selected, ...patch });
  };

  const deleteNote = async () => {
    if (!selected) return;
    const ok = await window.lhDialog.confirm({
      title: 'Delete this note?',
      message: `"${selected.title || 'Untitled'}" will be permanently removed.`,
      confirmLabel: 'Delete',
      danger: true,
      icon: 'delete_forever',
    });
    if (!ok) return;
    db.notes.delete(selected.id);
    setSelectedId(null);
  };

  // Wiki link click → open note by title (case-insensitive); create if missing
  const onWikiLinkClick = (title) => {
    const found = (notes || []).find(n => n.title.toLowerCase() === title.toLowerCase());
    if (found) setSelectedId(found.id);
    else {
      const id = db.uid('notes_');
      db.notes.put({ id, title, body: '', folder: 'Inbox', format: 'markdown', tags: [] });
      setSelectedId(id);
    }
  };

  // Selection-aware send-to
  const [selectionText, setSelectionText] = useState('');
  const onSelectionChange = useCallback(() => {
    const sel = window.getSelection?.();
    setSelectionText(sel ? sel.toString() : '');
  }, []);
  useEffect(() => {
    document.addEventListener('selectionchange', onSelectionChange);
    return () => document.removeEventListener('selectionchange', onSelectionChange);
  }, [onSelectionChange]);

  // Keyboard: ctrl+b/i for rich text in textarea
  const wrapSelection = (before, after = before) => {
    const ta = editorRef.current;
    if (!ta || !selected) return;
    const start = ta.selectionStart, end = ta.selectionEnd;
    const value = ta.value;
    const sel = value.slice(start, end);
    const next = value.slice(0, start) + before + sel + after + value.slice(end);
    setBodyDraft(next);
    updateNote({ body: next });
    setTimeout(() => {
      ta.focus();
      ta.setSelectionRange(start + before.length, end + before.length);
    }, 0);
  };

  return (
    <div className="tool">
      <div className={`tool-twopane note-twopane ${sidebarCollapsed ? 'is-list-collapsed' : ''}`}>
        {/* List */}
        <div className="tool-list">
          <div className="tool-list-collapsed-rail" onClick={toggleSidebar} title="Expand sidebar">
            <Icon name="chevron_right" />
          </div>
          <div className="tool-list-head">
            <h2>Notes</h2>
            <span className="count">{filtered.length}</span>
            <IconBtn name="add" title="New note" onClick={create} />
            <ToolSidebarToggle collapsed={sidebarCollapsed} onToggle={toggleSidebar} />
          </div>

          {folders.length > 0 && (
            <div className="note-folders">
              <button className={`note-folder ${!folder ? 'is-active' : ''}`} onClick={() => setFolder(null)}>
                <Icon name="all_inbox" />
                <span>All notes</span>
                <span className="count">{(notes || []).length}</span>
              </button>
              {folders.map(f => (
                <button key={f} className={`note-folder ${folder === f ? 'is-active' : ''}`} onClick={() => setFolder(f)}>
                  <Icon name="folder" />
                  <span>{f}</span>
                  <span className="count">{(notes || []).filter(n => n.folder === f).length}</span>
                </button>
              ))}
            </div>
          )}

          <div className="tool-list-search">
            <Icon name="search" />
            <input placeholder="Search notes…" value={search} onChange={e => setSearch(e.target.value)} />
          </div>

          <div className="tool-list-scroll">
            {filtered.length === 0 ? (
              <div className="lh-empty" style={{ padding: '40px 16px' }}>
                <Icon name="description" />
                <div className="title">No notes here</div>
                <div className="sub">Press <kbd>+</kbd> to create one.</div>
              </div>
            ) : filtered.map(n => (
              <NoteListItem
                key={n.id}
                note={n}
                selected={selectedId === n.id}
                onSelect={() => setSelectedId(n.id)}
              />
            ))}
          </div>
        </div>

        {/* Editor */}
        <div className="tool-detail">
          {selected ? (
            <>
              <div className="tool-detail-head">
                <input
                  className="note-title-input"
                  value={titleDraft}
                  placeholder="Untitled"
                  onChange={e => { setTitleDraft(e.target.value); updateNote({ title: e.target.value }); }}
                />
                  onChange={e => updateNote({ title: e.target.value })}
                />
                <div style={{ flex: 1 }} />
                <div className="lh-segmented">
                  <button className={mode === 'edit' ? 'is-active' : ''} onClick={() => setMode('edit')} title="Edit only"><Icon name="edit" /></button>
                  <button className={mode === 'split' ? 'is-active' : ''} onClick={() => setMode('split')} title="Split"><Icon name="vertical_split" /></button>
                  <button className={mode === 'preview' ? 'is-active' : ''} onClick={() => setMode('preview')} title="Preview"><Icon name="visibility" /></button>
                </div>
                <SendToButton
                  payload={{ text: selectionText || selected.body, title: selected.title }}
                  label={selectionText ? 'Send selection' : 'Send body'}
                />
                <IconBtn name="delete" tone="danger" title="Delete note" onClick={deleteNote} />
              </div>

              <div className="note-meta-bar">
                <input
                  className="note-meta-input"
                  placeholder="Folder"
                  value={selected.folder || ''}
                  onChange={e => updateNote({ folder: e.target.value || null })}
                />
                <span className="sep">·</span>
                <input
                  className="note-meta-input wide"
                  placeholder="tags, comma separated"
                  value={(selected.tags || []).join(', ')}
                  onChange={e => updateNote({ tags: e.target.value.split(',').map(s => s.trim()).filter(Boolean) })}
                />
                <div style={{ flex: 1 }} />
                <button className="lh-chip" onClick={() => wrapSelection('**')} title="Bold (Ctrl+B)"><b>B</b></button>
                <button className="lh-chip" onClick={() => wrapSelection('*')} title="Italic"><i>I</i></button>
                <button className="lh-chip" onClick={() => wrapSelection('`')} title="Inline code"><Icon name="code" /></button>
                <button className="lh-chip" onClick={() => wrapSelection('[[', ']]')} title="Wiki link"><Icon name="link" /></button>
                <span className="note-meta-time">Updated {relTime(selected.updatedAt)}</span>
              </div>

              <div className={`note-editor mode-${mode}`}>
                {(mode === 'edit' || mode === 'split') && (
                  <textarea
                    ref={editorRef}
                    className="note-textarea"
                    value={bodyDraft}
                    placeholder={'Start writing…\n\n# Heading\n**bold** *italic* `code`\n- list item\n\n[[Link to another note]]'}
                    onChange={e => { setBodyDraft(e.target.value); updateNote({ body: e.target.value }); }}
                    onKeyDown={e => {
                      if (e.metaKey || e.ctrlKey) {
                        if (e.key === 'b') { e.preventDefault(); wrapSelection('**'); }
                        if (e.key === 'i') { e.preventDefault(); wrapSelection('*'); }
                      }
                    }}
                    draggable
                    onDragStart={(e) => {
                      e.dataTransfer.setData('application/x-lifehub', JSON.stringify({ type: 'note', id: selected.id, title: selected.title }));
                    }}
                  />
                )}
                {(mode === 'preview' || mode === 'split') && (
                  <div className="note-preview">
                    <MarkdownView src={bodyDraft || '_No content yet._'} onWikiLinkClick={onWikiLinkClick} />
                    {backlinks.length > 0 && (
                      <div className="note-backlinks">
                        <div className="note-backlinks-head">
                          <Icon name="link" />
                          <span>Linked from</span>
                          <span className="count">{backlinks.length}</span>
                        </div>
                        {backlinks.map(b => (
                          <button
                            key={b.id}
                            className="note-backlink-item"
                            onClick={() => setSelectedId(b.id)}
                          >
                            <Icon name="description" />
                            <span>{b.title}</span>
                          </button>
                        ))}
                      </div>
                    )}
                  </div>
                )}
              </div>
            </>
          ) : (
            <div className="lh-empty">
              <Icon name="description" />
              <div className="title">No note selected</div>
              <div className="sub">Pick one from the list or create a new one.</div>
              <button className="lh-btn primary" style={{ marginTop: 16 }} onClick={create}>
                <Icon name="add" /> New note
              </button>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}

// ============================================================
// List item
// ============================================================
function NoteListItem({ note, selected, onSelect }) {
  const preview = (note.body || '').replace(/[#*`>\[\]]/g, '').replace(/\s+/g, ' ').trim().slice(0, 90);
  const onDragStart = (e) => {
    e.dataTransfer.setData('application/x-lifehub', JSON.stringify({ type: 'note', id: note.id, title: note.title }));
    e.dataTransfer.effectAllowed = 'copyLink';
  };
  return (
    <button
      className={`note-row ${selected ? 'is-selected' : ''}`}
      onClick={onSelect}
      draggable
      onDragStart={onDragStart}
    >
      <div className="note-row-title">{note.title || 'Untitled'}</div>
      {preview && <div className="note-row-preview">{preview}</div>}
      <div className="note-row-meta">
        <span className="note-row-time">{relTime(note.updatedAt)}</span>
        {note.folder && <span className="note-row-folder"><Icon name="folder" />{note.folder}</span>}
        {(note.tags || []).slice(0,2).map(t => <span key={t} className="note-row-tag">#{t}</span>)}
      </div>
      <PinButton type="note" id={note.id} className="row-pin" />
    </button>
  );
}

window.NoteTool = NoteTool;
