/* global React */
const { useState, useEffect, useRef, useMemo, useCallback, useContext, createContext } = React;

// ---------------------------------------------------------------
// Shared atoms — used by every tool
// ---------------------------------------------------------------

function Icon({ name, className = '', style, size }) {
  return (
    <span
      className={`material-symbols-outlined ${className}`}
      style={{ fontSize: size, ...style }}
    >
      {name}
    </span>
  );
}

function Badge({ children, tone = 'neutral', className = '' }) {
  const tones = {
    neutral: { bg: 'rgba(255,255,255,0.06)', fg: 'var(--text-md)', bd: 'rgba(255,255,255,0.08)' },
    primary: { bg: 'rgba(127,13,242,0.14)', fg: '#c4a0ff', bd: 'rgba(127,13,242,0.3)' },
    high:    { bg: 'rgba(248,113,113,0.12)', fg: '#fca5a5', bd: 'rgba(248,113,113,0.25)' },
    med:     { bg: 'rgba(251,191,36,0.12)', fg: '#fcd34d', bd: 'rgba(251,191,36,0.25)' },
    low:     { bg: 'rgba(96,165,250,0.10)', fg: '#93c5fd', bd: 'rgba(96,165,250,0.20)' },
    success: { bg: 'rgba(52,211,153,0.10)', fg: '#6ee7b7', bd: 'rgba(52,211,153,0.20)' },
  };
  const t = tones[tone] || tones.neutral;
  return (
    <span className={`lh-badge ${className}`} style={{
      background: t.bg, color: t.fg, border: `1px solid ${t.bd}`,
    }}>{children}</span>
  );
}

function IconBtn({ name, title, onClick, className = '', tone, active }) {
  return (
    <button
      type="button"
      title={title}
      onClick={onClick}
      className={`lh-iconbtn ${active ? 'is-active' : ''} ${className}`}
      data-tone={tone}
    >
      <Icon name={name} />
    </button>
  );
}

