// qiaopi-community.jsx — "网友寄存": letters written by today's visitors,
// kept distinct from the historical archive. Desktop mirrors the home 信路
// map (reusing projectLngLat / MAP_BOUNDS / COAST_PATHS from qiaopi-app-map.jsx)
// so today's letters trace their own routes across the same sea. Mobile keeps
// a simple card stack. Coordinates are resolved at publish time (see
// resolveLetterCoords in qiaopi-store.jsx); letters that resolve to nothing
// still appear in the index list, just not drawn on the map.

function CommunityPage({ letters, onBack, onOpen, onCompose, tweaks, width, height, isMobile = false }) {
  if (isMobile) return <CommunityCards letters={letters} onBack={onBack} onOpen={onOpen} onCompose={onCompose} width={width} height={height} />;
  return <CommunityMap letters={letters} onBack={onBack} onOpen={onOpen} onCompose={onCompose} tweaks={tweaks} width={width} height={height} />;
}

// ─── Desktop: 信路 map ─────────────────────────────────────────────────
function CommunityMap({ letters, onBack, onOpen, onCompose, tweaks, width, height }) {
  const [hoverId, setHoverId] = React.useState(null);
  const MAP_W = width, MAP_H = height;
  const list = letters || [];
  const plotted = list.filter(l => l.from && l.to && l.from.lng != null && l.from.lat != null && l.to.lng != null && l.to.lat != null);

  // Equirectangular world projection — fit the full globe to the viewport,
  // preserving the 2:1 aspect ratio and centering. Lets letters from anywhere
  // on Earth land in the right place.
  const s = Math.min(MAP_W / 360, MAP_H / 180);
  const ox = (MAP_W - 360 * s) / 2;
  const oy = (MAP_H - 180 * s) / 2;
  const P = (lng, lat) => ({ x: ox + (lng + 180) * s, y: oy + (90 - lat) * s });

  return (
    <div style={{ position: 'relative', width, height, overflow: 'hidden', fontFamily: '"Noto Serif SC", serif', color: '#2a1f17' }}>
      <PaperBg tone="sepia" style={{ position: 'absolute', inset: 0 }}>
        {/* HEADER */}
        <div style={{
          position: 'absolute', top: 36, left: 56, right: 56, zIndex: 3,
          display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start',
        }}>
          <div>
            <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 13, letterSpacing: 5, color: '#7a4828', textTransform: 'uppercase' }}>
              Letters from Today · 今人书
            </div>
            <h1 style={{ fontFamily: '"Noto Serif SC", serif', fontWeight: 900, fontSize: 64, lineHeight: 1, margin: '8px 0 6px', letterSpacing: 10 }}>网友寄存</h1>
            <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 20, color: '#5a3a22', letterSpacing: 1, maxWidth: 560 }}>
              今天的访客写下、并誊成侨批体的家书，循着同一片海各自寄出。
            </div>
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 14 }}>
            <button onClick={onBack} style={navBtn}>← 回到信路</button>
            <button onClick={onCompose} style={solidBtn}>+ 我也写一封</button>
          </div>
        </div>

        {/* MAP SVG — equirectangular world map, line-art coastlines */}
        <svg viewBox={`0 0 ${MAP_W} ${MAP_H}`} style={{ position: 'absolute', inset: 0, zIndex: 1 }}>
          <defs>
            <radialGradient id="cm-vignette" cx="50%" cy="45%" r="75%">
              <stop offset="0%" stopColor="rgba(245,236,214,0)" />
              <stop offset="100%" stopColor="rgba(155,90,45,0.30)" />
            </radialGradient>
            <filter id="cm-blur"><feGaussianBlur stdDeviation="0.3" /></filter>
          </defs>
          {/* graticule — every 30° */}
          {[-150, -120, -90, -60, -30, 0, 30, 60, 90, 120, 150].map((lng) => {
            const a = P(lng, 85), b = P(lng, -85);
            return <line key={'mer' + lng} x1={a.x} y1={a.y} x2={b.x} y2={b.y} stroke="#9b3a2a" strokeWidth="0.4" opacity="0.16" />;
          })}
          {[-60, -30, 0, 30, 60].map((lat) => {
            const a = P(-180, lat), b = P(180, lat);
            return <line key={'par' + lat} x1={a.x} y1={a.y} x2={b.x} y2={b.y} stroke="#9b3a2a" strokeWidth="0.4" opacity="0.16" />;
          })}
          {/* equator emphasis */}
          {(() => { const a = P(-180, 0), b = P(180, 0); return <line x1={a.x} y1={a.y} x2={b.x} y2={b.y} stroke="#6a3a26" strokeWidth="0.6" opacity="0.3" strokeDasharray="4 4" />; })()}
          {/* axis labels */}
          {[-120, -60, 0, 60, 120].map((lng) => {
            const p = P(lng, -85);
            return <text key={'lbl' + lng} x={p.x} y={p.y} fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="10" fill="#7a4828" opacity="0.45" textAnchor="middle">{Math.abs(lng)}°{lng < 0 ? 'W' : (lng > 0 ? 'E' : '')}</text>;
          })}
          {/* world land — line-art */}
          {WORLD_PATHS.map((ring, i) => {
            let path = '';
            for (let j = 0; j < ring.length; j += 2) {
              const p = P(ring[j], ring[j + 1]);
              path += (j === 0 ? 'M' : 'L') + ' ' + p.x.toFixed(1) + ' ' + p.y.toFixed(1) + ' ';
            }
            return <path key={i} d={path + 'Z'} fill="rgba(122,72,40,0.06)" stroke="#5a3a22" strokeWidth="0.9" opacity="0.55" filter="url(#cm-blur)" />;
          })}
          {/* ocean label */}
          <text x={P(150, -25).x} y={P(150, -25).y} fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="34" fill="#7a4828" opacity="0.12" textAnchor="middle" letterSpacing="4">Letters across the world</text>
          {/* arcs */}
          {plotted.map((l) => {
            const a = P(l.from.lng, l.from.lat);
            const b = P(l.to.lng, l.to.lat);
            const dx = b.x - a.x, dy = b.y - a.y;
            const seed = (idHash(l.id) % 13) / 13;
            const bow = 0.15 + seed * 0.18;
            const mx = (a.x + b.x) / 2 - dy * bow;
            const my = (a.y + b.y) / 2 + dx * bow;
            const active = hoverId === l.id;
            return (
              <g key={l.id} onClick={() => onOpen(l)} onMouseEnter={() => setHoverId(l.id)} onMouseLeave={() => setHoverId(null)} style={{ cursor: 'pointer' }}>
                <path d={`M ${a.x} ${a.y} Q ${mx} ${my} ${b.x} ${b.y}`} fill="none" stroke="transparent" strokeWidth="16" />
                <path d={`M ${a.x} ${a.y} Q ${mx} ${my} ${b.x} ${b.y}`}
                  fill="none" stroke={active ? '#9b3a2a' : '#6a3a26'}
                  strokeWidth={active ? 2.2 : 1} opacity={active ? 0.95 : 0.5}
                  strokeDasharray={active ? 'none' : '3 5'} style={{ transition: 'all .25s' }} />
              </g>
            );
          })}
          {/* dots */}
          {plotted.map((l) => {
            const a = P(l.from.lng, l.from.lat);
            const b = P(l.to.lng, l.to.lat);
            const active = hoverId === l.id;
            return (
              <g key={l.id + '-p'} pointerEvents="none">
                <circle cx={a.x} cy={a.y} r={active ? 6 : 3} fill="#9b3a2a" opacity={active ? 1 : 0.7} style={{ transition: 'all .25s' }} />
                {active && <text x={a.x + 10} y={a.y + 4} fontFamily="Noto Serif SC, serif" fontWeight="700" fontSize="15" fill="#2a1f17">{l.from.city}</text>}
                <circle cx={b.x} cy={b.y} r={active ? 6 : 3} fill="#2a1f17" opacity={active ? 1 : 0.7} style={{ transition: 'all .25s' }} />
                {active && <text x={b.x + 10} y={b.y + 4} fontFamily="Noto Serif SC, serif" fontWeight="700" fontSize="15" fill="#2a1f17">{l.to.city || l.to.region}</text>}
              </g>
            );
          })}
          {/* compass rose */}
          <g transform={`translate(${MAP_W - 90}, ${MAP_H - 90})`} opacity="0.5">
            <circle r="34" fill="none" stroke="#6a3a26" strokeWidth="0.8" />
            <path d="M 0 -30 L 5 0 L 0 30 L -5 0 Z" fill="#9b3a2a" opacity="0.75" />
            <path d="M -30 0 L 0 5 L 30 0 L 0 -5 Z" fill="#6a3a26" opacity="0.55" />
            <text y="-38" fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="12" fill="#6a3a26" textAnchor="middle">N</text>
          </g>
          <rect width={MAP_W} height={MAP_H} fill="url(#cm-vignette)" pointerEvents="none" />
        </svg>

        {/* 網寄 seal */}
        <div style={{ position: 'absolute', top: 150, right: 64, zIndex: 2 }}>
          <ChopSeal text="網寄" size={60} rotate={-8} />
        </div>

        {/* LEFT INDEX */}
        <div style={{ position: 'absolute', left: 56, top: 230, bottom: 56, width: 270, zIndex: 3 }}>
          <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 12, color: '#7a4828', letterSpacing: 3, textTransform: 'uppercase', marginBottom: 4 }}>
            Index · 寄存目录
          </div>
          <div style={{ fontFamily: '"Noto Serif SC", serif', fontSize: 10, color: '#7a4828', opacity: 0.7, letterSpacing: 1, marginBottom: 10 }}>
            共 {list.length} 通 · 已落点 {plotted.length} 通
          </div>
          {list.length === 0 ? (
            <div style={{ fontFamily: '"Noto Serif SC", serif', fontSize: 13, color: '#5a3a22', lineHeight: 1.8 }}>
              还没有人寄存家书。<br/>
              <button onClick={onCompose} style={{ ...solidBtn, marginTop: 14 }}>+ 写第一封</button>
            </div>
          ) : (
            <div style={{ display: 'flex', flexDirection: 'column', maxHeight: '100%', overflowY: 'auto', paddingRight: 8 }}>
              {list.map((l, i) => {
                const sel = hoverId === l.id;
                const noCoord = !(l.from && l.from.lng != null && l.to && l.to.lng != null);
                return (
                  <button key={l.id} onClick={() => onOpen(l)}
                    onMouseEnter={() => setHoverId(l.id)} onMouseLeave={() => setHoverId(null)}
                    style={{
                      textAlign: 'left', background: sel ? 'rgba(155,58,42,0.10)' : 'transparent',
                      border: 'none', borderBottom: '1px dotted rgba(106,58,38,0.35)',
                      padding: '4px 8px', cursor: 'pointer', fontFamily: 'inherit', color: 'inherit',
                      display: 'grid', gridTemplateColumns: '32px 1fr', gap: 8, alignItems: 'baseline',
                    }}>
                    <span style={{ fontFamily: '"IBM Plex Mono", monospace', fontSize: 10, color: '#7a4828' }}>№{String(i + 1).padStart(2, '0')}</span>
                    <span style={{ display: 'flex', alignItems: 'baseline', gap: 4, flexWrap: 'wrap' }}>
                      <span style={{ fontWeight: 700, fontSize: 13 }}>{l.from.city || '南洋'}</span>
                      <span style={{ opacity: 0.5 }}>→</span>
                      <span style={{ fontWeight: 700, fontSize: 13 }}>{l.to.city || l.to.region || '家'}</span>
                      {l.sender.name && l.sender.name !== '—' && (
                        <span style={{ fontSize: 10, color: '#7a4828', marginLeft: 4, opacity: 0.85 }}>· {l.sender.name}</span>
                      )}
                      {noCoord && <span title="未在地图落点" style={{ fontSize: 9, color: '#9b3a2a', opacity: 0.6 }}>（未落点）</span>}
                    </span>
                  </button>
                );
              })}
            </div>
          )}
        </div>

        {/* RIGHT: hover preview or intro */}
        <div style={{ position: 'absolute', right: 56, top: 230, bottom: 56, width: 320, zIndex: 3, display: 'flex', flexDirection: 'column' }}>
          {hoverId
            ? <CommunityPreview letter={list.find(l => l.id === hoverId)} index={list.findIndex(l => l.id === hoverId)} onOpen={onOpen} />
            : <CommunityIntro count={list.length} />}
        </div>
      </PaperBg>
    </div>
  );
}

