// components.jsx — 共通UIコンポーネント
const { useState, useMemo, useEffect } = React;

// ---- 小さなアイコン（インラインSVG。装飾用の最小限のみ） ----
function Ico({ name, size = 18 }) {
  const p = { width: size, height: size, viewBox: "0 0 24 24", fill: "none",
    stroke: "currentColor", strokeWidth: 1.8, strokeLinecap: "round", strokeLinejoin: "round" };
  const paths = {
    search: <><circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3" /></>,
    arrow:  <path d="M5 12h14M13 6l6 6-6 6" />,
    chevron:<path d="M9 6l6 6-6 6" />,
    check:  <path d="M5 12l4.5 4.5L19 7" />,
    cart:   <><circle cx="9" cy="21" r="1.6" /><circle cx="18" cy="21" r="1.6" /><path d="M2 3h3l2.4 12.4a1.5 1.5 0 0 0 1.5 1.2h8.3a1.5 1.5 0 0 0 1.5-1.2L22 7H6" /></>,
    filter: <path d="M3 5h18M7 12h10M11 19h2" />,
    grid:   <><rect x="3" y="3" width="7" height="7" rx="1" /><rect x="14" y="3" width="7" height="7" rx="1" /><rect x="3" y="14" width="7" height="7" rx="1" /><rect x="14" y="14" width="7" height="7" rx="1" /></>,
    list:   <><path d="M8 6h13M8 12h13M8 18h13" /><circle cx="3.5" cy="6" r="1.2" /><circle cx="3.5" cy="12" r="1.2" /><circle cx="3.5" cy="18" r="1.2" /></>,
    close:  <path d="M6 6l12 12M18 6L6 18" />,
    leaf:   <><path d="M11 20A7 7 0 0 1 4 13c0-6 8-9 16-9 0 8-3 16-9 16z" /><path d="M4 20c4-7 9-9 9-9" /></>,
    fiber:  <><path d="M4 7c4 3 12 3 16 0M4 12c4 3 12 3 16 0M4 17c4 3 12 3 16 0" /></>,
    potass: <><path d="M12 3v4M12 17v4M3 12h4M17 12h4" /><circle cx="12" cy="12" r="4" /></>,
    natural:<><path d="M12 2v20" /><path d="M12 8c-4 0-7-2-7-2M12 8c4 0 7-2 7-2M12 14c-4 0-7-2-7-2M12 14c4 0 7-2 7-2" /></>,
    energy: <path d="M13 2L4 14h7l-1 8 9-12h-7z" />,
    star:   <path d="M12 2l3 6.5 7 .7-5.2 4.8 1.5 7L12 17.6 5.2 21l1.5-7L1.5 9.2l7-.7z" />,
  };
  return <svg {...p}>{paths[name] || null}</svg>;
}

// ---- 星評価 ----
function Stars({ value, size }) {
  const cls = size === "lg" ? "stars lg" : "stars";
  return (
    <span className={cls}>
      {[0, 1, 2, 3, 4].map((i) => {
        const full = value >= i + 0.75;
        const half = !full && value >= i + 0.25;
        return (
          <svg key={i} viewBox="0 0 24 24" className={full ? "star-full" : "star-empty"}>
            {half && (
              <defs>
                <linearGradient id={`hg${i}-${Math.round(value*10)}`}>
                  <stop offset="50%" stopColor="var(--star)" />
                  <stop offset="50%" stopColor="var(--line)" />
                </linearGradient>
              </defs>
            )}
            <path d="M12 2l3 6.5 7 .7-5.2 4.8 1.5 7L12 17.6 5.2 21l1.5-7L1.5 9.2l7-.7z"
              fill={half ? `url(#hg${i}-${Math.round(value*10)})` : "currentColor"} />
          </svg>
        );
      })}
    </span>
  );
}

// ---- 項目別スコア（★表示） ----
const SCORE_LABELS = { sweet: "甘さ", aroma: "香り", flavor: "味わい", cost: "コスパ" };
const SCORE_ORDER = ["sweet", "aroma", "flavor", "cost"];
function ScoreBar({ k, val }) {
  return (
    <div className="scorebar">
      <span className="lbl">{SCORE_LABELS[k]}</span>
      <span className="stars sm"><Stars value={val} /></span>
      <span className="val num">{val.toFixed(1)}</span>
    </div>
  );
}

