// qiaopi-app-map.jsx — Map landing page
// Big atlas-style map of the South China Sea + arched routes. Click a route,
// a list-item, or a timeline dot → onOpen(letter) bubbles up to the App.

function MapPage({ letters, hoverId, setHoverId, onOpen, tweaks, width, height }) {
  const [yearRange] = React.useState([1880, 1970]);
  // Show all letters; if year is known, also enforce range
  const visible = letters.filter(l => l.year == null || (l.year >= yearRange[0] && l.year <= yearRange[1]));
  const MAP_W = width, MAP_H = height;

  return (
    <div style={{ position: 'relative', width, height, overflow: 'hidden', fontFamily: '"Noto Serif SC", serif', color: '#2a1f17' }}>
      <PaperBg tone={tweaks.paperTone} 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',
            }}>An Interactive Archive · 互动档案</div>
            <h1 style={{
              fontFamily: '"Noto Serif SC", serif', fontWeight: 900,
              fontSize: 72, lineHeight: 1, margin: '8px 0 6px', letterSpacing: 10,
            }}>南洋信路</h1>
            <div style={{
              fontFamily: '"EB Garamond", serif', fontStyle: 'italic',
              fontSize: 22, color: '#5a3a22', letterSpacing: 1,
            }}>Letters from the South Seas, 1880–1970</div>
          </div>
          <div style={{ display: 'flex', gap: 8, alignItems: 'center', marginTop: 16 }}>
            <NavBtn>关于侨批 · About</NavBtn>
            <NavBtn>档案 · All Letters</NavBtn>
            <button style={primaryBtn}>+ 寄存家信</button>
          </div>
        </div>

        {/* MAP SVG */}
        <svg viewBox={`0 0 ${MAP_W} ${MAP_H}`} style={{ position: 'absolute', inset: 0, zIndex: 1 }}>
          <defs>
            <pattern id="atlas-grid" width="60" height="60" patternUnits="userSpaceOnUse">
              <path d="M60 0 L0 0 0 60" fill="none" stroke="#9b3a2a" strokeWidth="0.4" opacity="0.18" />
            </pattern>
            <radialGradient id="atlas-vignette" cx="50%" cy="40%" r="70%">
              <stop offset="0%" stopColor="rgba(245,236,214,0)" />
              <stop offset="100%" stopColor="rgba(155,90,45,0.25)" />
            </radialGradient>
            <filter id="atlas-blur"><feGaussianBlur stdDeviation="0.4" /></filter>
          </defs>
          <rect width={MAP_W} height={MAP_H} fill="url(#atlas-grid)" />
          {/* meridian labels */}
          {[100, 110, 120].map(lng => {
            const p = projectLngLat(lng, MAP_BOUNDS.minLat, MAP_W, MAP_H);
            return <text key={lng} x={p.x} y={MAP_H - 14} fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="11" fill="#7a4828" opacity="0.55" textAnchor="middle">{lng}°E</text>;
          })}
          {[0, 10, 20].map(lat => {
            const p = projectLngLat(MAP_BOUNDS.minLng, lat, MAP_W, MAP_H);
            return <text key={lat} x={14} y={p.y + 4} fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="11" fill="#7a4828" opacity="0.55">{lat}°N</text>;
          })}
          {/* Sea label */}
          <text x={MAP_W * 0.55} y={MAP_H * 0.5}
            fontFamily="EB Garamond, serif" fontStyle="italic"
            fontSize="58" fill="#7a4828" opacity="0.16" textAnchor="middle" letterSpacing="6">
            South China Sea
          </text>
          <text x={MAP_W * 0.55} y={MAP_H * 0.5 + 44}
            fontFamily="Noto Serif SC, serif"
            fontSize="26" fill="#7a4828" opacity="0.26" textAnchor="middle" letterSpacing="20">
            南　海
          </text>
          {/* coastlines */}
          {COAST_PATHS.map((d, i) => {
            const pts = d.split(/[\sLMZ]+/).filter(Boolean);
            let path = '';
            for (let j = 0; j < pts.length; j += 2) {
              const lng = parseFloat(pts[j]), lat = parseFloat(pts[j + 1]);
              if (isNaN(lng) || isNaN(lat)) continue;
              const p = projectLngLat(lng, lat, MAP_W, MAP_H);
              path += (j === 0 ? 'M' : 'L') + ' ' + p.x.toFixed(1) + ' ' + p.y.toFixed(1) + ' ';
            }
            return <path key={i} d={path} fill="none" stroke="#5a3a22" strokeWidth="1.4" opacity="0.55" filter="url(#atlas-blur)" />;
          })}
          {/* arcs — non-featured first (dim), featured on top (bold) */}
          {[...visible].sort((a, b) => (a.featured ? 1 : 0) - (b.featured ? 1 : 0)).map((l) => {
            const a = projectLngLat(l.from.lng, l.from.lat, MAP_W, MAP_H);
            const b = projectLngLat(l.to.lng, l.to.lat, MAP_W, MAP_H);
            const dx = b.x - a.x, dy = b.y - a.y;
            // Stable random-ish curvature so overlapping routes spread apart
            const seed = (parseInt(l.ssno || '0', 10) % 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;
            const isFeatured = l.featured;
            return (
              <g key={l.id}
                onClick={() => isFeatured && onOpen(l)}
                onMouseEnter={() => setHoverId(l.id)}
                onMouseLeave={() => setHoverId(null)}
                style={{ cursor: isFeatured ? 'pointer' : 'default' }}>
                <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' : (isFeatured ? '#6a3a26' : '#8b6a4a')}
                  strokeWidth={active ? 2.2 : (isFeatured ? 1 : 0.6)}
                  opacity={active ? 0.95 : (isFeatured ? 0.55 : 0.22)}
                  strokeDasharray={active ? 'none' : (isFeatured ? '3 5' : '2 4')}
                  style={{ transition: 'all .25s' }} />
              </g>
            );
          })}
          {/* dots */}
          {visible.map((l) => {
            const a = projectLngLat(l.from.lng, l.from.lat, MAP_W, MAP_H);
            const b = projectLngLat(l.to.lng, l.to.lat, MAP_W, MAP_H);
            const active = hoverId === l.id;
            return (
              <g key={l.id + '-p'} pointerEvents="none">
                <circle cx={a.x} cy={a.y} r={active ? 6 : 3.5} 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}<tspan fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="12" dx="6" opacity="0.7">{l.from.cityEn}</tspan></text>}
                <circle cx={b.x} cy={b.y} r={active ? 6 : 3.5} 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}<tspan fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="12" dx="6" opacity="0.7">{l.to.cityEn}</tspan></text>}
              </g>
            );
          })}
          {/* compass rose */}
          <g transform={`translate(${MAP_W - 110}, ${MAP_H - 250})`} opacity="0.55">
            <circle r="46" fill="none" stroke="#6a3a26" strokeWidth="0.8" />
            <circle r="32" fill="none" stroke="#6a3a26" strokeWidth="0.5" strokeDasharray="2 3" />
            <path d="M 0 -42 L 6 0 L 0 42 L -6 0 Z" fill="#9b3a2a" opacity="0.75" />
            <path d="M -42 0 L 0 6 L 42 0 L 0 -6 Z" fill="#6a3a26" opacity="0.55" />
            <text y="-52" fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="13" fill="#6a3a26" textAnchor="middle">N</text>
            <text y="62" fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="13" fill="#6a3a26" textAnchor="middle">S</text>
            <text x="58" y="4" fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="13" fill="#6a3a26">E</text>
            <text x="-58" y="4" fontFamily="EB Garamond, serif" fontStyle="italic" fontSize="13" fill="#6a3a26" textAnchor="end">W</text>
          </g>
          <rect width={MAP_W} height={MAP_H} fill="url(#atlas-vignette)" pointerEvents="none" />
        </svg>

        {/* LEFT INDEX */}
        <div style={{
          position: 'absolute', left: 56, top: 195, bottom: 150,
          width: 270, zIndex: 3, pointerEvents: 'auto',
        }}>
          <div style={{
            fontFamily: '"EB Garamond", serif', fontStyle: 'italic',
            fontSize: 12, color: '#7a4828', letterSpacing: 3, textTransform: 'uppercase', marginBottom: 4,
          }}>
            Index of Letters · 信目
          </div>
          <div style={{
            fontFamily: '"Noto Serif SC", serif', fontSize: 10, color: '#7a4828',
            opacity: 0.7, letterSpacing: 1, marginBottom: 10,
          }}>
            精选 {visible.filter(l => l.hasLetterContent).length} 通 · 共 {visible.length} 通 · 可下滑
          </div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 0, maxHeight: '100%', overflowY: 'auto', paddingRight: 8 }}>
            {visible.map((l, i) => {
              const sel = hoverId === l.id;
              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: '3px 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' }}>
                    {l.hasLetterContent && (
                      <span title="含信纸内文 has letter content"
                        style={{ fontSize: 9, padding: '1px 4px', background: 'rgba(155,58,42,0.15)', color: '#9b3a2a',
                          fontFamily: '"EB Garamond", serif', fontStyle: 'italic', letterSpacing: 1, borderRadius: 2 }}>
                        信
                      </span>
                    )}
                    <span style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 13, color: '#7a4828' }}>{l.year || '近代'}</span>
                    <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}</span>
                    {l.sender.name && l.sender.name !== '—' && (
                      <span style={{ fontSize: 10, color: '#7a4828', marginLeft: 4, opacity: 0.85 }}>· {l.sender.name}</span>
                    )}
                  </span>
                </button>
              );
            })}
          </div>
        </div>

        {/* RIGHT: Definition / Selected preview */}
        <div style={{
          position: 'absolute', right: 56, top: 220, bottom: 160,
          width: 320, zIndex: 3,
          display: 'flex', flexDirection: 'column',
        }}>
          {hoverId
            ? <RoutePreview letter={letters.find(l => l.id === hoverId)} onOpen={onOpen} />
            : <DefinitionPanel />
          }
        </div>

        {/* BOTTOM TIMELINE */}
        <Timeline range={yearRange} letters={letters} focusId={hoverId} onPick={onOpen} onHover={setHoverId} showAnnots={tweaks.showAnnotations} />
      </PaperBg>
    </div>
  );
}

