// ============================================================
// Shared components for 買取価格比較
// ============================================================

const { useState, useEffect, useMemo, useRef } = React;

const fmtYen = (n) => n == null ? '—' : '¥' + n.toLocaleString('ja-JP');
const fmtYenShort = fmtYen;
const fmtPct = (n) => (n >= 0 ? '+' : '') + (n * 100).toFixed(1) + '%';

// ---------- Icons ----------
const Icon = {
  Search: (p) => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/>
    </svg>
  ),
  Up: (p) => (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M7 14l5-5 5 5"/>
    </svg>
  ),
  Down: (p) => (
    <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M7 10l5 5 5-5"/>
    </svg>
  ),
  Close: (p) => (
    <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M18 6 6 18M6 6l12 12"/>
    </svg>
  ),
  Filter: (p) => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M3 5h18M6 12h12M10 19h4"/>
    </svg>
  ),
  Star: (p) => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor" {...p}>
      <path d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.7 7-6.3-3.8L5.7 21.2l1.7-7L2 9.5l7.1-.6L12 2z"/>
    </svg>
  ),
  StarOutline: (p) => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" {...p}>
      <path d="M12 2l2.9 6.9 7.1.6-5.4 4.7 1.7 7-6.3-3.8L5.7 21.2l1.7-7L2 9.5l7.1-.6L12 2z"/>
    </svg>
  ),
  Logo: (p) => (
    <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" {...p}>
      <rect x="3.5" y="3.5" width="17" height="17" rx="4"/>
      <path d="M8 15l3-5 2.5 3L17 9" strokeLinecap="round" strokeLinejoin="round"/>
    </svg>
  ),
  Sort: (p) => (
    <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" {...p}>
      <path d="M3 6h13M3 12h9M3 18h5M17 8v12m0 0-3-3m3 3 3-3"/>
    </svg>
  ),
  Moon: (p) => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/>
    </svg>
  ),
  Sun: (p) => (
    <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" {...p}>
      <circle cx="12" cy="12" r="4"/>
      <path d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"/>
    </svg>
  ),
};

// ---------- Delta badge ----------
function Delta({ value, pct }) {
  if (!value) {
    return <span style={{ color: 'var(--ink-4)', fontSize: 12 }} className="mono">±0</span>;
  }
  const up = value > 0;
  return (
    <span style={{
      display: 'inline-flex', alignItems: 'center', gap: 2,
      color: up ? 'var(--up)' : 'var(--down)',
      background: up ? 'var(--up-bg)' : 'var(--down-bg)',
      padding: '2px 6px 2px 4px',
      borderRadius: 4,
      fontSize: 11,
      fontWeight: 600,
    }} className="mono">
      {up ? <Icon.Up/> : <Icon.Down/>}
      {fmtPct(pct)}
    </span>
  );
}

// ---------- Small spread bar ----------
function SpreadBar({ offers, max, min }) {
  if (!offers || offers.length === 0 || max == null || min == null) return null;
  const range = max - min || 1;
  return (
    <div style={{
      position: 'relative', height: 18, marginTop: 4,
      background: 'var(--surface-2)', borderRadius: 4,
      border: '1px solid var(--border)'
    }}>
      <div style={{
        position: 'absolute', left: 4, top: 4, fontSize: 9,
        color: 'var(--ink-4)', fontWeight: 500, letterSpacing: 0.3
      }} className="mono">MIN</div>
      <div style={{
        position: 'absolute', right: 4, top: 4, fontSize: 9,
        color: 'var(--ink-4)', fontWeight: 500, letterSpacing: 0.3
      }} className="mono">MAX</div>
      {offers.map((o, i) => {
        const pos = ((o.price - min) / range) * 100;
        const isMax = i === 0;
        return (
          <div key={o.sid} style={{
            position: 'absolute',
            left: `calc(${pos}% - 1px)`,
            top: 2, bottom: 2, width: 2,
            background: isMax ? 'var(--accent)' : 'var(--ink-3)',
            opacity: isMax ? 1 : 0.45,
            borderRadius: 1,
          }}/>
        );
      })}
    </div>
  );
}

