// qiaopi-app.jsx — Main app shell, routes between MapPage and LetterPage
// Owns view state ('map' | 'letter') + selected letter + Tweaks state.

function QiaopiApp() {
  const [view, setView] = React.useState('map'); // 'map' | 'letter'
  const [activeId, setActiveId] = React.useState(null);
  const [hoverId, setHoverId] = React.useState(null);
  const [transition, setTransition] = React.useState(null); // 'to-letter' | 'to-map' | null

  const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
    "paperTone": "cream",
    "bodyFont": "kaiti",
    "defaultLang": "zh",
    "showAnnotations": true,
    "accentColor": "#9b3a2a"
  }/*EDITMODE-END*/;
  const [tweaks, setTweak] = useTweaks(TWEAK_DEFAULTS);

  // True full-viewport fill — pass the real viewport dims into the layout.
  // Internal absolute-positioned elements adapt because they're computed
  // from MAP_W/MAP_H, not hardcoded.
  const [vp, setVp] = React.useState({ w: window.innerWidth, h: window.innerHeight });
  React.useEffect(() => {
    const onResize = () => setVp({ w: window.innerWidth, h: window.innerHeight });
    window.addEventListener('resize', onResize);
    return () => window.removeEventListener('resize', onResize);
  }, []);
  const scale = 1;
  const size = { w: vp.w, h: vp.h };
  const isMobile = vp.w < 768;

  const activeLetter = activeId ? QIAOPI.find(l => l.id === activeId) : null;

  const goLetter = (letter) => {
    setActiveId(letter.id);
    setTransition('to-letter');
    setTimeout(() => {
      setView('letter');
      setTimeout(() => setTransition(null), 400);
    }, 350);
  };
  const goMap = () => {
    setTransition('to-map');
    setTimeout(() => {
      setView('map');
      setTimeout(() => setTransition(null), 400);
    }, 350);
  };
  const pickInLetter = (letter) => {
    setActiveId(letter.id);
  };

  return (
    <React.Fragment>
      {/* Full-viewport canvas */}
      <div style={{
        width: '100vw', height: '100vh', position: 'relative', overflow: 'hidden',
      }}>
        <div style={{
          width: size.w, height: size.h, position: 'relative', overflow: 'hidden',
        }}>
        {view === 'map' && (
          <MapPage
            letters={QIAOPI}
            hoverId={hoverId}
            setHoverId={setHoverId}
            onOpen={goLetter}
            tweaks={tweaks}
            width={size.w}
            height={size.h}
            isMobile={isMobile}
          />
        )}
        {view === 'letter' && activeLetter && (
          <LetterPage
            letter={activeLetter}
            letters={QIAOPI}
            onBack={goMap}
            onPick={pickInLetter}
            tweaks={tweaks}
            width={size.w}
            height={size.h}
            isMobile={isMobile}
          />
        )}

        {/* Transition overlay (ink wash) */}
        {transition && (
          <div style={{
            position: 'fixed', inset: 0,
            background: transition === 'to-letter'
              ? 'radial-gradient(circle at 50% 50%, #1a130c 0%, rgba(26,19,12,0) 100%)'
              : 'radial-gradient(circle at 50% 50%, #f1e8d4 0%, rgba(241,232,212,0) 100%)',
            zIndex: 200,
            pointerEvents: 'none',
            animation: 'ink-wash .7s ease-out',
          }}>
            <style>{`@keyframes ink-wash { 0% { opacity: 0; } 40% { opacity: 1; } 100% { opacity: 0; } }`}</style>
          </div>
        )}
        </div>
      </div>

      {/* Background music — discreet bottom-left */}
      <AmbientPlayer isMobile={isMobile} />

      {/* Tweaks panel */}
      <TweaksPanel title="Tweaks · 调式">
        <TweakSection label="Paper · 纸张" />
        <TweakRadio label="Paper tone"
          value={tweaks.paperTone}
          onChange={(v) => setTweak('paperTone', v)}
          options={[
            { value: 'cream', label: '米色' },
            { value: 'rice', label: '宣纸' },
            { value: 'sepia', label: '古黄' },
          ]} />
        <TweakRadio label="Body font"
          value={tweaks.bodyFont}
          onChange={(v) => setTweak('bodyFont', v)}
          options={[
            { value: 'kaiti', label: '楷书' },
            { value: 'songti', label: '宋体' },
          ]} />
        <TweakSection label="Map · 信路" />
        <TweakToggle label="Show events"
          value={tweaks.showAnnotations}
          onChange={(v) => setTweak('showAnnotations', v)} />
        <TweakSection label="Letter · 信文" />
        <TweakRadio label="Default lang"
          value={tweaks.defaultLang}
          onChange={(v) => setTweak('defaultLang', v)}
          options={[
            { value: 'zh', label: '中文' },
            { value: 'both', label: '双语' },
          ]} />
        <TweakButton label="🎲 Random letter · 随机一封"
          onClick={() => goLetter(QIAOPI[Math.floor(Math.random() * QIAOPI.length)])} />
      </TweaksPanel>
    </React.Fragment>
  );
}