// ---- 画像プレースホルダ ----
function Ph({ label, className, style }) {
  return (
    <div className={`ph ${className || ""}`} style={style}>
      <span>{label}</span>
    </div>
  );
}

// ---- 商品画像（imageUrl があれば写真、なければプレースホルダ） ----
function ProductImg({ p, label }) {
  if (p && p.imageUrl) {
    return <img src={p.imageUrl} alt={p.name}
      style={{ position: "absolute", inset: 0, width: "100%", height: "100%", objectFit: "cover" }} />;
  }
  return <Ph label={label} style={{ position: "absolute", inset: 0 }} />;
}

// ---- 商品画像スライダー（詳細モーダル用・複数枚をスワイプ/矢印で切替） ----
function ProductGallery({ p, label }) {
  const imgs = (p && p.images && p.images.length) ? p.images : (p && p.imageUrl ? [p.imageUrl] : []);
  const [i, setI] = React.useState(0);
  const startX = React.useRef(null);
  if (imgs.length === 0) return <Ph label={label} style={{ position: "absolute", inset: 0 }} />;
  const go = (n) => setI((cur) => (n + imgs.length) % imgs.length);
  const onTouchStart = (e) => { startX.current = e.touches[0].clientX; };
  const onTouchEnd = (e) => {
    if (startX.current == null) return;
    const dx = e.changedTouches[0].clientX - startX.current;
    if (dx > 40) go(i - 1); else if (dx < -40) go(i + 1);
    startX.current = null;
  };
  return (
    <div className="gallery" onTouchStart={onTouchStart} onTouchEnd={onTouchEnd}>
      <div className="gallery-track" style={{ transform: `translateX(-${i * 100}%)` }}>
        {imgs.map((src, j) => (
          <div className="gallery-slide" key={j}>
            <img src={src} alt={`${p.name} ${j + 1}`} />
          </div>
        ))}
      </div>
      {imgs.length > 1 && (
        <>
          <button className="gallery-arrow left" onClick={(e) => { e.stopPropagation(); go(i - 1); }} aria-label="前の画像">
            <Ico name="chevron" size={20} />
          </button>
          <button className="gallery-arrow right" onClick={(e) => { e.stopPropagation(); go(i + 1); }} aria-label="次の画像">
            <Ico name="chevron" size={20} />
          </button>
          <div className="gallery-dots">
            {imgs.map((_, j) => (
              <button key={j} className={`gdot ${j === i ? "on" : ""}`} onClick={(e) => { e.stopPropagation(); setI(j); }} aria-label={`${j + 1}枚目`} />
            ))}
          </div>
          <div className="gallery-count num">{i + 1} / {imgs.length}</div>
        </>
      )}
    </div>
  );
}

// ---- 名前解決ヘルパ ----
const makerName = (id) => (MAKERS.find((m) => m.id === id) || {}).name || "";
const makerObj = (id) => MAKERS.find((m) => m.id === id) || {};
const varietyName = (id) => (VARIETIES.find((v) => v.id === id) || {}).name || "";
const shapeName = (id) => (SHAPES.find((s) => s.id === id) || {}).name || "";
const hardnessName = (id) => ((window.HARDNESS || []).find((h) => h.id === id) || {}).name || "";
const originName = (id) => ((window.ORIGINS || []).find((o) => o.id === id) || {}).name || "";

// ---- 購入ボタン（3チャンネル・未登録は無効化） ----
const BUY_CHANNELS = [
  { key: "rakuten",  label: "楽天市場", cls: "buy-rakuten" },
  { key: "amazon",   label: "Amazon",   cls: "buy-amazon" },
  { key: "official", label: "自社サイト", cls: "buy-official" },
];
function BuyButtons({ p, compact }) {
  const links = p.links || {};
  return (
    <div className={`buy-row ${compact ? "compact" : ""}`}>
      {BUY_CHANNELS.map((c) => {
        const url = links[c.key];
        if (!url) {
          return <span key={c.key} className="buy-btn disabled" title="取り扱いなし">{c.label}<i>取扱いなし</i></span>;
        }
        return (
          <a key={c.key} className={`buy-btn ${c.cls}`} href={url} target="_blank"
            rel="noopener noreferrer nofollow sponsored">
            {c.label}<i>で見る</i>
          </a>
        );
      })}
    </div>
  );
}

