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

// ============================================================
// Daily journal — date-keyed entries (one per day)
// Stored as notes with folder='__journal__', title='YYYY-MM-DD'
// We piggyback on the existing notes store so backup/import already
// covers it; the tool renders a date-driven UI on top.
// ============================================================

const JOURNAL_FOLDER = '__journal__';

function dateKey(d) {
  const dt = new Date(d);
  const y = dt.getFullYear();
  const m = String(dt.getMonth() + 1).padStart(2, '0');
  const day = String(dt.getDate()).padStart(2, '0');
  return `${y}-${m}-${day}`;
}
function dateLabel(key) {
  const [y, m, d] = key.split('-').map(Number);
  return new Date(y, m - 1, d).toLocaleDateString(undefined, {
    weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'
  });
}

function JournalTool() {
  const notes = useLiveQuery('notes');
  const todos = useLiveQuery('todos');
  const [selectedKey, setSelectedKey] = useState(dateKey(Date.now()));
  const [search, setSearch] = useState('');
  const [sidebarCollapsed, toggleSidebar] = useToolSidebar('journal');
  const [view, setView] = useState('calendar'); // 'calendar' | 'list'
  const [calMonth, setCalMonth] = useState(() => {
    const d = new Date(); d.setDate(1); d.setHours(0,0,0,0);
    return d.getTime();
  });

  // Persist view + load preferences
  useEffect(() => {
    db.kv.get('journalView').then(rec => { if (rec?.v) setView(rec.v); });
  }, []);
  const setViewPersist = (v) => {
    setView(v);
    db.kv.put({ k: 'journalView', v });
  };

  // Keep calMonth in sync with selectedKey when user picks via list / arrow
  useEffect(() => {
    const [y, m] = selectedKey.split('-').map(Number);
    const monthStart = new Date(y, m - 1, 1).getTime();
    setCalMonth(monthStart);
  }, [selectedKey]);

  const entries = useMemo(() => {
    const map = new Map();
    if (!notes) return map;
    for (const n of notes) {
      if (n.folder === JOURNAL_FOLDER && /^\d{4}-\d{2}-\d{2}$/.test(n.title)) {
        map.set(n.title, n);
      }
    }
    return map;
  }, [notes]);

  // Always-visible: last 30 days + any with entries
  const dateList = useMemo(() => {
    const set = new Set([selectedKey, dateKey(Date.now())]);
    for (const k of entries.keys()) set.add(k);
    // Last 14 days
    for (let i = 0; i < 14; i++) {
      const d = new Date(); d.setDate(d.getDate() - i);
      set.add(dateKey(d));
    }
    const arr = [...set].sort().reverse();
    if (!search) return arr;
    const q = search.toLowerCase();
    return arr.filter(k => {
      if (k.includes(q)) return true;
      const e = entries.get(k);
      return e && (e.body || '').toLowerCase().includes(q);
    });
  }, [entries, selectedKey, search]);

  const current = entries.get(selectedKey);

  // Local draft state. We are the source of truth while editing today's entry.
  // Sync from DB ONLY when the user switches to a different day, never on
  // subsequent re-renders (which happen each time we save).
  const [draft, setDraft] = useState('');
  const loadedKeyRef = useRef(null);
  useEffect(() => {
    if (loadedKeyRef.current === selectedKey) return;
    if (notes === null) return; // wait for first load
    const entry = entries.get(selectedKey);
    setDraft(entry?.body || '');
    loadedKeyRef.current = selectedKey;
  }, [selectedKey, entries, notes]);

  const ensureEntry = async (key) => {
    if (entries.get(key)) return entries.get(key);
    const id = db.uid('notes_');
    await db.notes.put({
      id, title: key, folder: JOURNAL_FOLDER, format: 'markdown',
      body: '', tags: [],
    });
    return await db.notes.get(id);
  };

  // Debounced DB write — captures every keystroke but only persists
  // after the user pauses, reducing IndexedDB churn.
  const writeTimerRef = useRef(null);
  const pendingBodyRef = useRef(null);
  const scheduleWrite = (body) => {
    pendingBodyRef.current = body;
    if (writeTimerRef.current) clearTimeout(writeTimerRef.current);
    writeTimerRef.current = setTimeout(() => {
      const b = pendingBodyRef.current;
      const entry = entries.get(selectedKey);
      if (entry) {
        db.notes.put({ ...entry, body: b });
      } else if (b && b.length > 0) {
        const id = db.uid('notes_');
        db.notes.put({
          id, title: selectedKey, folder: JOURNAL_FOLDER,
          format: 'markdown', body: b, tags: [],
        });
      }
      writeTimerRef.current = null;
    }, 400);
  };
  // Flush pending writes on unmount / day-switch
  useEffect(() => {
    return () => {
      if (writeTimerRef.current) {
        clearTimeout(writeTimerRef.current);
        const b = pendingBodyRef.current;
        const entry = entries.get(loadedKeyRef.current);
        if (entry && b != null) db.notes.put({ ...entry, body: b });
      }
    };
  }, []);
  useEffect(() => {
    // Day changed — flush any pending write to the previous day first
    if (writeTimerRef.current) {
      clearTimeout(writeTimerRef.current);
      const b = pendingBodyRef.current;
      if (loadedKeyRef.current && loadedKeyRef.current !== selectedKey) {
        const prevEntry = entries.get(loadedKeyRef.current);
        if (prevEntry && b != null) db.notes.put({ ...prevEntry, body: b });
      }
      writeTimerRef.current = null;
      pendingBodyRef.current = null;
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [selectedKey]);

  const updateBody = (body) => {
    setDraft(body);
    scheduleWrite(body);
  };

  // Auto-generated daily summary: completed todos with this date
  const completedToday = useMemo(() => {
    if (!todos) return [];
    const [y, m, d] = selectedKey.split('-').map(Number);
    const start = new Date(y, m - 1, d).setHours(0,0,0,0);
    const end = start + 86400000;
    return todos.filter(t => t.done && t.updatedAt >= start && t.updatedAt < end);
  }, [todos, selectedKey]);

  const insertSummary = () => {
    if (completedToday.length === 0) return;
    const lines = ['## Completed today', ...completedToday.map(t => `- ${t.title}${t.project ? ` _(${t.project})_` : ''}`)];
    const block = lines.join('\n') + '\n\n';
    updateBody((draft || '') + (draft ? '\n\n' : '') + block);
  };

  const todayKey = dateKey(Date.now());

  return (
    <div className="tool journal-tool">
      <div className={`tool-twopane journal-twopane ${sidebarCollapsed ? 'is-list-collapsed' : ''}`}>
        <aside className="tool-list journal-aside">
          <div className="tool-list-collapsed-rail" onClick={toggleSidebar} title="Expand sidebar">
            <Icon name="chevron_right" />
          </div>
          <div className="tool-list-head">
            <h2>Journal</h2>
            <span className="count">{entries.size}</span>
            <IconBtn name="today" title="Go to today" onClick={() => setSelectedKey(todayKey)} />
            <div className="journal-view-pick">
              <button
                className={`journal-view-btn ${view === 'calendar' ? 'is-active' : ''}`}
                onClick={() => setViewPersist('calendar')}
                title="Calendar view"
              ><Icon name="calendar_month" /></button>
              <button
                className={`journal-view-btn ${view === 'list' ? 'is-active' : ''}`}
                onClick={() => setViewPersist('list')}
                title="List view"
              ><Icon name="view_list" /></button>
            </div>
            <ToolSidebarToggle collapsed={sidebarCollapsed} onToggle={toggleSidebar} />
          </div>

          {view === 'calendar' ? (
            <JournalCalendar
              month={calMonth}
              setMonth={setCalMonth}
              entries={entries}
              selectedKey={selectedKey}
              todayKey={todayKey}
              onSelect={setSelectedKey}
            />
          ) : (
            <>
              <div className="tool-list-search">
                <Icon name="search" />
                <input placeholder="Search entries…" value={search} onChange={e => setSearch(e.target.value)} />
              </div>
              <div className="tool-list-scroll">
                {dateList.map(key => {
                  const entry = entries.get(key);
                  const hasContent = entry && (entry.body || '').trim().length > 0;
                  const preview = hasContent
                    ? entry.body.replace(/[#*_`>]/g, '').replace(/\s+/g, ' ').trim().slice(0, 90)
                    : '';
                  const isToday = key === todayKey;
                  return (
                    <button
                      key={key}
                      className={`journal-row ${selectedKey === key ? 'is-selected' : ''} ${isToday ? 'is-today' : ''} ${hasContent ? 'has-content' : ''}`}
                      onClick={() => setSelectedKey(key)}
                    >
                      <div className="journal-row-date">
                        {dateLabel(key).split(',')[0]}
                        {isToday && <span className="journal-today-pill">Today</span>}
                      </div>
                      <div className="journal-row-key">{key}</div>
                      {preview && <div className="journal-row-preview">{preview}</div>}
                      {!hasContent && <div className="journal-row-empty">No entry</div>}
                    </button>
                  );
                })}
              </div>
            </>
          )}
        </aside>

        <div className="tool-detail">
          <div className="tool-detail-head">
            <Icon name="auto_stories" style={{ color: 'var(--color-primary)', fontSize: 22 }} />
            <div>
              <div className="journal-title">{dateLabel(selectedKey)}</div>
              <div className="journal-subtitle">{selectedKey}</div>
            </div>
            <div style={{ flex: 1 }} />
            {completedToday.length > 0 && (
              <button className="lh-btn" onClick={insertSummary}>
                <Icon name="auto_fix_high" />
                Insert today's wins ({completedToday.length})
              </button>
            )}
            {current && (
              <IconBtn name="delete" tone="danger" title="Delete entry" onClick={async () => {
                const ok = await window.lhDialog.confirm({
                  title: 'Delete this entry?',
                  message: `${dateLabel(selectedKey)} will be permanently removed.`,
                  confirmLabel: 'Delete', danger: true, icon: 'delete_forever',
                });
                if (ok) db.notes.delete(current.id);
              }} />
            )}
          </div>

          <div className="journal-prompts">
            <span className="journal-prompts-label">Prompts:</span>
            {[
              "What went well today?",
              "What did I learn?",
              "What's on my mind?",
              "Top 3 priorities for tomorrow",
            ].map(p => (
              <button
                key={p}
                className="lh-chip"
                onClick={() => updateBody((draft || '') + (draft ? '\n\n' : '') + `**${p}**\n\n`)}
              >
                {p}
              </button>
            ))}
          </div>

          <textarea
            className="journal-textarea"
            value={draft}
            placeholder={`Start writing about ${dateLabel(selectedKey)}…\n\nThis is your space — what happened, how you felt, what you noticed.`}
            onChange={e => updateBody(e.target.value)}
          />

          <div className="journal-foot">
            {draft.length} characters · {draft.trim().split(/\s+/).filter(Boolean).length} words
            {current && <span> · saved {relTime(current.updatedAt)}</span>}
          </div>
        </div>
      </div>
    </div>
  );
}

window.JournalTool = JournalTool;

// ============================================================
// Calendar grid — month view with entry markers
// Carefully built to handle: any month, leap years, week boundaries,
// past/future navigation. Uses local time (not UTC).
// ============================================================
const MONTHS = ['January','February','March','April','May','June','July','August','September','October','November','December'];
const WEEKDAYS = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'];

function JournalCalendar({ month, setMonth, entries, selectedKey, todayKey, onSelect }) {
  // Build grid: 6 weeks × 7 days, starting on Sunday of the week containing day 1
  const cells = useMemo(() => {
    const cur = new Date(month);
    const y = cur.getFullYear();
    const m = cur.getMonth();
    // First day of month + its weekday (0 = Sun)
    const first = new Date(y, m, 1);
    const startOffset = first.getDay();
    // Grid start = first day minus its weekday offset
    const gridStart = new Date(y, m, 1 - startOffset);
    // Always 6 rows = 42 cells (handles any month/locale safely)
    const arr = [];
    for (let i = 0; i < 42; i++) {
      const d = new Date(gridStart.getFullYear(), gridStart.getMonth(), gridStart.getDate() + i);
      arr.push({
        date: d,
        key: dateKey(d),
        inMonth: d.getMonth() === m,
      });
    }
    return arr;
  }, [month]);

  const cur = new Date(month);
  const title = `${MONTHS[cur.getMonth()]} ${cur.getFullYear()}`;

  const goPrev = () => {
    const d = new Date(month);
    d.setMonth(d.getMonth() - 1);
    setMonth(d.getTime());
  };
  const goNext = () => {
    const d = new Date(month);
    d.setMonth(d.getMonth() + 1);
    setMonth(d.getTime());
  };
  const goToday = () => {
    const t = new Date();
    setMonth(new Date(t.getFullYear(), t.getMonth(), 1).getTime());
    onSelect(dateKey(t));
  };

  const monthEntries = cells.filter(c => c.inMonth && entries.get(c.key));
  const monthEntryCount = monthEntries.length;

  return (
    <div className="journal-cal">
      <div className="journal-cal-head">
        <IconBtn name="chevron_left" title="Previous month" onClick={goPrev} />
        <div className="journal-cal-title">{title}</div>
        <IconBtn name="chevron_right" title="Next month" onClick={goNext} />
      </div>
      <div className="journal-cal-sub">
        <button className="journal-cal-today-btn" onClick={goToday}>
          <Icon name="today" />Today
        </button>
        <span className="journal-cal-count">
          <span className="journal-cal-dot" />{monthEntryCount} entr{monthEntryCount === 1 ? 'y' : 'ies'} this month
        </span>
      </div>
      <div className="journal-cal-weekdays">
        {WEEKDAYS.map(w => <div key={w}>{w}</div>)}
      </div>
      <div className="journal-cal-grid">
        {cells.map((c, i) => {
          const entry = entries.get(c.key);
          const hasContent = entry && (entry.body || '').trim().length > 0;
          const isFuture = c.date.getTime() > Date.now();
          return (
            <button
              key={i}
              className={[
                'journal-cal-cell',
                c.inMonth ? 'in-month' : 'out-month',
                c.key === todayKey ? 'is-today' : '',
                c.key === selectedKey ? 'is-selected' : '',
                hasContent ? 'has-entry' : '',
                isFuture ? 'is-future' : '',
              ].join(' ')}
              onClick={() => onSelect(c.key)}
              title={dateLabel(c.key) + (hasContent ? ' — has entry' : '')}
            >
              <span className="journal-cal-day">{c.date.getDate()}</span>
              {hasContent && <span className="journal-cal-marker" />}
            </button>
          );
        })}
      </div>
    </div>
  );
}