const NavBtn = ({ children }) => (
  <button style={{
    background: 'transparent', border: 'none',
    fontFamily: '"EB Garamond", serif', fontStyle: 'italic',
    fontSize: 13, letterSpacing: 1, color: '#5a3a22', cursor: 'pointer',
    padding: '8px 12px',
  }}>{children}</button>
);
const primaryBtn = {
  background: '#2a1f17', border: 'none',
  fontFamily: '"Noto Serif SC", serif',
  fontSize: 13, letterSpacing: 4,
  color: '#f1e8d4', cursor: 'pointer',
  padding: '10px 18px',
};

function DefinitionPanel() {
  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: 32, paddingTop: 20, borderTop: '1px dotted rgba(106,58,38,0.35)',
        fontFamily: '"Noto Serif SC", serif', fontSize: 13, color: '#5a3a22', lineHeight: 1.7,
      }}>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 10 }}>
          <strong style={{ fontSize: 22, fontFamily: '"Noto Serif SC", serif', fontWeight: 900 }}>侨批</strong>
          <span style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 14, color: '#7a4828' }}>qiáo · pī</span>
          <span style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 12, opacity: 0.6 }}>n.</span>
        </div>
        <div style={{
          fontFamily: '"EB Garamond", serif', fontStyle: 'italic',
          fontSize: 13, opacity: 0.88, marginTop: 8, lineHeight: 1.6,
        }}>
          A combined letter & remittance sent home by an overseas Chinese, 1820s–1970s. From <i>"piao"</i> (批 — letter) in Hokkien/Teochew. Listed on UNESCO's Memory of the World, 2013.
        </div>
        <div style={{ fontSize: 12, marginTop: 10, lineHeight: 1.65 }}>
          一封批信，半笺家书，半张汇款单。一百五十年间，从槟城、新加坡、曼谷、马尼拉，越海寄回闽粤侨乡。
        </div>
      </div>
    </div>
  );
}

