// Reusable UI primitives for 株価急騰要因メモ

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

// Tiny sparkline
function Sparkline({ points, color = "var(--up)", w = 56, h = 18 }) {
  const min = Math.min(...points);
  const max = Math.max(...points);
  const range = max - min || 1;
  const stepX = w / (points.length - 1);
  const path = points
    .map((p, i) => `${i === 0 ? "M" : "L"} ${(i * stepX).toFixed(2)} ${(h - ((p - min) / range) * h).toFixed(2)}`)
    .join(" ");
  return (
    <svg width={w} height={h} viewBox={`0 0 ${w} ${h}`} aria-hidden="true">
      <path d={path} className="spark-path" stroke={color} />
    </svg>
  );
}

// Up/down chip (Japanese convention: red=up, green=down)
function PctChip({ pct, size = "md" }) {
  const isUp = pct >= 0;
  const sz = size === "sm"
    ? { fs: 11.5, py: "2px", px: "5px" }
    : size === "lg"
      ? { fs: 16, py: "4px", px: "8px" }
      : { fs: 13, py: "3px", px: "6px" };
  return (
    <span className="mono" style={{
      color: isUp ? "var(--up)" : "var(--down)",
      background: isUp ? "var(--up-soft)" : "var(--down-soft)",
      fontSize: sz.fs, fontWeight: 600,
      padding: `${sz.py} ${sz.px}`,
      letterSpacing: "0.01em",
      whiteSpace: "nowrap",
    }}>
      {isUp ? "+" : ""}{pct.toFixed(1)}%
    </span>
  );
}

// Section header used inside scrolling pages
function SectionHeader({ kicker, title, more, onMore }) {
  return (
    <div style={{
      display: "flex", alignItems: "flex-end", justifyContent: "space-between",
      padding: "22px 18px 10px",
    }}>
      <div>
        {kicker && <div className="eyebrow" style={{ marginBottom: 4 }}>{kicker}</div>}
        <h2 className="mincho" style={{
          margin: 0, fontSize: 19, fontWeight: 600, letterSpacing: "0.02em",
          color: "var(--ink)",
        }}>{title}</h2>
      </div>
      {more && (
        <button onClick={onMore} className="tappable" style={{
          background: "none", border: "none", color: "var(--muted)",
          fontSize: 11.5, letterSpacing: "0.06em", padding: 4, cursor: "pointer",
        }}>
          {more} <span style={{ marginLeft: 2 }}>›</span>
        </button>
      )}
    </div>
  );
}

// Zone break — ホームの大きな意味の切れ目（今日 / 蓄積 / ご案内）を示す区切り
function ZoneBreak({ label, sub }) {
  return (
    <div style={{ padding: "38px 18px 0" }}>
      <div style={{ display: "flex", alignItems: "center", gap: 14 }}>
        <span style={{ flex: 1, borderTop: "1px solid var(--hairline-strong)" }}></span>
        <span aria-hidden="true" style={{
          width: 5, height: 5, background: "var(--gold)",
          transform: "rotate(45deg)", flexShrink: 0,
        }}></span>
        <span className="mincho" style={{
          fontSize: 15, fontWeight: 600, letterSpacing: "0.2em",
          color: "var(--navy)", whiteSpace: "nowrap",
        }}>{label}</span>
        <span aria-hidden="true" style={{
          width: 5, height: 5, background: "var(--gold)",
          transform: "rotate(45deg)", flexShrink: 0,
        }}></span>
        <span style={{ flex: 1, borderTop: "1px solid var(--hairline-strong)" }}></span>
      </div>
      {sub && (
        <div style={{
          textAlign: "center", marginTop: 7,
          fontSize: 10.5, letterSpacing: "0.1em", color: "var(--muted)",
        }}>{sub}</div>
      )}
    </div>
  );
}

