// ─────── TELESCOPE: fuzzy file/symbol finder (Ctrl-P / :find) ───────

const TELESCOPE_BLURBS = {
  projects: 'Shipped work — raytracer, RLVR pipeline, GPU kernel DSL, and the load-bearing bullets.',
  experience: 'Three years, two countries. Reverse-chronological paid work + education.',
  about: 'Who I am, what I optimize for, and why the loop matters more than the model.',
  now: "What I'm actually doing this month — including the “looking for” card.",
  writing: 'Notes and posts. Pick one to read inline.',
  contact: 'Email, socials, and a vim-native mail composer.',
};

function fuzzyScore(query, text) {
  // subsequence match; returns { ok, score, ranges } — lower score = better
  const q = query.toLowerCase();
  const t = text.toLowerCase();
  if (!q) return { ok: true, score: 0, ranges: [] };
  let qi = 0;
  let score = 0;
  let prev = -2;
  const ranges = [];
  for (let ti = 0; ti < t.length && qi < q.length; ti++) {
    if (t[ti] === q[qi]) {
      score += ti - prev === 1 ? 0 : ti; // reward contiguous + early matches
      if (ranges.length && ranges[ranges.length - 1][1] === ti) {
        ranges[ranges.length - 1][1] = ti + 1;
      } else {
        ranges.push([ti, ti + 1]);
      }
      prev = ti;
      qi++;
    }
  }
  return qi === q.length ? { ok: true, score, ranges } : { ok: false, score: Infinity, ranges: [] };
}

function HighlightRanges({ text, ranges }) {
  if (!ranges || !ranges.length) return text;
  const out = [];
  let cursor = 0;
  ranges.forEach(([s, e], i) => {
    if (s > cursor) out.push(<span key={`p${i}`}>{text.slice(cursor, s)}</span>);
    out.push(<span key={`m${i}`} className="tel-match">{text.slice(s, e)}</span>);
    cursor = e;
  });
  if (cursor < text.length) out.push(<span key="tail">{text.slice(cursor)}</span>);
  return out;
}

function Telescope({ deps, onOpen, onClose }) {
  const { buffers, posts } = deps;
  const inputRef = React.useRef(null);
  const listRef = React.useRef(null);
  const [query, setQuery] = React.useState('');
  const [sel, setSel] = React.useState(0);

  const entries = React.useMemo(() => {
    const bufs = buffers.map((b) => ({
      kind: 'buffer',
      icon: '',
      label: b.file,
      hint: `buffer ${b.key}`,
      bufferId: b.id,
      blurb: TELESCOPE_BLURBS[b.id] || '',
    }));
    const writ = (posts || []).map((p) => ({
      kind: 'post',
      icon: '',
      label: `writing/${p.slug}.md`,
      hint: p.date || 'post',
      bufferId: 'writing',
      slug: p.slug,
      blurb: p.excerpt || p.title || '',
      title: p.title,
    }));
    return [...bufs, ...writ];
  }, [buffers, posts]);

  const results = React.useMemo(() => {
    const scored = entries
      .map((e) => ({ e, m: fuzzyScore(query, e.label) }))
      .filter((x) => x.m.ok)
      .sort((a, b) => a.m.score - b.m.score);
    return scored;
  }, [entries, query]);

  React.useEffect(() => {
    setSel(0);
  }, [query]);

  React.useEffect(() => {
    if (inputRef.current) inputRef.current.focus();
  }, []);

  // keep selected row in view
  React.useEffect(() => {
    const list = listRef.current;
    if (!list) return;
    const row = list.querySelector('[data-sel="true"]');
    if (row) {
      const r = row.getBoundingClientRect();
      const p = list.getBoundingClientRect();
      if (r.top < p.top) list.scrollBy({ top: r.top - p.top });
      else if (r.bottom > p.bottom) list.scrollBy({ top: r.bottom - p.bottom });
    }
  }, [sel, results.length]);

  const choose = (i) => {
    const hit = results[i];
    if (!hit) return;
    onOpen(hit.e.bufferId, hit.e.slug);
    onClose();
  };

  const move = (delta) => {
    setSel((s) => {
      const n = results.length;
      if (!n) return 0;
      return ((s + delta) % n + n) % n;
    });
  };

  const onKeyDown = (e) => {
    if (e.key === 'Escape') {
      e.preventDefault();
      onClose();
    } else if (e.key === 'ArrowDown' || (e.ctrlKey && (e.key === 'n' || e.key === 'j'))) {
      e.preventDefault();
      move(1);
    } else if (e.key === 'ArrowUp' || (e.ctrlKey && (e.key === 'p' || e.key === 'k'))) {
      e.preventDefault();
      move(-1);
    } else if (e.key === 'Enter') {
      e.preventDefault();
      choose(sel);
    }
  };

  const active = results[sel] && results[sel].e;

  return (
    <div className="tel-overlay" onMouseDown={onClose}>
      <div className="tel-win" onMouseDown={(e) => e.stopPropagation()}>
        <div className="tel-left">
          <div className="tel-results-head">
            <span className="tel-title">Find Files</span>
            <span className="tel-count">{results.length}/{entries.length}</span>
          </div>
          <div className="tel-results" ref={listRef}>
            {results.length === 0 && <div className="tel-empty">— no matching files —</div>}
            {results.map((r, i) => (
              <div
                key={r.e.label}
                className={'tel-row' + (i === sel ? ' on' : '')}
                data-sel={i === sel ? 'true' : undefined}
                onMouseMove={() => setSel(i)}
                onClick={() => choose(i)}
              >
                <span className="tel-caret">{i === sel ? '▌' : ''}</span>
                <span className="tel-icon">{r.e.icon}</span>
                <span className="tel-label">
                  <HighlightRanges text={r.e.label} ranges={r.m.ranges} />
                </span>
                <span className="tel-hint">{r.e.hint}</span>
              </div>
            ))}
          </div>
          <div className="tel-prompt">
            <span className="tel-prompt-glyph"> </span>
            <input
              ref={inputRef}
              value={query}
              onChange={(e) => setQuery(e.target.value)}
              onKeyDown={onKeyDown}
              placeholder="type to filter — ↑↓ / C-n C-p to move, ⏎ to open, esc to close"
              spellCheck={false}
              autoComplete="off"
            />
          </div>
        </div>
        <div className="tel-preview">
          <div className="tel-preview-head">{active ? active.label : 'preview'}</div>
          <div className="tel-preview-body">
            {active ? (
              <>
                {active.title && <div className="tel-pv-title">{active.title}</div>}
                <div className="tel-pv-kind">{active.kind === 'post' ? 'markdown · note' : 'markdown · buffer'}</div>
                <p className="tel-pv-blurb">{active.blurb}</p>
                <div className="tel-pv-foot">⏎ open {active.label}</div>
              </>
            ) : (
              <p className="tel-pv-blurb">Nothing to preview.</p>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { Telescope });