function CommunityIntro({ count }) {
  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', justifyContent: 'flex-end', padding: '24px 22px', borderLeft: '1px solid rgba(106,58,38,0.35)' }}>
      <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 13, color: '#7a4828', letterSpacing: 2 }}>↳ hover or click a route</div>
      <div style={{ fontFamily: '"Noto Serif SC", serif', fontSize: 13, color: '#5a3a22', marginTop: 6, letterSpacing: 2 }}>　将光标移至航路，<br/>　点击即可展信细读。</div>
      <div style={{ marginTop: 28, paddingTop: 20, borderTop: '1px dotted rgba(106,58,38,0.35)', fontFamily: '"Noto Serif SC", serif', fontSize: 13, color: '#5a3a22', lineHeight: 1.7 }}>
        <strong style={{ fontSize: 18, fontWeight: 900 }}>网友寄存</strong>
        <div style={{ fontSize: 12, marginTop: 10, lineHeight: 1.7 }}>
          这里的 {count} 通家书，都是今人用大白话写下、再誊成侨批体寄存的。它们不属于历史档案，却用同样的方式，越过同一片海。
        </div>
      </div>
    </div>
  );
}

function CommunityPreview({ letter, index, onOpen }) {
  if (!letter) return null;
  const dt = letter.createdAt ? new Date(letter.createdAt) : null;
  const dateStr = dt ? `${dt.getFullYear()}.${String(dt.getMonth() + 1).padStart(2, '0')}.${String(dt.getDate()).padStart(2, '0')}` : '';
  return (
    <div style={{ flex: 1, display: 'flex', flexDirection: 'column', borderLeft: '1px solid rgba(106,58,38,0.45)', paddingLeft: 22, animation: 'cp-in .25s' }}>
      <style>{`@keyframes cp-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }`}</style>
      <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 12, color: '#7a4828', letterSpacing: 3, textTransform: 'uppercase' }}>網寄 · 第 {index + 1} 通</div>
      <div style={{ fontFamily: '"EB Garamond", serif', fontSize: 28, lineHeight: 1, fontWeight: 700, marginTop: 10, marginBottom: 6 }}>{dateStr}</div>
      <div style={{ fontFamily: '"Noto Serif SC", serif', fontSize: 22, fontWeight: 700, lineHeight: 1.3 }}>
        {letter.from.city || '南洋'} <span style={{ color: '#9b3a2a' }}>→</span> {letter.to.city || letter.to.region || '家'}
      </div>
      <div style={{ marginTop: 16, display: 'grid', gridTemplateColumns: 'auto 1fr', columnGap: 16, rowGap: 6 }}>
        <Meta label="寄信人 Sender">{letter.sender.name}</Meta>
        <Meta label="收信人 To">{letter.recipient.name}</Meta>
        {letter.amount && <Meta label="汇款 Remittance">{letter.amount.value} {letter.amount.currency}</Meta>}
        <Meta label="日期 Dated">{letter.postmark || <span style={{ opacity: 0.5 }}>—</span>}</Meta>
      </div>
      {letter.bodyEn && (
        <div style={{
          marginTop: 16, padding: '12px 0 0', borderTop: '1px dotted rgba(106,58,38,0.35)',
          fontFamily: '"LXGW WenKai", "Noto Serif SC", serif', fontSize: 13, lineHeight: 1.75, color: '#2a1f17',
          flex: 1, overflow: 'hidden', maskImage: 'linear-gradient(to bottom, black 70%, transparent 100%)',
        }}>{letter.bodyEn.slice(0, 240)}…</div>
      )}
      <button onClick={() => onOpen(letter)} style={{
        marginTop: 10, background: '#9b3a2a', color: '#f5ecd6', border: 'none',
        fontFamily: '"Noto Serif SC", serif', fontSize: 13, letterSpacing: 4, padding: '12px 18px', cursor: 'pointer',
        display: 'flex', alignItems: 'center', justifyContent: 'center', gap: 10,
      }}>
        <span>展信细读</span>
        <span style={{ opacity: 0.7, fontFamily: '"EB Garamond", serif', fontStyle: 'italic', letterSpacing: 1 }}>open →</span>
      </button>
    </div>
  );
}