// Top app bar
function TopBar({ onMenu, onHome, status = "fallback" }) {
  return (
    <div style={{
      padding: "10px 16px 8px",
      background: "var(--bg)",
      borderBottom: "1px solid var(--hairline)",
      position: "sticky", top: 0, zIndex: 10,
    }}>
      <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
        <div style={{ display: "flex", alignItems: "center", gap: 8 }}>
          <button
            className="tappable"
            onClick={onHome}
            aria-label="ホームへ戻る"
            style={{ background: "transparent", border: "none", padding: 0, cursor: "pointer", color: "inherit" }}
          >
            <span className="mincho" style={{ fontSize: 16, fontWeight: 700, letterSpacing: "0.04em" }}>
              株価急騰要因メモ
            </span>
          </button>
          <ConnectionPill status={status}/>
        </div>
        <div style={{ display: "flex", gap: 12, alignItems: "center", color: "var(--muted)" }}>
          <button className="tappable" style={iconBtnStyle} aria-label="検索">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><circle cx="11" cy="11" r="7"/><path d="m20 20-3.5-3.5"/></svg>
          </button>
          <button className="tappable" style={iconBtnStyle} onClick={onMenu} aria-label="メニュー">
            <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8"><path d="M3 6h18M3 12h18M3 18h18"/></svg>
          </button>
        </div>
      </div>
    </div>
  );
}

// Live connection status pill — replaces the old static BETA badge
function ConnectionPill({ status }) {
  if (status === "live") {
    return (
      <span title="Supabase Realtime接続中" style={{
        display: "inline-flex", alignItems: "center", gap: 4,
        fontSize: 9, color: "var(--up)", border: "1px solid var(--up)",
        padding: "1px 5px 1px 4px", letterSpacing: "0.12em", fontWeight: 600,
      }}>
        <span className="live-dot" style={{ width: 5, height: 5 }}/>
        LIVE
      </span>
    );
  }
  if (status === "connecting") {
    return (
      <span style={{
        fontSize: 9, color: "var(--muted)", border: "1px solid var(--hairline-strong)",
        padding: "1px 4px", letterSpacing: "0.12em",
      }}>接続中…</span>
    );
  }
  if (status === "error") {
    return (
      <span title="Supabase接続エラー" style={{
        fontSize: 9, color: "#B85C00", border: "1px solid #E0B070",
        padding: "1px 4px", letterSpacing: "0.1em",
      }}>オフライン</span>
    );
  }
  // fallback (未設定・サンプル表示)
  return (
    <span title="site-config.js に Supabase 接続情報を設定してください" style={{
      fontSize: 9, color: "var(--muted)", border: "1px solid var(--hairline-strong)",
      padding: "1px 4px", letterSpacing: "0.1em",
    }}>サンプル</span>
  );
}
const iconBtnStyle = {
  background: "transparent", border: "none", color: "inherit",
  padding: 4, cursor: "pointer", display: "inline-flex",
};