function RoutePreview({ letter, onOpen }) {
  if (!letter) return null;
  return (
    <div style={{
      flex: 1, display: 'flex', flexDirection: 'column',
      borderLeft: '1px solid rgba(106,58,38,0.45)', paddingLeft: 22,
      animation: 'rp-in .25s',
    }}>
      <style>{`@keyframes rp-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',
      }}>Letter · 第 {QIAOPI.indexOf(letter) + 1} 通</div>
      <div style={{
        fontFamily: '"EB Garamond", serif', fontSize: letter.year ? 60 : 28, lineHeight: 1, fontWeight: 700,
        marginTop: 10, marginBottom: 4, fontStyle: letter.year ? 'italic' : 'normal',
      }}>{letter.year || '近代'}</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}
      </div>
      <div style={{
        fontFamily: '"EB Garamond", serif', fontStyle: 'italic',
        fontSize: 13, color: '#5a3a22', marginTop: 2,
      }}>{letter.from.cityEn}, {letter.from.countryEn} → {letter.to.regionEn}</div>

      <div style={{ marginTop: 18, display: 'grid', gridTemplateColumns: 'auto 1fr', columnGap: 16, rowGap: 6 }}>
        <Meta label="寄信人 Sender">{letter.sender.name}{letter.sender.nameEn && <> · <i>{letter.sender.nameEn}</i></>}</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.bodyZh ? (
        <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', position: 'relative',
          maskImage: 'linear-gradient(to bottom, black 70%, transparent 100%)',
        }}>
          <span style={{ fontSize: 28, fontWeight: 700, float: 'left', lineHeight: 1, marginRight: 4, marginTop: 2, color: '#9b3a2a' }}>{letter.bodyZh.charAt(0)}</span>
          {letter.bodyZh.slice(1, 280)}…
        </div>
      ) : letter.scans && letter.scans.length ? (
        <div style={{
          marginTop: 16, padding: '12px 0 0', borderTop: '1px dotted rgba(106,58,38,0.35)',
          flex: 1, display: 'flex', alignItems: 'center', justifyContent: 'center',
          overflow: 'hidden',
        }}>
          <img src={letter.scans[0]} alt={letter.title}
            style={{ maxWidth: '100%', maxHeight: '100%', objectFit: 'contain', boxShadow: '0 6px 16px rgba(40,20,10,0.4)' }} />
        </div>
      ) : (
        <div style={{
          marginTop: 16, padding: '12px 0 0', borderTop: '1px dotted rgba(106,58,38,0.35)',
          flex: 1, fontFamily: '"EB Garamond", serif', fontStyle: 'italic',
          fontSize: 12, color: '#7a4828', opacity: 0.7,
        }}>
          原信尚未释读 · transcription pending
        </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 the letter →</span>
      </button>
    </div>
  );
}

function Meta({ label, children }) {
  return (
    <React.Fragment>
      <div style={{
        fontFamily: '"IBM Plex Mono", monospace', fontSize: 10, color: '#7a4828',
        letterSpacing: 1, textTransform: 'uppercase', paddingTop: 2,
      }}>{label}</div>
      <div style={{ fontFamily: '"Noto Serif SC", serif', fontSize: 13, color: '#2a1f17' }}>{children}</div>
    </React.Fragment>
  );
}

function Timeline({ range, letters, focusId, onPick, onHover, showAnnots }) {
  const start = 1880, end = 1970;
  const pct = (y) => ((y - start) / (end - start)) * 100;
  return (
    <div style={{ position: 'absolute', left: 56, right: 56, bottom: 40, zIndex: 3 }}>
      <div style={{
        display: 'flex', justifyContent: 'space-between', marginBottom: 6,
        fontFamily: '"EB Garamond", serif', fontStyle: 'italic',
        fontSize: 11, color: '#7a4828', letterSpacing: 2, textTransform: 'uppercase',
      }}>
        <div>Timeline · 年表</div>
        <div>{range[0]} — {range[1]}</div>
      </div>
      <div style={{ position: 'relative', height: 76, paddingTop: 30 }}>
        <div style={{ position: 'absolute', left: 0, right: 0, top: 48, height: 1, background: 'rgba(106,58,38,0.5)' }} />
        {Array.from({ length: 10 }).map((_, i) => {
          const y = start + i * 10;
          return (
            <div key={y} style={{ position: 'absolute', left: `${pct(y)}%`, top: 44, transform: 'translateX(-50%)' }}>
              <div style={{ width: 1, height: 8, background: 'rgba(106,58,38,0.6)' }} />
              <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 10, color: '#5a3a22', marginTop: 4, textAlign: 'center', transform: 'translateX(-50%)', position: 'absolute', left: 0 }}>{y}</div>
            </div>
          );
        })}
        {letters.map((l) => (
          <button key={l.id}
            onClick={() => onPick(l)}
            onMouseEnter={() => onHover(l.id)}
            onMouseLeave={() => onHover(null)}
            title={`${l.year} ${l.from.city} → ${l.to.city}`}
            style={{
              position: 'absolute', left: `${pct(l.year)}%`, top: 24,
              transform: 'translateX(-50%)',
              width: focusId === l.id ? 16 : 12,
              height: focusId === l.id ? 16 : 12,
              borderRadius: '50%',
              background: focusId === l.id ? '#9b3a2a' : '#2a1f17',
              border: '2px solid #f1e8d4',
              padding: 0, cursor: 'pointer',
              boxShadow: focusId === l.id ? '0 0 0 4px rgba(155,58,42,0.18)' : 'none',
              transition: 'all .2s',
            }} />
        ))}
        {showAnnots && (
          <React.Fragment>
            <Annot left={pct(1900)} label="海禁渐弛" sub="Emigration loosens" />
            <Annot left={pct(1929)} label="经济大萧条" sub="Great Depression" />
            <Annot left={pct(1937)} label="抗战开始" sub="2nd Sino-Japanese War" />
            <Annot left={pct(1945)} label="二战结束" sub="WWII ends" />
            <Annot left={pct(1955)} label="侨汇统一" sub="State remittance" />
          </React.Fragment>
        )}
      </div>
    </div>
  );
}
function Annot({ left, label, sub }) {
  return (
    <div style={{
      position: 'absolute', left: `${left}%`, top: 0,
      transform: 'translateX(-50%)', fontFamily: '"Noto Serif SC", serif',
      fontSize: 10, color: '#7a4828', textAlign: 'center', opacity: 0.75, whiteSpace: 'nowrap',
    }}>
      <div style={{ fontWeight: 700 }}>{label}</div>
      <div style={{ fontFamily: '"EB Garamond", serif', fontStyle: 'italic', fontSize: 9 }}>{sub}</div>
    </div>
  );
}

window.MapPage = MapPage;