// small stable hash for arc curvature
function idHash(s) {
  let h = 0;
  for (let i = 0; i < (s || '').length; i++) h = (h * 31 + s.charCodeAt(i)) >>> 0;
  return h;
}

// ─── Mobile: card stack ────────────────────────────────────────────────
function CommunityCards({ letters, onBack, onOpen, onCompose, width, height }) {
  const list = letters || [];
  return (
    <div style={{ position: 'relative', width, height, overflow: 'hidden', fontFamily: '"Noto Serif SC", serif', color: '#2a1f17' }}>
      <PaperBg tone="sepia" style={{ position: 'absolute', inset: 0 }}>
        <div style={{ position: 'absolute', top: 22, left: 20, right: 20, zIndex: 3 }}>
          <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 12, letterSpacing: 4, color: '#7a4828', textTransform: 'uppercase' }}>Letters from Today · 今人书</div>
          <h1 style={{ fontFamily: '"Noto Serif SC", serif', fontWeight: 900, fontSize: 38, lineHeight: 1, margin: '6px 0 6px', letterSpacing: 8 }}>网友寄存</h1>
          <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 14, color: '#5a3a22' }}>今人写下、誊成侨批体的家书。</div>
          <div style={{ display: 'flex', gap: 8, marginTop: 12 }}>
            <button onClick={onBack} style={{ ...navBtn, padding: '6px 10px', fontSize: 12 }}>← 信路</button>
            <button onClick={onCompose} style={{ ...solidBtn, padding: '6px 12px', fontSize: 12 }}>+ 写一封</button>
          </div>
        </div>
        <div style={{ position: 'absolute', inset: '170px 0 0', overflowY: 'auto', WebkitOverflowScrolling: 'touch', padding: '0 20px 40px', zIndex: 2 }}>
          {list.length === 0 ? (
            <div style={{ textAlign: 'center', color: '#5a3a22', border: '1px dashed rgba(106,58,38,0.4)', padding: '40px 24px', marginTop: 24 }}>
              <div style={{ fontSize: 17, fontWeight: 700, marginBottom: 10 }}>还没有人寄存家书</div>
              <button onClick={onCompose} style={solidBtn}>+ 写第一封</button>
            </div>
          ) : (
            <div style={{ display: 'grid', gap: 16 }}>
              {list.map((l) => <CommunityCard key={l.id} letter={l} onOpen={onOpen} />)}
            </div>
          )}
        </div>
      </PaperBg>
    </div>
  );
}