// Live ticker strip (under top bar) — 最新の検知を新しい順に流す
function Ticker({ stocks }) {
  // 同一銘柄は最新の1件のみ、最大12件
  const seen = new Set();
  const latest = [];
  (stocks || []).forEach(s => {
    const k = String(s.code);
    if (seen.has(k) || latest.length >= 12) return;
    seen.add(k);
    latest.push(s);
  });
  if (latest.length === 0) return null;
  const items = [...latest, ...latest, ...latest];
  return (
    <div style={{
      background: "var(--navy-deep)",
      color: "#fff",
      padding: "7px 0",
      fontSize: 11.5,
      overflow: "hidden",
      position: "relative",
      borderBottom: "1px solid #000",
    }}>
      <div style={{
        position: "absolute", left: 0, top: 0, bottom: 0,
        display: "flex", alignItems: "center", gap: 5, zIndex: 2,
        background: "var(--navy-deep)", paddingLeft: 10, paddingRight: 10,
      }}>
        <span className="live-dot"/>
        <span style={{ fontSize: 9.5, letterSpacing: "0.18em", color: "#aab2c5" }}>LIVE</span>
      </div>
      <div style={{ paddingLeft: 68 }}>
        <div className="ticker-track">
          {items.map((s, i) => {
            // 上昇率: pct があればそれ、無ければ急騰時→現在価格から算出
            let pct = (s.pct !== null && s.pct !== undefined) ? Number(s.pct) : null;
            if (pct === null || isNaN(pct)) {
              const sp = Number(s.surge_price ?? s.price);
              const cp = s.current_price != null ? Number(s.current_price) : NaN;
              pct = (sp > 0 && !isNaN(cp)) ? ((cp - sp) / sp) * 100 : null;
            }
            const theme = (s.themes && s.themes[0]) || null;
            return (
              <span key={i} style={{ display: "inline-flex", alignItems: "center", gap: 8 }}>
                <span className="mono" style={{ color: "#aab2c5", fontSize: 10.5 }}>{s.code}</span>
                <span style={{ color: "#fff", fontWeight: 500 }}>{s.name}</span>
                {pct !== null ? (
                  <span className="mono" style={{
                    color: pct >= 0 ? "#ff8a8f" : "#7fd29b",
                    fontWeight: 600,
                  }}>
                    {pct >= 0 ? "+" : ""}{pct.toFixed(1)}%
                  </span>
                ) : theme ? (
                  <span style={{ color: "#aab2c5", fontSize: 10.5 }}>{theme}</span>
                ) : null}
                <span style={{ color: "#3a4358" }}>·</span>
              </span>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// Hero
function Hero({ onPrimary, onSecondary, stockCount = 0, status = "fallback", lastUpdate = null }) {
  const updateLabel = lastUpdate
    ? new Date(lastUpdate).toLocaleTimeString("ja-JP", { hour: "2-digit", minute: "2-digit", hour12: false, timeZone: "Asia/Tokyo" })
    : "—";
  return (
    <div style={{
      background: "var(--bg)",
      padding: "26px 18px 22px",
      borderBottom: "1px solid var(--hairline)",
      position: "relative", overflow: "hidden",
    }}>
      {/* Subtle background grid */}
      <div aria-hidden="true" style={{
        position: "absolute", inset: 0,
        backgroundImage: "linear-gradient(var(--hairline) 1px, transparent 1px)",
        backgroundSize: "100% 28px",
        opacity: 0.4, pointerEvents: "none",
      }}/>
      <div style={{ position: "relative" }}>
        <div className="eyebrow" style={{ marginBottom: 14, color: "var(--navy)" }}>
          日本株 · 当日の急騰背景を整理
        </div>
        <h1 className="mincho" style={{
          margin: 0, fontSize: 28, lineHeight: 1.35,
          fontWeight: 700, letterSpacing: "0.02em",
          color: "var(--ink)",
        }}>
          急騰の理由から、<br/>
          <span style={{ color: "var(--navy)" }}>資金の流れを読む。</span>
        </h1>
        <p style={{
          margin: "14px 0 22px",
          fontSize: 13, lineHeight: 1.75,
          color: "var(--muted)",
          letterSpacing: "0.02em",
          maxWidth: 320,
        }}>
          今日動いた日本株の<wbr/>材料・決算・テーマを整理し、<br/>
          今どこに資金が集まっているのかを可視化します。
        </p>
        <div style={{ display: "flex", gap: 8, flexWrap: "wrap" }}>
          <button className="btn btn-primary tappable" onClick={onPrimary} style={{ flex: 1, minWidth: 0 }}>
            今日の急騰理由を見る
          </button>
          <button className="btn btn-secondary tappable" onClick={onSecondary} style={{ flex: 1, minWidth: 0 }}>
            資金流入レポート
          </button>
        </div>

        {/* Inline stat row */}
        <div style={{
          marginTop: 22, display: "grid", gridTemplateColumns: "repeat(3, 1fr)",
          gap: 1, background: "var(--hairline)",
          border: "1px solid var(--hairline)",
        }}>
          {[
            { l: "本日急騰した銘柄", v: String(stockCount), u: "件" },
            { l: "接続状態", v: status === "live" ? "LIVE" : status === "connecting" ? "接続中" : status === "error" ? "オフライン" : "サンプル", u: "" },
            { l: "最終更新", v: updateLabel, u: "JST" },
          ].map((s, i) => (
            <div key={i} style={{
              background: "var(--surface)", padding: "10px 8px", textAlign: "center",
            }}>
              <div style={{ fontSize: 9.5, color: "var(--muted)", letterSpacing: "0.1em", marginBottom: 2 }}>
                {s.l}
              </div>
              <div className="mono" style={{
                fontSize: 15, fontWeight: 600,
                color: i === 1 && status === "live" ? "var(--up)" : "var(--ink)",
                display: "inline-flex", alignItems: "center", gap: 4,
              }}>
                {i === 1 && status === "live" && <span className="live-dot" style={{ width: 5, height: 5 }}/>}
                {s.v}{s.u && <span style={{ fontSize: 10, color: "var(--muted)", marginLeft: 2 }}>{s.u}</span>}
              </div>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
}

// Stock card — list item style
function StockCard({ stock, onOpen, idx, isNew = false, highlightThemes = null, onTheme = null }) {
  const hasPrice = stock.pct !== null && stock.pct !== undefined;
  const hasSpark = Array.isArray(stock.spark) && stock.spark.length > 0;
  return (
    <div className={"tappable" + (isNew ? " new-card" : "")} onClick={() => onOpen?.(stock)} style={{
      background: "var(--surface)",
      padding: "14px 16px",
      borderBottom: "1px solid var(--hairline)",
      cursor: "pointer", display: "flex", gap: 12, alignItems: "flex-start",
    }}>
      <div style={{
        flexShrink: 0, width: 22,
        fontSize: 10.5, color: "var(--muted-2)", letterSpacing: "0.08em",
        paddingTop: 2,
      }} className="mono">
        {String(idx + 1).padStart(2, "0")}
      </div>
      <div style={{ flex: 1, minWidth: 0 }}>
        <div style={{ display: "flex", alignItems: "center", gap: 6, marginBottom: 3 }}>
          <span className="mono" style={{
            fontSize: 11, color: "var(--muted)", letterSpacing: "0.04em",
          }}>{stock.code}</span>
          <span style={{
            fontSize: 9, color: "var(--muted)", border: "1px solid var(--hairline-strong)",
            padding: "0 3px", letterSpacing: "0.06em",
          }}>{stock.market}</span>
          <span style={{ fontSize: 10.5, color: "var(--muted-2)", marginLeft: "auto" }} className="mono">
            {stock.time}
          </span>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 8, marginBottom: 6 }}>
          <div style={{ fontSize: 15, fontWeight: 600, letterSpacing: "0.01em" }}>
            {stock.name}
          </div>
        </div>
        <div style={{ display: "flex", alignItems: "center", gap: 10, marginBottom: 8 }}>
          {hasPrice ? (
            <>
              <PctChip pct={stock.pct}/>
              {hasSpark && <Sparkline points={stock.spark} color={stock.pct >= 0 ? "var(--up)" : "var(--down)"}/>}
              <span className="mono" style={{ fontSize: 11, color: "var(--muted)" }}>
                ¥{stock.price.toLocaleString()}
              </span>
              {stock.vol && <span style={{ fontSize: 10.5, color: "var(--muted-2)", marginLeft: "auto" }} className="mono">
                出来高 {stock.vol}
              </span>}
            </>
          ) : (
            <>
              <span style={{
                color: "var(--up)", background: "var(--up-soft)",
                fontSize: 11, fontWeight: 600, padding: "3px 6px",
                letterSpacing: "0.08em", display: "inline-flex", alignItems: "center", gap: 4,
              }}>
                <span className="live-dot" style={{ width: 5, height: 5 }}/>
                急騰検知
              </span>
              {stock.chart_url && (
                <img src={stock.chart_url} alt="" loading="lazy" style={{
                  height: 22, width: 56, objectFit: "cover", objectPosition: "left center",
                  border: "1px solid var(--hairline)", flexShrink: 0,
                }}/>
              )}
              <span style={{ fontSize: 10.5, color: "var(--muted-2)", marginLeft: "auto" }} className="mono">
                チャート付き
              </span>
            </>
          )}
        </div>
        <div style={{
          fontSize: 12.5, color: "var(--ink-soft)",
          padding: "8px 10px", background: "var(--surface-sub)",
          borderLeft: "2px solid var(--navy)",
          marginBottom: 8, lineHeight: 1.55,
        }}>
          <span style={{ color: "var(--muted)", fontSize: 10.5, marginRight: 6 }}>急騰理由</span>
          {stock.reason}
        </div>
        <div style={{ display: "flex", gap: 6, flexWrap: "wrap" }}>
          {stock.themes.map(t => {
            const hit = highlightThemes && highlightThemes.has(String(t).trim());
            return (
              <span key={t}
                onClick={onTheme ? (e => { e.stopPropagation(); onTheme(String(t).trim()); }) : undefined}
                role={onTheme ? "button" : undefined}
                style={{
                fontSize: 10.5,
                color: hit ? "#fff" : "var(--ink-soft)",
                background: hit ? "var(--navy)" : "transparent",
                border: "1px solid " + (hit ? "var(--navy)" : "var(--hairline-strong)"),
                fontWeight: hit ? 600 : 400,
                padding: "2px 7px",
                cursor: onTheme ? "pointer" : undefined,
              }}>{t}</span>
            );
          })}
        </div>
      </div>
    </div>
  );
}

// Bottom tab bar
function TabBar({ active, onChange }) {
  const tabs = [
    { k: "home",   label: "ホーム",   icon: (a) => <IconHome active={a}/> },
    { k: "cats",   label: "カテゴリ", icon: (a) => <IconGrid active={a}/> },
    { k: "archive",label: "アーカイブ", icon: (a) => <IconArchive active={a}/> },
    { k: "watch",  label: "ウォッチ", icon: (a) => <IconStar active={a}/> },
    { k: "report", label: "レポート", icon: (a) => <IconChart active={a}/> },
    { k: "plan",   label: "プラン",   icon: (a) => <IconCrown active={a}/> },
  ];
  return (
    <div style={{
      borderTop: "1px solid var(--hairline)",
      background: "var(--surface)",
      paddingBottom: "calc(env(safe-area-inset-bottom, 0) + 4px)",
      display: "flex",
    }}>
      {tabs.map(t => {
        const a = active === t.k;
        return (
          <button key={t.k} onClick={() => onChange(t.k)} className="tappable" style={{
            flex: 1, background: "transparent", border: "none",
            padding: "9px 0 7px", cursor: "pointer",
            display: "flex", flexDirection: "column",
            alignItems: "center", gap: 3,
            color: a ? "var(--navy)" : "var(--muted-2)",
            position: "relative",
          }}>
            {a && <span style={{
              position: "absolute", top: 0, left: "50%", transform: "translateX(-50%)",
              width: 28, height: 2, background: "var(--navy)",
            }}/>}
            {t.icon(a)}
            <span style={{ fontSize: 10, letterSpacing: "0.04em", fontWeight: a ? 600 : 400 }}>
              {t.label}
            </span>
          </button>
        );
      })}
    </div>
  );
}

function IconHome({ active }) {
  return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={active ? 2 : 1.6}><path d="M3 11 12 3l9 8v10a1 1 0 0 1-1 1h-5v-7H9v7H4a1 1 0 0 1-1-1V11Z"/></svg>;
}
function IconGrid({ active }) {
  return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={active ? 2 : 1.6}><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>;
}
function IconChart({ active }) {
  return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={active ? 2 : 1.6}><path d="M3 3v18h18"/><path d="m7 15 4-4 3 3 6-7"/></svg>;
}
function IconStar({ active }) {
  return <svg width="20" height="20" viewBox="0 0 24 24" fill={active ? "currentColor" : "none"} stroke="currentColor" strokeWidth={active ? 2 : 1.6} strokeLinecap="round" strokeLinejoin="round"><path d="m12 2 3.09 6.26L22 9.27l-5 4.87 1.18 6.88L12 17.77l-6.18 3.25L7 14.14 2 9.27l6.91-1.01L12 2Z"/></svg>;
}
function IconCrown({ active }) {
  return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={active ? 2 : 1.6}><path d="m3 18 2-10 5 5 2-8 2 8 5-5 2 10z"/><path d="M3 21h18"/></svg>;
}
function IconArchive({ active }) {
  return <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={active ? 2 : 1.6}><rect x="3" y="4" width="18" height="4"/><path d="M5 8v12a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1V8"/><path d="M10 12h4"/></svg>;
}

Object.assign(window, {
  Sparkline, PctChip, SectionHeader, TopBar, Ticker, Hero, StockCard, TabBar,
  ConnectionPill, ZoneBreak, IconHome, IconGrid, IconArchive, IconStar, IconChart, IconCrown,
});