// ---------------------------------------------------------------
// Live data hook — re-renders when a store changes
// ---------------------------------------------------------------
function useLiveQuery(store, opts, deps = []) {
  const [data, setData] = useState(null);
  const optsRef = useRef(opts);
  optsRef.current = opts;

  const run = useCallback(async () => {
    if (!store) return;
    if (Array.isArray(store)) {
      const results = await Promise.all(store.map(s => db[s].list(optsRef.current?.[s])));
      setData(Object.fromEntries(store.map((s,i) => [s, results[i]])));
    } else {
      const r = await db[store].list(optsRef.current);
      setData(r);
    }
  // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [Array.isArray(store) ? store.join(',') : store, ...deps]);

  useEffect(() => {
    run();
    const unsub = db.subscribe(({ store: s }) => {
      if (Array.isArray(store) ? store.includes(s) : s === store) run();
    });
    return unsub;
  }, [run]);

  return data;
}

// ---------------------------------------------------------------
// Live single-record from db.kv — re-renders when that key changes.
// Returns the record's `.v` payload (or undefined while loading).
// Use for tools whose state lives in kv (habits, reading, etc.).
// ---------------------------------------------------------------
function useKvRecord(key) {
  const [val, setVal] = useState(undefined);
  useEffect(() => {
    let mounted = true;
    const load = () => db.kv.get(key).then(rec => {
      if (mounted) setVal(rec?.v);
    });
    load();
    const unsub = db.subscribe(({ store, value }) => {
      if (store !== 'kv') return;
      // On a `clear` notification value is undefined — reload to be safe.
      if (!value || value.k === key) load();
    });
    return () => { mounted = false; unsub(); };
  }, [key]);
  return val;
}

// ---------------------------------------------------------------
// Time helpers
// ---------------------------------------------------------------
const DAY = 86400000;
function relTime(ts) {
  if (!ts) return '';
  const d = ts - Date.now();
  const ad = Math.abs(d);
  if (ad < 60000) return d < 0 ? 'just now' : 'in a moment';
  if (ad < 3600000) {
    const m = Math.round(ad / 60000);
    return d < 0 ? `${m}m ago` : `in ${m}m`;
  }
  if (ad < DAY) {
    const h = Math.round(ad / 3600000);
    return d < 0 ? `${h}h ago` : `in ${h}h`;
  }
  if (ad < DAY * 7) {
    const days = Math.round(ad / DAY);
    if (d < 0) return days === 1 ? 'yesterday' : `${days}d ago`;
    return days === 1 ? 'tomorrow' : `in ${days}d`;
  }
  const date = new Date(ts);
  return date.toLocaleDateString(undefined, { month:'short', day:'numeric' });
}

function fmtDateShort(ts) {
  if (!ts) return '';
  const date = new Date(ts);
  return date.toLocaleDateString(undefined, { month:'short', day:'numeric' });
}

// ---------------------------------------------------------------
// Send-to bus — cross-tool data passing
// ---------------------------------------------------------------
const SendToContext = createContext({ send: () => {}, register: () => () => {} });

function SendToProvider({ children, currentTool, setTool, disabledTools = [] }) {
  const handlersRef = useRef({});
  const register = useCallback((tool, handler) => {
    handlersRef.current[tool] = handler;
    return () => { if (handlersRef.current[tool] === handler) delete handlersRef.current[tool]; };
  }, []);
  const send = useCallback((toolName, payload) => {
    setTool(toolName);
    // Wait for component to mount/handler to register
    setTimeout(() => {
      const h = handlersRef.current[toolName];
      if (h) h(payload);
    }, 60);
  }, [setTool]);
  const isToolEnabled = useCallback((id) => !disabledTools.includes(id), [disabledTools]);
  return (
    <SendToContext.Provider value={{ send, register, disabledTools, isToolEnabled }}>
      {children}
    </SendToContext.Provider>
  );
}

function useSendTo() { return useContext(SendToContext); }

function useReceiveFrom(tool, handler) {
  const { register } = useSendTo();
  useEffect(() => register(tool, handler), [tool, handler, register]);
}

// ---------------------------------------------------------------
// Send-to popover (used in Note, JSON, etc.)
// ---------------------------------------------------------------
function SendToButton({ payload, label = 'Send to', compact }) {
  const { send, isToolEnabled } = useSendTo();
  const [open, setOpen] = useState(false);
  const ref = useRef(null);

  useEffect(() => {
    if (!open) return;
    const onDoc = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener('mousedown', onDoc);
    return () => document.removeEventListener('mousedown', onDoc);
  }, [open]);

  const allTargets = [
    { tool: 'json',     icon: 'data_object', label: 'JSON viewer' },
    { tool: 'note',     icon: 'edit_note', label: 'New note' },
    { tool: 'diff',     icon: 'difference', label: 'Diff viewer' },
    { tool: 'todo',     icon: 'check_box', label: 'Quick todo' },
  ];
  const targets = allTargets.filter(t => !isToolEnabled || isToolEnabled(t.tool));

  return (
    <div className="lh-sendto" ref={ref}>
      <button className={`lh-chip ${compact ? 'is-compact' : ''}`} onClick={() => setOpen(o => !o)}>
        <Icon name="north_east" />
        {!compact && <span>{label}</span>}
      </button>
      {open && (
        <div className="lh-sendto-menu">
          <div className="lh-sendto-head">Send selection to</div>
          {targets.map(t => (
            <button
              key={t.tool}
              className="lh-sendto-item"
              onClick={() => { send(t.tool, payload); setOpen(false); }}
            >
              <Icon name={t.icon} />
              <span>{t.label}</span>
              <Icon name="chevron_right" className="trail" />
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

// ---------------------------------------------------------------
// Confirm dialog (lightweight)
// ---------------------------------------------------------------
function useConfirm() {
  return useCallback((message) => {
    if (window.lhDialog) return window.lhDialog.confirm({ message });
    return Promise.resolve(window.confirm(message));
  }, []);
}

// ---------------------------------------------------------------
// Pin system — pin records from any tool to the Home page
// ---------------------------------------------------------------
const PIN_KEY = 'pinnedItems';

async function getPinned() {
  const rec = await db.kv.get(PIN_KEY);
  return Array.isArray(rec?.v) ? rec.v : [];
}
async function setPinned(arr) {
  await db.kv.put({ k: PIN_KEY, v: arr });
}
async function togglePin(type, id) {
  const arr = await getPinned();
  const i = arr.findIndex(p => p.type === type && p.id === id);
  let next;
  if (i >= 0) next = arr.filter((_, j) => j !== i);
  else        next = [{ type, id, pinnedAt: Date.now() }, ...arr];
  await setPinned(next);
  return i < 0;
}

function usePinned() {
  const [list, setList] = useState([]);
  useEffect(() => {
    let mounted = true;
    const run = () => getPinned().then(v => { if (mounted) setList(v); });
    run();
    const unsub = db.subscribe(({ store }) => { if (store === 'kv') run(); });
    return () => { mounted = false; unsub(); };
  }, []);
  return list;
}

function PinButton({ type, id, className = '' }) {
  const pinned = usePinned();
  const isPinned = pinned.some(p => p.type === type && p.id === id);
  return (
    <button
      type="button"
      className={`pin-btn ${isPinned ? 'is-pinned' : ''} ${className}`}
      title={isPinned ? 'Unpin from home' : 'Pin to home'}
      onClick={(e) => { e.preventDefault(); e.stopPropagation(); togglePin(type, id); }}
    >
      <Icon name={isPinned ? 'push_pin' : 'push_pin'} />
    </button>
  );
}

// ---------------------------------------------------------------
// Clipboard — robust copy with fallback for sandboxed iframes
// ---------------------------------------------------------------
async function copyText(text) {
  if (!text) return false;
  try {
    if (navigator.clipboard && window.isSecureContext) {
      await navigator.clipboard.writeText(text);
      return true;
    }
  } catch (e) { /* fall through */ }
  // Fallback: hidden textarea + execCommand
  try {
    const ta = document.createElement('textarea');
    ta.value = text;
    ta.style.cssText = 'position:fixed;top:0;left:0;width:1px;height:1px;padding:0;border:0;opacity:0;pointer-events:none;';
    document.body.appendChild(ta);
    ta.focus();
    ta.select();
    const ok = document.execCommand('copy');
    document.body.removeChild(ta);
    return ok;
  } catch (e) { return false; }
}

// Hook: returns [copied, copyFn] — `copied` flips true for 1.5s after success
function useCopy() {
  const [copied, setCopied] = useState(false);
  const copy = useCallback(async (text) => {
    const ok = await copyText(text);
    if (ok) {
      setCopied(true);
      setTimeout(() => setCopied(false), 1500);
    }
    return ok;
  }, []);
  return [copied, copy];
}

// ---------------------------------------------------------------
// Tool sidebar collapse — for two-pane tools (Notes, JSON, Todo, Journal…)
// Persists per-tool in db.kv under key `toolSidebar_<id>`.
// ---------------------------------------------------------------
function useToolSidebar(toolId, defaultCollapsed = false) {
  const [collapsed, setCollapsed] = useState(defaultCollapsed);
  useEffect(() => {
    let mounted = true;
    db.kv.get('toolSidebar_' + toolId).then(rec => {
      if (mounted && rec?.v != null) setCollapsed(!!rec.v);
    });
    return () => { mounted = false; };
  }, [toolId]);
  const toggle = useCallback(() => {
    setCollapsed(c => {
      const next = !c;
      db.kv.put({ k: 'toolSidebar_' + toolId, v: next });
      return next;
    });
  }, [toolId]);
  return [collapsed, toggle];
}

function ToolSidebarToggle({ collapsed, onToggle, className = '' }) {
  return (
    <button
      type="button"
      className={`tool-sidebar-toggle ${collapsed ? 'is-collapsed' : ''} ${className}`}
      onClick={onToggle}
      title={collapsed ? 'Expand sidebar' : 'Collapse sidebar'}
    >
      <Icon name={collapsed ? 'chevron_right' : 'chevron_left'} />
    </button>
  );
}

Object.assign(window, {
  Icon, Badge, IconBtn,
  useLiveQuery, useSendTo, useReceiveFrom,
  useKvRecord,
  SendToProvider, SendToButton,
  relTime, fmtDateShort, DAY,
  useConfirm,
  PinButton, usePinned, togglePin, getPinned,
  copyText, useCopy,
  useToolSidebar, ToolSidebarToggle,
});