function CommunityCard({ letter, onOpen }) {
  const [hover, setHover] = React.useState(false);
  const dt = letter.createdAt ? new Date(letter.createdAt) : null;
  const dateStr = dt ? `${dt.getFullYear()}.${String(dt.getMonth() + 1).padStart(2, '0')}.${String(dt.getDate()).padStart(2, '0')}` : '';
  return (
    <button onClick={() => onOpen(letter)}
      onMouseEnter={() => setHover(true)} onMouseLeave={() => setHover(false)}
      style={{
        textAlign: 'left', cursor: 'pointer', display: 'block', width: '100%',
        background: 'rgba(255,250,240,0.7)', border: '1px solid rgba(106,58,38,0.3)',
        padding: '20px 22px', borderRadius: 2, color: '#2a1f17',
        boxShadow: hover ? '0 14px 34px rgba(60,30,12,0.22)' : '0 4px 14px rgba(60,30,12,0.1)',
        transform: hover ? 'translateY(-4px)' : 'translateY(0)',
        transition: 'transform .18s, box-shadow .18s', fontFamily: '"Noto Serif SC", serif',
      }}>
      <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', marginBottom: 10 }}>
        <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 11, color: '#9b3a2a', letterSpacing: 2, textTransform: 'uppercase' }}>網寄 · web letter</div>
        <div style={{ fontFamily: '"IBM Plex Mono", monospace', fontSize: 11, color: '#7a4828' }}>{dateStr}</div>
      </div>
      <div style={{ fontSize: 14, fontWeight: 700, letterSpacing: 1, marginBottom: 8, lineHeight: 1.5 }}>
        {(letter.from.city || '南洋')} {letter.sender.name} <span style={{ opacity: 0.55, fontWeight: 400 }}>寄</span> {letter.recipient.name !== '—' ? letter.recipient.name : (letter.to.region || letter.to.city)}
      </div>
      <div style={{
        fontFamily: '"LXGW WenKai", "Noto Serif SC", serif', fontSize: 13, lineHeight: 1.8,
        color: '#3a2818', opacity: 0.85, display: '-webkit-box', WebkitLineClamp: 3, WebkitBoxOrient: 'vertical', overflow: 'hidden',
      }}>
        {(letter.bodyEn || letter.bodyZh || '').slice(0, 80)}
      </div>
      {letter.amount && (
        <div style={{ marginTop: 12, fontSize: 12, color: '#9b3a2a', letterSpacing: 1 }}>汇 {letter.amount.value} {letter.amount.currency}</div>
      )}
    </button>
  );
}

const navBtn = {
  background: 'transparent', border: '1px solid rgba(106,58,38,0.4)',
  color: '#5a3a22', fontFamily: '"EB Garamond", serif', fontStyle: 'italic',
  fontSize: 13, letterSpacing: 2, padding: '8px 12px', cursor: 'pointer',
};
const solidBtn = {
  background: '#2a1f17', border: 'none', color: '#f1e8d4',
  fontFamily: '"Noto Serif SC", serif', fontSize: 13, letterSpacing: 3,
  padding: '10px 16px', cursor: 'pointer',
};