// ---- 商品カード（縦型・トップやグリッド用） ----
function ProductCard({ p, onOpen }) {
  return (
    <article className="card pcard" onClick={() => onOpen(p)}>
      <div className="pimg">
        {p.badge && <span className="badge">{p.badge}</span>}
        <ProductImg p={p} label={`${varietyName(p.variety)}・${shapeName(p.shape)}`} />
      </div>
      <div className="pbody">
        <span className="pmaker">{makerName(p.maker)}</span>
        <span className="pname">{p.name}</span>
        <span className="stars"><Stars value={p.rating} /><b className="rating-num">{p.rating.toFixed(1)}</b>
          <span style={{ fontSize: 12, color: "var(--ink-faint)", marginLeft: 4 }}>({p.reviewCount})</span>
        </span>
        <div style={{ display: "flex", alignItems: "baseline", justifyContent: "space-between", marginTop: "auto" }}>
          <span className="pprice num">¥{p.price.toLocaleString()}<small> / {p.weight}g</small></span>
        </div>
      </div>
    </article>
  );
}

// ---- 商品行（一覧リスト表示用） ----
function ProductRow({ p, onOpen }) {
  return (
    <article className="card prow">
      <div className="pimg" onClick={() => onOpen(p)} style={{ cursor: "pointer" }}>
        {p.badge && <span className="badge">{p.badge}</span>}
        <ProductImg p={p} label="商品画像" />
      </div>
      <div className="pmain">
        <div className="ptop">
          {p.rank <= 3 && <span className={`rank-badge r${p.rank}`}>{p.rank}</span>}
          <span className="pmaker">{makerName(p.maker)}</span>
          <span className="chip olive">{varietyName(p.variety)}</span>
          <span className="chip">{shapeName(p.shape)}</span>
          <span className="chip">{hardnessName(p.hardness)}</span>
        </div>
        <h3 className="pname" onClick={() => onOpen(p)} style={{ cursor: "pointer" }}>{p.name}</h3>
        <span className="stars">
          <Stars value={p.rating} />
          <b className="rating-num">{p.rating.toFixed(1)}</b>
          <span style={{ fontSize: 12.5, color: "var(--ink-soft)", marginLeft: 6 }}>
            {p.reviewCount}件のレビュー
          </span>
        </span>
        <div className="pscores">
          {SCORE_ORDER.map((k) => <ScoreBar key={k} k={k} val={p.scores[k]} />)}
        </div>
        <div className="ptags">
          {p.tags.map((t) => <span key={t} className="chip">{t}</span>)}
        </div>
      </div>
      <div className="pright">
        <div>
          <div className="pprice num">¥{p.price.toLocaleString()}</div>
          <div className="pper">税込 / {p.weight}g（100gあたり ¥{p.pricePer100}）</div>
        </div>
        <BuyButtons p={p} compact />
        <button className="btn-detail" onClick={() => onOpen(p)}>詳細・レビュー →</button>
      </div>
    </article>
  );
}

