// Direction 2 v2 — "INDEX" iterated
// - Drops the ◐ logo mark; just wordmark + Vol. 01 pill.
// - Moves search bar OUT of the top bar — now sits above the categories rail
//   (like A · Ledger).
// - Tweaks panel exposes:
//    · Headline font (5 unconventional pairings, Annetta-leaning)
//    · Body font
//    · Accessibility: high-contrast mode, dyslexia-friendly font (Atkinson),
//      colour-blind safe palette (Okabe–Ito), reduced motion, larger text.

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "headline_font": "inter-tight",
  "body_font": "inter-tight",
  "high_contrast": false,
  "dyslexia_font": false,
  "color_safe": false,
  "reduced_motion": false,
  "text_scale": 1
}/*EDITMODE-END*/;

const HEADLINE_FONTS = [
  { value: 'inter-tight',    label: 'Inter Tight',   stack: "'Inter Tight', 'Helvetica Neue', Arial, sans-serif", note: 'utilitarian sans' },
  { value: 'syne',           label: 'Syne',          stack: "'Syne', 'Inter Tight', sans-serif",                   note: 'geometric display' },
  { value: 'space-grotesk',  label: 'Space Grotesk', stack: "'Space Grotesk', 'Inter Tight', sans-serif",          note: 'quirky neo-grotesque' },
  { value: 'plex-sans',      label: 'IBM Plex Sans', stack: "'IBM Plex Sans', 'Inter Tight', sans-serif",          note: 'humanist sans' },
  { value: 'plex-mono',      label: 'IBM Plex Mono', stack: "'IBM Plex Mono', ui-monospace, monospace",            note: 'archive / mono' },
  { value: 'old-standard',   label: 'Old Standard',  stack: "'Old Standard TT', 'Times New Roman', serif",         note: 'serif fallback' },
  { value: 'newsreader',     label: 'Newsreader',    stack: "'Newsreader', 'EB Garamond', serif",                  note: 'serif fallback' },
];

const BODY_FONTS = [
  { value: 'inter-tight',    label: 'Inter Tight',   stack: "'Inter Tight', 'Helvetica Neue', Arial, sans-serif" },
  { value: 'plex-sans',      label: 'IBM Plex Sans', stack: "'IBM Plex Sans', 'Inter Tight', sans-serif" },
  { value: 'plex-mono',      label: 'IBM Plex Mono', stack: "'IBM Plex Mono', ui-monospace, monospace" },
  { value: 'space-grotesk',  label: 'Space Grotesk', stack: "'Space Grotesk', 'Inter Tight', sans-serif" },
  { value: 'newsreader',     label: 'Newsreader',    stack: "'Newsreader', 'EB Garamond', serif" },
];