// ---------- Price card ----------
function PriceCard({ m, onOpen, favorited, onToggleFav, density }) {
  const site = m.maxSite ? window.SITES_BY_ID[m.maxSite] : null;
  const compact = density === 'compact';
  const hasData = m.rankings.length > 0;

  return (
    <article
      onClick={() => onOpen(m)}
      style={{
        background: 'var(--surface)',
        border: '1px solid var(--border)',
        borderRadius: 'var(--radius)',
        padding: compact ? '14px 16px' : '18px 20px',
        boxShadow: 'var(--shadow)',
        cursor: 'pointer',
        transition: 'transform .15s ease, box-shadow .15s ease, border-color .15s ease',
        display: 'flex', flexDirection: 'column', gap: compact ? 8 : 12,
      }}
      onMouseEnter={e => { e.currentTarget.style.transform = 'translateY(-1px)'; e.currentTarget.style.boxShadow = 'var(--shadow-lg)'; e.currentTarget.style.borderColor = 'var(--border-strong)'; }}
      onMouseLeave={e => { e.currentTarget.style.transform = ''; e.currentTarget.style.boxShadow = 'var(--shadow)'; e.currentTarget.style.borderColor = 'var(--border)'; }}
    >
      <header style={{ display: 'flex', alignItems: 'flex-start', justifyContent: 'space-between', gap: 8 }}>
        <div style={{ minWidth: 0 }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginBottom: 2 }}>
            <TierBadge tier={m.tier}/>
          </div>
          <h3 style={{ margin: 0, fontSize: compact ? 15 : 16, fontWeight: 600, color: 'var(--ink)', letterSpacing: '-0.01em' }}>
            {m.model}
          </h3>
          <div style={{ fontSize: 13, color: 'var(--ink-2)', marginTop: 1 }}>{m.storage}</div>
        </div>
        <button
          onClick={e => { e.stopPropagation(); onToggleFav(m.id); }}
          style={{ padding: 4, color: favorited ? 'var(--accent)' : 'var(--ink-4)', display: 'flex' }}
          aria-label="お気に入り"
        >
          {favorited ? <Icon.Star/> : <Icon.StarOutline/>}
        </button>
      </header>

      <div>
        <div style={{ display: 'flex', alignItems: 'baseline', gap: 8, flexWrap: 'wrap' }}>
          <div style={{
            fontSize: compact ? 26 : 30,
            fontWeight: 600,
            color: hasData ? 'var(--ink)' : 'var(--ink-4)',
            letterSpacing: '-0.02em',
            lineHeight: 1,
          }} className="mono">
            {fmtYen(m.maxPrice)}
          </div>
          {hasData && <Delta value={m.delta} pct={m.deltaPct}/>}
        </div>
        {site && (
          <div style={{ display: 'flex', alignItems: 'center', gap: 6, marginTop: 6, fontSize: 12, color: 'var(--ink-3)' }}>
            <span style={{
              display: 'inline-block', width: 8, height: 8, borderRadius: 2,
              background: site.color,
            }}/>
            <span style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{site.short}</span>
            <span style={{ color: 'var(--ink-4)' }}>が最高値</span>
          </div>
        )}
        {!hasData && (
          <div style={{ marginTop: 6, fontSize: 12, color: 'var(--ink-4)' }}>価格データ取得中...</div>
        )}
      </div>

      {!compact && hasData && m.rankings.length > 1 && (
        <SpreadBar offers={m.rankings} max={m.maxPrice} min={m.minPrice}/>
      )}

      <footer style={{
        display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        borderTop: '1px solid var(--border)',
        paddingTop: compact ? 8 : 10,
        fontSize: 11, color: 'var(--ink-3)',
      }}>
        <div style={{ display: 'flex', gap: 12 }}>
          <Stat label="比較サイト" value={m.siteCount > 0 ? m.siteCount + '社' : '—'}/>
          {hasData && <Stat label="価格差" value={fmtYen(m.spread)} mono/>}
        </div>
        <span style={{ color: 'var(--ink-4)', fontWeight: 500 }}>詳細 →</span>
      </footer>
    </article>
  );
}

