// The journey screen. One decision per screen: where you are → where next →
// how you travel → what it costs → go. Settlement and persistence stay in App.
//
// It deliberately carries no planning surface any more: no draggable schematic
// route board, no chapter numbers, no coordinates, no timezone readout, no
// "not to scale" disclaimers. Those were the "route planning desk" and they
// buried the one thing the player is here to do. Destination choice is now a
// photograph of the city.
(function () {
  const { useState, useEffect, useRef } = React;
  // Every string on this screen lives in the one i18n table (i18n.js), so a
  // language the game supports is a language this screen speaks. It used to be
  // a local ternary over five languages and fr/es read English.
  const DESK_KEYS = ['here','next','ways','begin','choose','budget','left','goal',
    'stampsOne','stamps','collection','album','menu','explore','account','help','end',
    'all','search','empty','revisit','cabin','seconds','additional','waived',
    'plane','subway','bus','ship','insufficient','traveling','visited'];
  const deskCopy = T => {
    const c = {};
    for (const k of DESK_KEYS) c[k] = T('desk.' + k);
    // shared with the rest of the app — one word per concept, not two
    c.journal = T('top.log');
    c.language = T('top.lang');
    c.close = T('btn.close');
    return c;
  };
  function Icon({ name, size = 20 }) {
    const paths = {
      arrow:'M5 12h14m-6-6 6 6-6 6', compass:'M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Zm4 5-2.5 5.5L8 16l2.5-5.5L16 8Z',
      plane:'m21 3-6 18-4-8-8-4 18-6ZM11 13l5-5', subway:'M6 16V5a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v11H6Zm0-7h12M8 16l-3 5m11-5 3 5M7 19h10M9 13h.01M15 13h.01', bus:'M5 17V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v12H5Zm0-8h14M8 17v3m8-3v3M8 13h.01M16 13h.01', ship:'M7 12V6h10v6M12 6V2M3 14l9-4 9 4-3 5H6l-3-5Zm1 7 4-1 4 1 4-1 4 1',
      book:'M12 5c-3-2-6-2-9-1v15c3-1 6-1 9 1 3-2 6-2 9-1V4c-3-1-6-1-9 1v15', photo:'M3 5h18v15H3V5Zm0 12 6-6 5 5 3-3 4 4M16 9h.01', menu:'M4 8h16M4 16h16', close:'m6 6 12 12M6 18 18 6', check:'m5 12 4 4L19 6', pin:'M12 21S5 14 5 9a7 7 0 0 1 14 0c0 5-7 12-7 12Zm0-15a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z', search:'M10 3a7 7 0 1 0 0 14 7 7 0 0 0 0-14Zm5 12 6 6', stamp:'M7 3h10v6l-2 3v3h4v5H5v-5h4v-3L7 9V3Z', film:'M4 4h16v16H4V4Zm0 4h16M4 16h16M8 4v16m8-16v16', note:'M9 18V5l12-2v13M9 13l12-2M6 18a3 3 0 1 0 0 6 3 3 0 0 0 0-6Zm15-2a3 3 0 1 0 0 6 3 3 0 0 0 0-6Z'
    };
    return <svg width={size} height={size} viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.35" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><path d={paths[name] || paths.compass}/></svg>;
  }
  const nameOf = (city, lang) => !city ? '' : lang === 'zh' ? city.name_zh || city.name : (city.name || '').toLowerCase().replace(/(^|\s)\S/g, s => s.toUpperCase());

  // The curated shots are 3840px wide. A card is at most ~430 CSS pixels, so
  // ask the thumbnailer for a width Wikimedia actually keeps cached. If that
  // width 400s we fall back to the original URL, and only if THAT fails do we
  // show the lettered plate — a photo is never dropped silently.
  const sizedPhoto = url => typeof url === 'string' ? url.replace(/\/\d{3,4}px-/, '/1280px-') : url;

  // A photograph of the city over a plate in the same palette carrying the
  // city's initial. The plate is always painted, so there is never a blank
  // hole while the photo loads, or when the device is offline.
  function CityPhoto({ city, lang, className = '' }) {
    const raw = window.useWikiImage ? window.useWikiImage(city?.name || '') : '';
    const [stage, setStage] = useState(0);
    const [ready, setReady] = useState(false);
    useEffect(() => { setStage(0); setReady(false); }, [raw]);
    const src = !raw ? '' : stage === 0 ? sizedPhoto(raw) : stage === 1 ? raw : '';
    const label = nameOf(city, lang);
    const initial = (lang === 'zh' ? (city?.name_zh || city?.name || '') : (city?.name || ''))
      .trim().charAt(0).toUpperCase() || '·';
    return (
      <div className={`ad-photo ${className}`} data-initial={initial} role="img" aria-label={label}>
        {src && <img src={src} alt="" aria-hidden="true" decoding="async"
                     className={ready ? 'is-ready' : ''}
                     onLoad={() => setReady(true)}
                     onError={() => { setReady(false); setStage(n => n + 1); }}/>}
      </div>
    );
  }

  function AtlasDesk(p) {
    const lang = window.getLang?.() || 'en';
    const T = window.useT ? window.useT() : key => key;
    const c = deskCopy(T);
    const [drawer, setDrawer] = useState(null);
    const [query, setQuery] = useState('');
    const dialogRef = useRef(null);
    const drawerTrigger = useRef(null);
    useEffect(() => {
      if (!drawer) return;
      const oldOverflow = document.body.style.overflow;
      drawerTrigger.current = document.activeElement;
      document.body.style.overflow = 'hidden';
      const first = dialogRef.current?.querySelector('button,input');
      first?.focus();
      const onKey = e => {
        if (e.key === 'Escape') { e.stopImmediatePropagation(); setDrawer(null); }
        if (e.key !== 'Tab') return;
        const els = [...(dialogRef.current?.querySelectorAll('button:not(:disabled),input') || [])];
        if (!els.length) return;
        if (e.shiftKey && document.activeElement === els[0]) { e.preventDefault(); els[els.length - 1].focus(); }
        else if (!e.shiftKey && document.activeElement === els[els.length - 1]) { e.preventDefault(); els[0].focus(); }
      };
      window.addEventListener('keydown', onKey, true);
      return () => { document.body.style.overflow = oldOverflow; window.removeEventListener('keydown', onKey, true); drawerTrigger.current?.focus(); };
    }, [drawer]);

    const isAllowed = city => !window.PATHFINDER_DESTINATIONS || window.PATHFINDER_DESTINATIONS.includes(city.country);
    const all = (window.CITIES || []).filter(city => city.id !== p.current?.id && isAllowed(city));
    const fallback = all.sort((a, b) => {
      const av = p.visited.has(a.id), bv = p.visited.has(b.id);
      return Number(av) - Number(bv) || (p.current ? window.haversineKm(p.current, a) - window.haversineKm(p.current, b) : 0);
    }).slice(0, 3);
    const routes = (p.recommendations.length ? p.recommendations.filter(isAllowed) : fallback).slice(0, 3);
    // A manually chosen city always leads the shelf, even when it wasn't suggested.
    if (p.destination && !routes.some(city => city.id === p.destination.id)) routes.splice(2, 1, p.destination);
    const suggested = routes[0]?.id;
    const openedFrom = useRef(null);
    useEffect(() => {
      if (p.traveling || !suggested || openedFrom.current === p.current?.id) return;
      openedFrom.current = p.current?.id;
      if (!p.destination) p.onSelect(suggested);
    }, [p.current?.id, suggested, p.traveling]);

    const dest = p.destination;
    const modes = (window.TRANSPORTS || []).map(mode => {
      const check = window.canTravel(p.current, dest, mode.id);
      const base = window.calcWorldCost(p.current, dest, mode, p.cabin);
      const pet = p.pet && window.calcPetFee ? window.calcPetFee(mode.id, base) : 0;
      const km = p.current && dest ? window.haversineKm(p.current, dest) : 0;
      const mult = window.CABIN_CLASSES?.[p.transport]?.find(x => x.id === p.cabin)?.timeMult || 1;
      const seconds = Math.round(Math.max(4500, Math.max(8000, Math.min(28000, km / ({ plane:900, subway:350, bus:90, ship:40 }[mode.id])* 3500)) * mult / Math.max(.1, p.speed)) / 1000);
      return { ...mode, check, price: base + pet, seconds };
    });
    const available = modes.filter(mode => mode.check.ok);
    const selectedMode = modes.find(m => m.id === p.transport);
    const insufficient = selectedMode?.price > p.balance;
    // One cultural work — a song, a film, a book — is the whole reason to go.
    const archiveItem = [...(dest?.media || []), ...(dest?.landmarks || []).flatMap(lm => lm.media || [])][0]
      || (dest?.country && window.COUNTRY_MEDIA?.[dest.country] || []).find(m => m.type !== 'FOOD');
    const cabins = window.CABIN_CLASSES?.[p.transport] || [];

    const pick = id => { p.onSelect(id); setDrawer(null); };
    const actions = [
      [c.collection, () => setDrawer('memories')],
      [c.account, p.onAccount],
      [c.language, p.onLang],
      [c.help, p.onHelp],
      [c.end, p.onEnd],
    ];
    const visibleCities = all.filter(city => `${city.name} ${city.name_zh} ${city.country}`.toLowerCase().includes(query.toLowerCase()));
    const flag = code => window.flagFor ? window.flagFor(code) : '';
    const countryOf = city => !city ? '' : (window.countryDisplay ? window.countryDisplay(city.country) : city.country);
    const alternates = routes.filter(city => city.id !== dest?.id).slice(0, 2);
    const stampCount = p.visited.size;

    return <main className="atlas-desk">
      <header className="ad-top">
        <div className="ad-brand">Pathfinder<i aria-hidden="true"/></div>
        <nav className="ad-nav">
          <button onClick={p.onLog} aria-label={c.journal} title={c.journal}><Icon name="book"/></button>
          <button onClick={p.onAlbum} aria-label={c.album} title={c.album}><Icon name="photo"/></button>
          <button onClick={() => setDrawer('menu')} aria-label={c.menu} title={c.menu} aria-haspopup="dialog"><Icon name="menu"/></button>
        </nav>
      </header>

      <section className="ad-here">
        <div className="ad-here-main">
          <p className="ad-kicker">{c.here}</p>
          <h1><span className="ad-flag" aria-hidden="true">{flag(p.current?.country)}</span>{nameOf(p.current, lang)}</h1>
          <button className="ad-here-link" type="button" onClick={p.onExplore} disabled={p.traveling}>{c.explore}<Icon name="arrow" size={14}/></button>
        </div>
        <div className="ad-funds">
          <small>{c.budget}</small>
          <strong><span>$</span>{p.balance.toLocaleString()}</strong>
        </div>
      </section>

      {p.traveling ? (
        <p className="ad-inflight">{c.traveling}</p>
      ) : (
      <>
        <section className="ad-next" aria-label={c.next}>
          <div className="ad-section-head">
            <p className="ad-kicker">{c.next}</p>
            <button className="ad-text-btn" type="button" onClick={() => setDrawer('cities')}>{c.all}<Icon name="arrow" size={15}/></button>
          </div>

          {dest ? (
            <div className="ad-hero">
              <CityPhoto city={dest} lang={lang} className="ad-hero-photo"/>
              <div className="ad-hero-body">
                <p className="ad-hero-country">{flag(dest.country)} {countryOf(dest)}{p.visited.has(dest.id) ? ` · ${c.visited}` : ''}</p>
                <h2>{nameOf(dest, lang)}</h2>
                {archiveItem && (
                  <p className="ad-hero-note">
                    <Icon name={archiveItem.type === 'SONG' ? 'note' : archiveItem.type === 'FILM' ? 'film' : 'book'} size={14}/>
                    {window.localizeMediaTitle?.(archiveItem.title) || archiveItem.title}
                    {archiveItem.year ? ` · ${archiveItem.year}` : ''}
                  </p>
                )}
              </div>
            </div>
          ) : (
            <div className="ad-hero ad-hero-empty"><div className="ad-hero-body"><h2>{c.choose}</h2></div></div>
          )}

          {alternates.length > 0 && (
            <div className="ad-alts">
              {alternates.map(city => (
                <button key={city.id} type="button" className="ad-alt" onClick={() => pick(city.id)}>
                  <CityPhoto city={city} lang={lang} className="ad-alt-photo"/>
                  <span className="ad-alt-name">{nameOf(city, lang)}</span>
                </button>
              ))}
            </div>
          )}
        </section>

        {dest && (
          <section className="ad-go" aria-label={c.ways}>
            <p className="ad-kicker">{c.ways}</p>
            <div className="ad-modes" role="group">
              {available.map(mode => (
                <button type="button" key={mode.id}
                        className={`ad-mode ${p.transport === mode.id ? 'is-selected' : ''}`}
                        aria-pressed={p.transport === mode.id}
                        onClick={() => p.onTransport(mode.id)}>
                  <span className="ad-mode-icon"><Icon name={mode.id}/></span>
                  <span className="ad-mode-copy">
                    <strong>{T(`trans.${mode.id}`)}</strong>
                    <small>{c[mode.id]}</small>
                  </span>
                  <span className="ad-mode-quote">
                    <strong>${mode.price}</strong>
                    <small>{mode.seconds}{c.seconds}</small>
                  </span>
                </button>
              ))}
            </div>

            {!p.firstActive && cabins.length > 1 && (
              <div className="ad-cabins" role="group" aria-label={c.cabin}>
                {cabins.map(cabin => (
                  <button key={cabin.id} type="button"
                          className={`ad-cabin ${p.cabin === cabin.id ? 'is-selected' : ''}`}
                          aria-pressed={p.cabin === cabin.id}
                          onClick={() => p.onCabin(cabin.id)}>
                    {lang === 'zh' ? (cabin.name_zh || cabin.name) : (cabin.name || cabin.name_zh)}
                  </button>
                ))}
              </div>
            )}

            <div className="ad-balance"><span>{c.left}</span><strong>${Math.max(0, p.balance - (selectedMode?.price || 0)).toLocaleString()}</strong></div>
            <button className="ad-depart" type="button" onClick={p.onDepart}
                    disabled={p.traveling || insufficient || !selectedMode?.check.ok}>
              {insufficient ? c.insufficient : c.begin}<Icon name="arrow"/>
            </button>
            <p className="ad-fare-note">{p.firstActive && (window.FREE_CITY_IDS || []).includes(dest.id) ? c.waived : c.additional}</p>
          </section>
        )}

        <section className="ad-stamps-row" aria-label={c.collection}>
          {p.firstActive ? (
            <>
              <p className="ad-kicker">{c.goal}</p>
              <div className="ad-stamps">
                {[0, 1, 2].map(i => (
                  <span key={i} className={`ad-stamp ${i < p.count ? 'is-collected' : ''}`}>
                    <Icon name={i < p.count ? 'check' : 'stamp'} size={17}/>
                  </span>
                ))}
              </div>
            </>
          ) : (
            <button type="button" className="ad-collection-link" onClick={() => setDrawer('memories')}>
              <span className="ad-kicker">{c.collection}</span>
              <strong>{stampCount} {stampCount === 1 ? c.stampsOne : c.stamps}</strong>
              <Icon name="arrow" size={15}/>
            </button>
          )}
        </section>
      </>
      )}

      {p.notice && <p role="status" className="ad-notice">{p.notice}</p>}

      {drawer && (
        <div className="ad-drawer-scrim" onClick={e => { if (e.target === e.currentTarget) setDrawer(null); }}>
          <section ref={dialogRef} className="ad-drawer" role="dialog" aria-modal="true"
                   aria-label={drawer === 'cities' ? c.all : drawer === 'memories' ? c.collection : c.menu}>
            <header>
              <h2>{drawer === 'cities' ? c.all : drawer === 'memories' ? c.collection : 'Pathfinder'}</h2>
              <button onClick={() => setDrawer(null)} aria-label={c.close}><Icon name="close"/></button>
            </header>
            {drawer === 'memories' ? (
              <div className="ad-city-list">
                {(window.CITIES || []).filter(city => p.visited.has(city.id)).map(city => (
                  <button key={city.id} onClick={() => { setDrawer(null); p.onMemory(city); }}>
                    <span><strong>{nameOf(city, lang)}</strong><small>{flag(city.country)} {countryOf(city)} · {c.revisit}</small></span>
                    <Icon name="book" size={17}/>
                  </button>
                ))}
              </div>
            ) : drawer === 'cities' ? (
              <>
                <label className="ad-search"><Icon name="search"/><input placeholder={c.search} aria-label={c.search} value={query} onChange={e => setQuery(e.target.value)}/></label>
                <div className="ad-city-list">
                  {visibleCities.length ? visibleCities.map(city => (
                    <button key={city.id} onClick={() => pick(city.id)}>
                      <span><strong>{nameOf(city, lang)}</strong><small>{flag(city.country)} {countryOf(city)}{p.visited.has(city.id) ? ` · ${c.visited}` : ''}</small></span>
                      <Icon name="arrow" size={17}/>
                    </button>
                  )) : <p className="ad-empty">{c.empty}</p>}
                </div>
              </>
            ) : (
              <div className="ad-menu-list">
                {actions.map(([label, action]) => (
                  <button key={label} onClick={() => { setDrawer(null); action?.(); }}>{label}<Icon name="arrow" size={17}/></button>
                ))}
              </div>
            )}
          </section>
        </div>
      )}
    </main>;
  }
  window.AtlasDesk = AtlasDesk;
})();