// ---- 商品詳細モーダル ----
function ProductModal({ p, onClose }) {
  useEffect(() => {
    const h = (e) => e.key === "Escape" && onClose();
    window.addEventListener("keydown", h);
    document.body.style.overflow = "hidden";
    return () => { window.removeEventListener("keydown", h); document.body.style.overflow = ""; };
  }, []);
  if (!p) return null;
  const reviews = p.reviews || [];
  const mk = makerObj(p.maker);
  return (
    <div className="modal-back" onClick={onClose}>
      <div className="modal" onClick={(e) => e.stopPropagation()}>
        <div className="modal-img">
          {p.badge && <span className="badge">{p.badge}</span>}
          <button className="modal-close" onClick={onClose} aria-label="閉じる"><Ico name="close" /></button>
          <ProductGallery p={p} label="商品画像（高解像度）" />
        </div>
        <div className="modal-body">
          <div style={{ display: "flex", gap: 8, flexWrap: "wrap", marginBottom: 10 }}>
            <span className="chip olive">{varietyName(p.variety)}</span>
            {originName(p.origin) && <span className="chip">産地：{originName(p.origin)}</span>}
            <span className="chip">{shapeName(p.shape)}</span>
            <span className="chip">硬さ：{hardnessName(p.hardness)}</span>
            {p.tags.map((t) => <span key={t} className="chip">{t}</span>)}
          </div>
          <h2 style={{ fontFamily: "var(--font-head)", fontSize: 25, margin: "0 0 6px", lineHeight: 1.3 }}>{p.name}</h2>
          <div style={{ fontSize: 13.5, color: "var(--ink-soft)", marginBottom: 14 }}>
            {mk.name}（{mk.region}・創業{mk.since}年）{originName(p.origin) ? `　原料産地：${originName(p.origin)}` : ""}
          </div>
          <div style={{ display: "flex", alignItems: "center", gap: 14, flexWrap: "wrap", marginBottom: 18 }}>
            <span className="stars"><Stars value={p.rating} size="lg" /><b className="rating-num" style={{ fontSize: 20 }}>{p.rating.toFixed(1)}</b></span>
            <span style={{ fontSize: 13, color: "var(--ink-soft)" }}>{p.reviewCount}件のレビュー</span>
            <span className="num" style={{ marginLeft: "auto", fontWeight: 700, fontSize: 28 }}>¥{p.price.toLocaleString()}
              <span style={{ fontSize: 13, color: "var(--ink-soft)", fontWeight: 500 }}> / {p.weight}g</span>
            </span>
          </div>
          <p style={{ fontSize: 14.5, color: "var(--ink-soft)", lineHeight: 1.8, margin: "0 0 18px" }}>{p.desc}</p>
          <div className="card" style={{ padding: "16px 18px", marginBottom: 20 }}>
            <h4 style={{ margin: "0 0 12px", fontSize: 14, fontWeight: 700 }}>項目別スコア</h4>
            <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: "10px 28px" }}>
              {SCORE_ORDER.map((k) => <ScoreBar key={k} k={k} val={p.scores[k]} />)}
            </div>
          </div>
          <div style={{ marginBottom: 22 }}>
            <div style={{ fontSize: 12.5, color: "var(--ink-soft)", marginBottom: 8, fontWeight: 700 }}>購入する（販売店を選ぶ）</div>
            <BuyButtons p={p} />
          </div>
          <h4 style={{ margin: "0 0 4px", fontSize: 16, fontFamily: "var(--font-head)", fontWeight: 700 }}>
            レビュー <span className="num" style={{ color: "var(--ink-faint)", fontSize: 13 }}>({reviews.length})</span>
          </h4>
          {reviews.length === 0 && <p style={{ fontSize: 13, color: "var(--ink-faint)" }}>まだレビューがありません。</p>}
          {reviews.map((r, i) => (
            <div className="review-item" key={i}>
              <div className="rv-head">
                <span className="rv-user">{r.user}</span>
                <span className="rv-date num">{r.date}</span>
              </div>
              <p>{r.body}</p>
              {(r.images || []).filter((u) => u).length > 0 && (
                <div className="rv-photos">
                  {r.images.filter((u) => u).map((url, j) => (
                    <a className="rv-photo" href={url} target="_blank" rel="noopener" key={j} onClick={(e) => e.preventDefault()}>
                      <img src={url} alt={`レビュー画像 ${j + 1}`} onError={(e) => { e.target.parentNode.style.display = "none"; }} />
                    </a>
                  ))}
                </div>
              )}
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

Object.assign(window, {
  Ico, Stars, ScoreBar, Ph, ProductImg, ProductGallery, ProductCard, ProductRow, ProductModal, BuyButtons,
  makerName, makerObj, varietyName, shapeName, hardnessName, originName, SCORE_LABELS, SCORE_ORDER,
});