function Stat({ label, value, mono }) {
  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 1 }}>
      <span style={{ fontSize: 10, color: 'var(--ink-4)', letterSpacing: 0.3, textTransform: 'uppercase', fontWeight: 500 }}>{label}</span>
      <span className={mono ? 'mono' : ''} style={{ color: 'var(--ink-2)', fontSize: 12, fontWeight: 500 }}>{value}</span>
    </div>
  );
}

function TierBadge({ tier }) {
  const map = {
    standard: { label: 'Standard', color: 'var(--ink-3)', bg: 'var(--surface-2)' },
    air:      { label: 'Air',      color: 'var(--ink-3)', bg: 'var(--surface-2)' },
    pro:      { label: 'Pro',      color: 'var(--accent)', bg: 'var(--accent-soft)' },
    max:      { label: 'Pro Max',  color: 'var(--accent)', bg: 'var(--accent-soft)' },
  };
  const t = map[tier] || map.standard;
  return (
    <span style={{
      fontSize: 10, fontWeight: 600, letterSpacing: 0.4,
      textTransform: 'uppercase',
      padding: '2px 6px', borderRadius: 3,
      color: t.color, background: t.bg,
      border: '1px solid ' + (tier === 'pro' || tier === 'max' ? 'transparent' : 'var(--border)')
    }}>{t.label}</span>
  );
}

// ---------- Table row ----------
function PriceRow({ m, onOpen, favorited, onToggleFav }) {
  const site = m.maxSite ? window.SITES_BY_ID[m.maxSite] : null;
  const hasData = m.rankings.length > 0;
  return (
    <tr
      onClick={() => onOpen(m)}
      style={{ cursor: 'pointer', borderBottom: '1px solid var(--border)' }}
      onMouseEnter={e => e.currentTarget.style.background = 'var(--surface-2)'}
      onMouseLeave={e => e.currentTarget.style.background = ''}
    >
      <td style={{ padding: '14px 12px 14px 20px', width: 32 }}>
        <button onClick={e => { e.stopPropagation(); onToggleFav(m.id); }}
          style={{ color: favorited ? 'var(--accent)' : 'var(--ink-4)', display: 'flex', padding: 2 }}>
          {favorited ? <Icon.Star/> : <Icon.StarOutline/>}
        </button>
      </td>
      <td style={{ padding: '14px 12px' }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
          <TierBadge tier={m.tier}/>
          <div>
            <div style={{ fontWeight: 600, color: 'var(--ink)', fontSize: 14 }}>{m.model}</div>
            <div style={{ color: 'var(--ink-3)', fontSize: 12 }}>{m.storage}</div>
          </div>
        </div>
      </td>
      <td style={{ padding: '14px 12px', textAlign: 'right' }}>
        <div className="mono" style={{ fontSize: 17, fontWeight: 600, color: hasData ? 'var(--ink)' : 'var(--ink-4)', letterSpacing: '-0.01em' }}>
          {fmtYen(m.maxPrice)}
        </div>
      </td>
      <td style={{ padding: '14px 12px', textAlign: 'right' }}>
        {hasData && <Delta value={m.delta} pct={m.deltaPct}/>}
      </td>
      <td style={{ padding: '14px 12px', minWidth: 160 }}>
        {site ? (
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, fontSize: 13 }}>
            <span style={{ display: 'inline-block', width: 8, height: 8, borderRadius: 2, background: site.color }}/>
            <span style={{ color: 'var(--ink-2)', fontWeight: 500 }}>{site.short}</span>
          </div>
        ) : (
          <span style={{ fontSize: 12, color: 'var(--ink-4)' }}>—</span>
        )}
      </td>
      <td style={{ padding: '14px 12px', textAlign: 'right', color: 'var(--ink-2)', fontSize: 13 }} className="mono">
        {m.siteCount > 0 ? m.siteCount + '社' : '—'}
      </td>
      <td style={{ padding: '14px 12px', textAlign: 'right', color: 'var(--ink-2)', fontSize: 13 }} className="mono">
        {fmtYen(m.spread)}
      </td>
      <td style={{ padding: '14px 20px 14px 12px', width: 180 }}>
        {hasData && m.rankings.length > 1 && (
          <SpreadBar offers={m.rankings} max={m.maxPrice} min={m.minPrice}/>
        )}
      </td>
    </tr>
  );
}