// ─── Ambient music player ───────────────────────────────────────────
// Discreet bottom-left widget. Default off (browsers block autoplay anyway).
// Tracks are looked up dynamically — files that 404 are skipped silently.
const AMBIENT_TRACKS = [
  { file: 'audio/season.mp3', title: '等你的季节', sub: '刘诗诗 · 《步步惊心》OST' },
  { file: 'audio/jie.mp3',    title: '劫',          sub: '音频怪物 · 《琉璃》OST' },
];

function AmbientPlayer({ isMobile = false }) {
  const [available, setAvailable] = React.useState([]);
  const [playing, setPlaying] = React.useState(false);
  const [idx, setIdx] = React.useState(0);
  const audioRef = React.useRef(null);

  // Probe which mp3 files actually exist
  React.useEffect(() => {
    Promise.all(AMBIENT_TRACKS.map(t =>
      fetch(t.file, { method: 'HEAD' }).then(r => r.ok ? t : null).catch(() => null)
    )).then(rs => setAvailable(rs.filter(Boolean)));
  }, []);

  if (available.length === 0) return null;
  const cur = available[idx % available.length];

  const toggle = () => {
    const a = audioRef.current;
    if (!a) return;
    if (playing) { a.pause(); setPlaying(false); }
    else { a.play().then(() => setPlaying(true)).catch(() => setPlaying(false)); }
  };
  const next = () => {
    setIdx((idx + 1) % available.length);
    setTimeout(() => {
      const a = audioRef.current;
      if (a && playing) a.play().catch(() => {});
    }, 100);
  };

  // Mobile: compact round button only — title hidden, tap to play, long-press for next
  if (isMobile) {
    return (
      <div style={{
        position: 'fixed', left: 12, bottom: 124, zIndex: 2147483646,
      }}>
        <button onClick={toggle} title={cur.title + (playing ? ' · 暂停' : ' · 播放')}
          onContextMenu={(e) => { e.preventDefault(); if (available.length > 1) next(); }}
          style={{
            width: 36, height: 36, borderRadius: '50%',
            background: playing ? 'rgba(200,164,90,0.95)' : 'rgba(20,12,6,0.75)',
            border: '1px solid rgba(200,164,90,0.5)',
            color: playing ? '#1a130c' : '#c8a45a',
            cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
            fontSize: 13, padding: 0, backdropFilter: 'blur(8px)',
            boxShadow: '0 4px 14px rgba(0,0,0,0.4)',
          }}>{playing ? '❚❚' : '▶'}</button>
        <audio ref={audioRef} src={cur.file} loop={available.length === 1}
          onEnded={() => { if (available.length > 1) next(); }}
          preload="none" />
      </div>
    );
  }

  return (
    <div style={{
      position: 'fixed', left: 16, bottom: 16, zIndex: 2147483646,
      display: 'flex', alignItems: 'center', gap: 10,
      padding: '8px 14px 8px 10px',
      background: 'rgba(20,12,6,0.78)', backdropFilter: 'blur(8px)',
      border: '1px solid rgba(200,164,90,0.28)', borderRadius: 999,
      color: '#e0c98a', fontFamily: '"Noto Serif SC", serif',
      boxShadow: '0 10px 28px rgba(0,0,0,0.45)',
      maxWidth: 320,
    }}>
      <button onClick={toggle} title={playing ? '暂停' : '播放'} style={{
        width: 32, height: 32, borderRadius: '50%',
        background: playing ? '#c8a45a' : 'transparent',
        border: '1px solid rgba(200,164,90,0.6)',
        color: playing ? '#1a130c' : '#c8a45a',
        cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
        fontSize: 14, flexShrink: 0,
      }}>{playing ? '❚❚' : '▶'}</button>
      <div style={{ minWidth: 0, lineHeight: 1.25 }}>
        <div style={{ fontSize: 12, fontWeight: 700, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis' }}>{cur.title}</div>
      </div>
      {available.length > 1 && (
        <button onClick={next} title="下一曲" style={{
          background: 'transparent', border: 'none', color: '#c8a45a',
          cursor: 'pointer', fontSize: 14, padding: '0 4px', flexShrink: 0,
        }}>⏭</button>
      )}
      <audio ref={audioRef} src={cur.file} loop={available.length === 1}
        onEnded={() => { if (available.length > 1) next(); }}
        preload="none" />
    </div>
  );
}

ReactDOM.createRoot(document.getElementById('root')).render(<QiaopiApp />);