const D2v2 = ({ entries, tags, formats }) => {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [view, setView] = React.useState('grid');
  const [activeTag, setActiveTag] = React.useState('All');
  const [activeFormat, setActiveFormat] = React.useState('All');
  const [query, setQuery] = React.useState('');
  const [weather, setWeather] = React.useState(null);
  const [weatherStatus, setWeatherStatus] = React.useState('locating…');

  // Live weather. Geolocation first, IP fallback.
  React.useEffect(() => {
    let cancelled = false;
    const fetchWeather = async (lat, lon, place) => {
      try {
        const r = await fetch(`https://api.open-meteo.com/v1/forecast?latitude=${lat}&longitude=${lon}&current=temperature_2m,weather_code`);
        const j = await r.json();
        if (cancelled) return;
        setWeather({ temp: Math.round(j.current?.temperature_2m), code: j.current?.weather_code, place });
        setWeatherStatus('ok');
      } catch (e) { if (!cancelled) setWeatherStatus('offline'); }
    };
    const ipFallback = async () => {
      try {
        const r = await fetch('https://ipapi.co/json/');
        const j = await r.json();
        if (cancelled) return;
        if (j.latitude && j.longitude) fetchWeather(j.latitude, j.longitude, j.city || j.region || '');
        else setWeatherStatus('offline');
      } catch { if (!cancelled) setWeatherStatus('offline'); }
    };
    if (navigator.geolocation) {
      navigator.geolocation.getCurrentPosition(
        pos => fetchWeather(pos.coords.latitude, pos.coords.longitude, ''),
        () => ipFallback(),
        { timeout: 4000, maximumAge: 600000 }
      );
      setTimeout(() => { if (!cancelled && weatherStatus === 'locating…') ipFallback(); }, 4500);
    } else ipFallback();
    return () => { cancelled = true; };
  }, []);

  const filtered = React.useMemo(() => {
    const q = query.trim().toLowerCase();
    return entries.filter(e => {
      if (activeTag !== 'All' && !e.tags.includes(activeTag)) return false;
      if (activeFormat !== 'All' && !e.format.split(',').map(s=>s.trim()).includes(activeFormat)) return false;
      if (!q) return true;
      return (
        e.name.toLowerCase().includes(q) ||
        e.author.toLowerCase().includes(q) ||
        e.description.toLowerCase().includes(q) ||
        e.tags.join(' ').toLowerCase().includes(q)
      );
    });
  }, [entries, activeTag, activeFormat, query]);

  // Tag → CSS variable. Color-blind-safe mode swaps to Okabe–Ito.
  const tagColor = (tag) => `var(--d2-c-${(tag || '').toLowerCase().replace(/[^a-z0-9]+/g,'-').replace(/^-|-$/g,'')})`;
  const tileFor = (e) => tagColor(e.tags[0]);

  const headlineStack = (HEADLINE_FONTS.find(f => f.value === t.headline_font) || HEADLINE_FONTS[0]).stack;
  const bodyStack = (BODY_FONTS.find(f => f.value === t.body_font) || BODY_FONTS[0]).stack;

  const rootClass = [
    'd2-root',
    t.high_contrast ? 'is-hc' : '',
    t.dyslexia_font ? 'is-dys' : '',
    t.color_safe ? 'is-cb' : '',
    t.reduced_motion ? 'is-rm' : '',
  ].filter(Boolean).join(' ');

  const rootStyle = {
    '--d2-headline-font': headlineStack,
    '--d2-body-font': t.dyslexia_font
      ? "'Atkinson Hyperlegible', 'Inter Tight', sans-serif"
      : bodyStack,
    '--d2-text-scale': t.text_scale,
  };

  return (
    <div className={rootClass} style={rootStyle}>
      {/* Top sticky bar — logo + vol pill, weather, view toggle */}
      <header className="d2-bar">
        <div className="d2-bar-left">
          <a className="d2-logo" href="#">
            <span className="d2-logo-text">openplan</span>
            <span className="d2-logo-vol">vol. 01</span>
          </a>
        </div>
        <div className="d2-bar-right">
          <div className="d2-weather" title={weather?.place || ''}>
            {weather ? (
              <>
                <span className="d2-weather-icon" aria-hidden="true">{weatherIcon(weather.code)}</span>
                <span className="d2-weather-temp">{weather.temp}°c</span>
                {weather.place && <span className="d2-weather-place">{weather.place.toLowerCase()}</span>}
              </>
            ) : (
              <span className="d2-weather-place">{weatherStatus}</span>
            )}
          </div>
          <div className="d2-view-toggle" role="tablist" aria-label="View">
            <button className={`d2-view-btn ${view==='grid' ? 'is-active' : ''}`} onClick={() => setView('grid')} aria-label="grid view">▦</button>
            <button className={`d2-view-btn ${view==='list' ? 'is-active' : ''}`} onClick={() => setView('list')} aria-label="list view">☰</button>
          </div>
        </div>
      </header>

      {/* Hero */}
      <section className="d2-hero">
        <div className="d2-hero-headline">
          A collective library of radical ideas &amp; resources for architects, students &amp; everyone shaping the built environment.
        </div>
      </section>

      {/* Search — above the categories rail */}
      <div className="d2-searchbar">
        <span className="d2-search-icon" aria-hidden="true">⌕</span>
        <input
          className="d2-search-input"
          placeholder="Search the library — title, author, idea…"
          value={query}
          onChange={e => setQuery(e.target.value)}
          aria-label="Search the library"
        />
        {query && <button className="d2-search-clear" onClick={() => setQuery('')} aria-label="Clear search">✕ clear</button>}
      </div>

      {/* Categories rail */}
      <nav className="d2-rail d2-rail--sm" aria-label="Subjects">
        <button
          className={`d2-rail-btn ${activeTag==='All' ? 'is-active' : ''}`}
          onClick={() => setActiveTag('All')}
        >
          <span className="d2-rail-swatch d2-rail-swatch--all" aria-hidden="true" />
          <span className="d2-rail-label">all</span>
          <span className="d2-rail-n">{entries.length}</span>
        </button>
        {tags.map((tag) => {
          const n = entries.filter(e => e.tags.includes(tag)).length;
          return (
            <button
              key={tag}
              className={`d2-rail-btn ${activeTag===tag ? 'is-active' : ''}`}
              style={{'--tag-c': tagColor(tag)}}
              onClick={() => setActiveTag(activeTag===tag ? 'All' : tag)}
            >
              <span className="d2-rail-swatch" aria-hidden="true" />
              <span className="d2-rail-label">{tag.toLowerCase()}</span>
              <span className="d2-rail-n">{n}</span>
            </button>
          );
        })}
      </nav>

      {/* Format secondary filter */}
      <div className="d2-formats">
        <span className="d2-formats-label">filter by form:</span>
        <button className={`d2-format-pill ${activeFormat==='All' ? 'is-active' : ''}`} onClick={() => setActiveFormat('All')}>any</button>
        {formats.map(f => (
          <button key={f} className={`d2-format-pill ${activeFormat===f ? 'is-active' : ''}`} onClick={() => setActiveFormat(activeFormat===f ? 'All' : f)}>{f.toLowerCase()}</button>
        ))}
        <span className="d2-result-count">— {filtered.length} showing</span>
        <button className="d2-shuffle" onClick={() => {
          const pool = ['All', ...tags];
          setActiveTag(pool[Math.floor(Math.random()*pool.length)]);
        }}>
          <span className="d2-shuffle-icon">↻</span> Shuffle
        </button>
      </div>

      {view === 'grid' ? (
        <div className="d2-grid">
          {filtered.map((e) => (
            <article key={e.name} className="d2-tile" style={{'--tile-c': tileFor(e)}}>
              <div className="d2-tile-thumb">
                <div className="d2-tile-glyph" aria-hidden="true">{glyphFor(e.format)}</div>
                <div className="d2-tile-corner">№{String(entries.indexOf(e)+1).padStart(3,'0')}</div>
                <div className="d2-tile-format">{e.format}</div>
              </div>
              <div className="d2-tile-body">
                <h3 className="d2-tile-title">{e.name}</h3>
                <div className="d2-tile-meta">
                  <span>{e.author}</span>
                  <span className="d2-tile-dot" aria-hidden="true">·</span>
                  <span>{e.date}</span>
                </div>
                <p className="d2-tile-desc">{e.description}</p>
                <div className="d2-tile-tags">
                  {e.tags.map(tag => <span key={tag} className="d2-tile-tag">#{tag.toLowerCase().replace(/\s+/g,'-')}</span>)}
                </div>
              </div>
            </article>
          ))}
          {filtered.length === 0 && <div className="d2-empty">no entries matched. try shuffle ↻</div>}
        </div>
      ) : (
        <div className="d2-list">
          {filtered.map((e) => (
            <a key={e.name} href="#" className="d2-list-row" onClick={ev=>ev.preventDefault()}>
              <span className="d2-list-glyph" aria-hidden="true">{glyphFor(e.format)}</span>
              <span className="d2-list-title">{e.name}</span>
              <span className="d2-list-author">{e.author}</span>
              <span className="d2-list-tags">
                {e.tags.map(tag => <span key={tag} className="d2-list-tag">{tag.toLowerCase()}</span>)}
              </span>
              <span className="d2-list-format">{e.format.toLowerCase()}</span>
              <span className="d2-list-date">{e.date}</span>
              <span className="d2-list-arrow" aria-hidden="true">↗</span>
            </a>
          ))}
          {filtered.length === 0 && <div className="d2-empty">no entries matched. try shuffle ↻</div>}
        </div>
      )}

      {/* Tweaks panel */}
      <TweaksPanel title="Tweaks">
        <TweakSection label="Typography">
          <TweakSelect
            label="Headline font"
            value={t.headline_font}
            options={HEADLINE_FONTS.map(f => ({ value: f.value, label: `${f.label} — ${f.note}` }))}
            onChange={v => setTweak('headline_font', v)}
          />
          <TweakSelect
            label="Body font"
            value={t.body_font}
            options={BODY_FONTS.map(f => ({ value: f.value, label: f.label }))}
            onChange={v => setTweak('body_font', v)}
          />
          <TweakSlider
            label="Text size"
            value={t.text_scale} min={0.85} max={1.4} step={0.05}
            onChange={v => setTweak('text_scale', v)}
          />
        </TweakSection>

        <TweakSection label="Accessibility">
          <TweakToggle
            label="High contrast"
            value={t.high_contrast}
            onChange={v => setTweak('high_contrast', v)}
          />
          <TweakToggle
            label="Dyslexia-friendly font"
            value={t.dyslexia_font}
            onChange={v => setTweak('dyslexia_font', v)}
          />
          <TweakToggle
            label="Colour-blind safe palette"
            value={t.color_safe}
            onChange={v => setTweak('color_safe', v)}
          />
          <TweakToggle
            label="Reduced motion"
            value={t.reduced_motion}
            onChange={v => setTweak('reduced_motion', v)}
          />
        </TweakSection>
      </TweaksPanel>
    </div>
  );
};

function glyphFor(format) {
  const f = format.toLowerCase();
  if (f.includes('book')) return '▤';
  if (f.includes('publication')) return '◫';
  if (f.includes('website')) return '◐';
  if (f.includes('video')) return '▶';
  if (f.includes('audio')) return '◉';
  if (f.includes('map')) return '◇';
  return '○';
}

function weatherIcon(code) {
  if (code == null) return '○';
  if (code === 0) return '☀';
  if (code <= 2) return '⛅';
  if (code === 3) return '☁';
  if (code >= 45 && code <= 48) return '𓊝';
  if (code >= 51 && code <= 67) return '☂';
  if (code >= 71 && code <= 77) return '❄';
  if (code >= 80 && code <= 82) return '☔';
  if (code >= 95) return '⚡';
  return '○';
}

window.D2v2 = D2v2;