// ---------- Detail modal ----------
function DetailModal({ model, onClose }) {
  useEffect(() => {
    const onKey = (e) => { if (e.key === 'Escape') onClose(); };
    window.addEventListener('keydown', onKey);
    document.body.style.overflow = 'hidden';
    return () => { window.removeEventListener('keydown', onKey); document.body.style.overflow = ''; };
  }, [onClose]);

  if (!model) return null;
  const m = model;
  const max = m.maxPrice, min = m.minPrice;
  const range = (max != null && min != null) ? (max - min || 1) : 1;
  const hasData = m.rankings.length > 0;

  return (
    <div
      onClick={onClose}
      style={{
        position: 'fixed', inset: 0, zIndex: 50,
        background: 'rgba(10,10,8,0.5)', backdropFilter: 'blur(4px)',
        display: 'flex', alignItems: 'center', justifyContent: 'center',
        padding: 20,
        animation: 'fadeIn .15s ease',
      }}
    >
      <div
        onClick={e => e.stopPropagation()}
        style={{
          background: 'var(--surface)', borderRadius: 14,
          maxWidth: 680, width: '100%', maxHeight: '88vh',
          overflow: 'auto', boxShadow: 'var(--shadow-lg)',
          border: '1px solid var(--border)',
        }}>
        <header style={{
          padding: '24px 28px', borderBottom: '1px solid var(--border)',
          display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', gap: 16,
          position: 'sticky', top: 0, background: 'var(--surface)', zIndex: 1,
        }}>
          <div>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
              <TierBadge tier={m.tier}/>
            </div>
            <h2 style={{ margin: 0, fontSize: 22, fontWeight: 600, letterSpacing: '-0.02em' }}>
              {m.model} <span style={{ color: 'var(--ink-3)', fontWeight: 500 }}>{m.storage}</span>
            </h2>
            <div style={{ marginTop: 4, fontSize: 13, color: 'var(--ink-3)' }}>
              {hasData ? `${m.siteCount}社の買取価格` : '価格データ取得中...'}
            </div>
          </div>
          <button onClick={onClose} style={{
            padding: 8, borderRadius: 8, color: 'var(--ink-3)',
            border: '1px solid var(--border)', display: 'flex',
          }} aria-label="閉じる"><Icon.Close/></button>
        </header>

        {hasData && (
          <section style={{
            display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)',
            borderBottom: '1px solid var(--border)',
          }}>
            {[
              { label: '最高値',  value: fmtYen(max),       accent: true },
              { label: '最安値',  value: fmtYen(min) },
              { label: '平均',    value: fmtYen(m.avgPrice) },
              { label: '価格差',  value: fmtYen(m.spread) },
            ].map((s, i) => (
              <div key={i} style={{
                padding: '18px 20px',
                borderRight: i < 3 ? '1px solid var(--border)' : 'none',
              }}>
                <div style={{ fontSize: 10, color: 'var(--ink-4)', letterSpacing: 0.4, textTransform: 'uppercase', fontWeight: 600 }}>
                  {s.label}
                </div>
                <div className="mono" style={{
                  marginTop: 4, fontSize: 17, fontWeight: 600,
                  color: s.accent ? 'var(--accent)' : 'var(--ink)',
                  letterSpacing: '-0.01em',
                }}>{s.value}</div>
              </div>
            ))}
          </section>
        )}

        <section style={{ padding: '20px 28px 28px' }}>
          {hasData ? (
            <>
              <div style={{
                display: 'flex', justifyContent: 'space-between', alignItems: 'baseline',
                marginBottom: 14,
              }}>
                <h3 style={{ margin: 0, fontSize: 13, fontWeight: 600, color: 'var(--ink-2)', letterSpacing: 0.3, textTransform: 'uppercase' }}>
                  サイト別ランキング
                </h3>
                <div style={{ fontSize: 11, color: 'var(--ink-4)' }} className="mono">
                  更新: {window.LAST_UPDATED}
                </div>
              </div>

              <ol style={{ margin: 0, padding: 0, listStyle: 'none', display: 'flex', flexDirection: 'column', gap: 2 }}>
                {m.rankings.map((o, i) => {
                  const site = window.SITES_BY_ID[o.sid];
                  const width = (o.price - (min || 0)) / range * 100;
                  const isTop = i === 0;
                  const diff = o.price - (max || 0);
                  return (
                    <li key={o.sid} style={{
                      position: 'relative',
                      display: 'grid',
                      gridTemplateColumns: '28px 1fr auto auto',
                      gap: 14, alignItems: 'center',
                      padding: '12px 14px',
                      background: isTop ? 'var(--accent-soft)' : 'transparent',
                      borderRadius: 8,
                      border: isTop ? '1px solid rgba(31,78,216,0.2)' : '1px solid transparent',
                    }}>
                      <span className="mono" style={{
                        fontSize: 13, fontWeight: 600,
                        color: isTop ? 'var(--accent)' : 'var(--ink-4)',
                        textAlign: 'center',
                      }}>#{i + 1}</span>

                      <div style={{ minWidth: 0 }}>
                        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 4 }}>
                          <span style={{ width: 8, height: 8, borderRadius: 2, background: site.color, display: 'inline-block' }}/>
                          <span style={{ fontWeight: 500, color: 'var(--ink)', fontSize: 14 }}>{site.name}</span>
                          {isTop && <span style={{
                            fontSize: 10, fontWeight: 700, color: 'var(--accent)',
                            border: '1px solid var(--accent)', padding: '1px 5px',
                            borderRadius: 3, letterSpacing: 0.4,
                          }}>TOP</span>}
                        </div>
                        <div style={{ height: 4, background: 'var(--surface-2)', borderRadius: 2, overflow: 'hidden' }}>
                          <div style={{
                            width: `${Math.max(width, 2)}%`, height: '100%',
                            background: isTop ? 'var(--accent)' : 'var(--ink-3)',
                            opacity: isTop ? 1 : 0.5,
                            transition: 'width .3s',
                          }}/>
                        </div>
                      </div>

                      <div className="mono" style={{
                        fontSize: 15, fontWeight: 600,
                        color: isTop ? 'var(--accent)' : 'var(--ink)',
                        letterSpacing: '-0.01em', textAlign: 'right',
                      }}>{fmtYen(o.price)}</div>

                      <div className="mono" style={{
                        fontSize: 11, color: 'var(--ink-4)',
                        minWidth: 60, textAlign: 'right',
                      }}>
                        {diff === 0 ? '—' : fmtYen(diff)}
                      </div>
                    </li>
                  );
                })}
              </ol>
            </>
          ) : (
            <div style={{
              padding: '40px 0', textAlign: 'center',
              color: 'var(--ink-4)', fontSize: 14,
            }}>
              価格データを取得中です。しばらくお待ちください。
            </div>
          )}

          <div style={{
            marginTop: 18, padding: '12px 14px',
            background: 'var(--surface-2)', borderRadius: 8,
            fontSize: 12, color: 'var(--ink-3)',
            border: '1px solid var(--border)',
          }}>
            <strong style={{ color: 'var(--ink-2)' }}>ご注意:</strong> 表示価格は各社公表の買取上限額です。端末の状態、付属品、キャリア、動作確認結果により実査定額は変動します。
          </div>
        </section>
      </div>
    </div>
  );
}

Object.assign(window, { Icon, Delta, PriceCard, PriceRow, DetailModal, TierBadge, Stat, fmtYen, fmtPct });
