// WeaveStudy — common components
// Header, Footer, Portrait, AbilityScore, BadgeRow, etc.

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

// ── Icon helper ────────────────────────────────────────────
function Icon({ name, size = 20, color = "currentColor", style }) {
  // Renders an inline-SVG-like <img> with mask so we can use currentColor
  // But to actually inherit color, we use mask-image trick:
  const sty = {
    display: "inline-block",
    width: size,
    height: size,
    backgroundColor: color,
    WebkitMaskImage: `url(assets/icons/${name}.svg)`,
    maskImage: `url(assets/icons/${name}.svg)`,
    WebkitMaskSize: "contain",
    maskSize: "contain",
    WebkitMaskRepeat: "no-repeat",
    maskRepeat: "no-repeat",
    WebkitMaskPosition: "center",
    maskPosition: "center",
    flexShrink: 0,
    ...style
  };
  return <span style={sty} aria-hidden="true" />;
}

// ── Category strip (campaign filter bar) ───────────────────
function CatStrip({ navigate }) {
  const { CAMPAIGNS } = window.WS_DATA || {};
  if (!CAMPAIGNS) return null;

  // Build strip items: "전체 캠페인" + main campaigns + shorts acts
  const shortsActs = (CAMPAIGNS.find(c => c.id === "shorts")?.acts || []).slice(0, 5);
  const mainCampaigns = CAMPAIGNS.filter(c => c.id !== "shorts");

  const items = [
    { key: "all", label: "전체 캠페인", isAll: true },
    { key: "bg3-main",     label: "본편·BG3",             campaignId: "bg3-main" },
    { key: "pholandver",   label: "판델버의 잃어버린 광산", campaignId: "pholandver" },
    { key: "strahd",       label: "스트라드의 저주",         campaignId: "strahd" },
    ...shortsActs.map((a, i) => ({
      key: `shorts-act-${i}`,
      label: a.title.replace(/^단편 #\d+ — /, ""),
      campaignId: "shorts"
    }))
  ];

  return (
    <div className="cat-strip">
      <div className="cat-strip-inner">
        {items.map(item => (
          item.isAll
            ? <span key={item.key} className="pill">{item.label}</span>
            : <button key={item.key}
                      onClick={() => navigate({ name: "campaign", id: item.campaignId })}
                      style={{ fontSize: 13, color: "var(--label-neutral)", background: "none", cursor: "pointer" }}>
                {item.label}
              </button>
        ))}
        <button onClick={() => navigate({ name: "campaigns" })}
                style={{ marginLeft: "auto", fontSize: 13, color: "var(--label-alternative)", whiteSpace: "nowrap", background: "none", cursor: "pointer" }}>
          모든 캠페인 보기 →
        </button>
      </div>
    </div>
  );
}

// ── Header / Top nav (인강 사이트 클래식) ──────────────────
function Header({ route, navigate, parodyIntensity }) {
  const [drawerOpen, setDrawerOpen] = useState(false);
  const NAVS = [
    { id: "camp",      label: "캠프" },
    { id: "origins",   label: "오리진 라인업" },
    { id: "campaigns", label: "캠페인" },
    { id: "library",   label: "자나사의 만물 가이드" },
    { id: "qna",       label: "위더스에게 묻기" }
  ];

  return (
    <>
      <div className="top-strip">
        <div className="top-strip-inner">
          <span>
            <span style={{ opacity: 0.5 }}>위브에 접속하는 모든 모험가를 위한 프리미엄 비전 인강</span>
          </span>
          <span style={{ display: "flex", alignItems: "center" }}>
            <a href="#">캐릭터 시트</a>
            <span className="dot">·</span>
            <a href="#">모험 회고</a>
            <span className="dot">·</span>
            <a href="#">고객센터</a>
            <span className="dot">·</span>
            <a href="#">로그인</a>
          </span>
        </div>
      </div>

      <header className="appbar">
        <div className="appbar-inner">
          <button className="brand-mark" onClick={() => navigate({ name: "camp" })}>
            <span>Weave</span>
            <span className="w">Study</span>
            <span className="ko">위브스터디</span>
          </button>
          <nav>
            {NAVS.map(n => (
              <button
                key={n.id}
                className={route.name === n.id || (n.id === "origins" && route.name === "character") ? "active" : ""}
                onClick={() => navigate({ name: n.id })}
              >
                {n.label}
              </button>
            ))}
          </nav>
          <div className="appbar-right">
            <div className="appbar-search">
              <Icon name="search" size={16} color="var(--label-alternative)" />
              <span className="appbar-search-label">주문 · 캐릭터 검색</span>
            </div>
            <button className="appbar-icon-btn" data-hide-narrow="1" title="알림">
              <Icon name="bell" size={20} />
            </button>
            <button className="appbar-icon-btn" title="캐릭터 시트">
              <Icon name="person-fill" size={20} />
            </button>
            <button className="hamburger" onClick={() => setDrawerOpen(true)} title="메뉴">
              <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
                <line x1="3" y1="7" x2="21" y2="7" />
                <line x1="3" y1="12" x2="21" y2="12" />
                <line x1="3" y1="17" x2="21" y2="17" />
              </svg>
            </button>
          </div>
        </div>
      </header>

      <CatStrip navigate={navigate} />

      {/* Mobile drawer */}
      <div className={"mobile-drawer" + (drawerOpen ? " open" : "")} onClick={() => setDrawerOpen(false)}>
        <div className="mobile-drawer-panel" onClick={e => e.stopPropagation()}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", marginBottom: 24 }}>
            <div className="brand-mark" style={{ fontSize: 18 }}>
              <span>Weave</span><span className="w">Study</span>
            </div>
            <button onClick={() => setDrawerOpen(false)} className="appbar-icon-btn">
              <Icon name="close" size={18} />
            </button>
          </div>
          <div className="mobile-drawer-nav" style={{ display: "flex", flexDirection: "column", gap: 2 }}>
            {NAVS.map(n => (
              <button key={n.id}
                      className={route.name === n.id || (n.id === "origins" && route.name === "character") ? "active" : ""}
                      onClick={() => { navigate({ name: n.id }); setDrawerOpen(false); }}>
                {n.label}
              </button>
            ))}
          </div>
          <div style={{ marginTop: 24, padding: 16, background: "var(--cool-neutral-99)", borderRadius: 12 }}>
            <div style={{ fontSize: 11, fontWeight: 700, color: "var(--label-alternative)", letterSpacing: "0.08em", textTransform: "uppercase", marginBottom: 8 }}>
              소개
            </div>
            <div style={{ fontSize: 13, color: "var(--label-neutral)", lineHeight: 1.55 }}>
              위브에 접속하는 모든 모험가를 위한<br />
              프리미엄 비전 인강 · 위브스터디.
            </div>
          </div>
        </div>
      </div>
    </>
  );
}

// ── Footer (with hidden D20 trigger) ───────────────────────
function Footer({ navigate }) {
  return (
    <footer style={{ background: "var(--weave-ink)", color: "rgba(255,255,255,0.7)", marginTop: 96 }}>
      <div style={{ maxWidth: "var(--container-max)", margin: "0 auto", padding: "56px 24px 32px" }}>
        <div style={{ display: "grid", gridTemplateColumns: "1.4fr 1fr 1fr 1fr", gap: 48 }}>
          <div>
            <div style={{ fontSize: 22, fontWeight: 800, letterSpacing: "-0.03em", color: "white", marginBottom: 12 }}>
              Weave<span style={{ color: "var(--weave-violet-bright)" }}>Study</span>
              <span style={{ fontSize: 13, fontWeight: 600, opacity: 0.5, marginLeft: 8 }}>위브스터디</span>
            </div>
            <p style={{ fontSize: 13, lineHeight: 1.6, opacity: 0.6, margin: 0 }}>
              위브(Weave)에 접속하는 모든 모험가를 위한 프리미엄 비전 인강.<br />
              모든 명사 워딩은 발더스 게이트 3 공식 한국어 번역을 따릅니다.
            </p>
            <div style={{ marginTop: 16, fontSize: 12, opacity: 0.4 }}>
              © 2026 WeaveStudy. 본 사이트는 동인 아카이브이며 라리안 스튜디오 · 위저즈 오브 더 코스트와 무관합니다.
            </div>
          </div>
          <div>
            <div style={{ fontSize: 13, fontWeight: 700, color: "white", marginBottom: 12 }}>커리큘럼</div>
            <FootLink>본편 · 베스티 캠페인</FootLink>
            <FootLink>판델버의 잃어버린 광산</FootLink>
            <FootLink>스트라드의 저주</FootLink>
            <FootLink>단편탁 모음</FootLink>
          </div>
          <div>
            <div style={{ fontSize: 13, fontWeight: 700, color: "white", marginBottom: 12 }}>자료</div>
            <FootLink>자나사의 만물 가이드</FootLink>
            <FootLink>플레이어 핸드북 (PHB)</FootLink>
            <FootLink>위더스에게 묻기</FootLink>
            <FootLink>출처 표기 규칙</FootLink>
          </div>
          <div>
            <div style={{ fontSize: 13, fontWeight: 700, color: "white", marginBottom: 12 }}>고객센터</div>
            <FootLink>1:1 문의</FootLink>
            <FootLink>이용약관</FootLink>
            <FootLink>개인정보 처리방침</FootLink>
            <FootLink>운영자에게 보내는 캠프 보급품</FootLink>
          </div>
        </div>

        <div className="weave-line" style={{ margin: "32px 0 24px", opacity: 0.3 }} />

        <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between" }}>
          <div style={{ fontSize: 12, opacity: 0.4 }}>
            <span>한국어</span>
            <span style={{ padding: "0 8px" }}>·</span>
            <span>English</span>
            <span style={{ padding: "0 8px" }}>·</span>
            <span>日本語</span>
          </div>
          <D20Trigger onUnlock={() => navigate({ name: "alignment" })} />
        </div>
      </div>
    </footer>
  );
}

function FootLink({ children }) {
  return (
    <a style={{ display: "block", fontSize: 13, lineHeight: 1.9, opacity: 0.65 }}
       onMouseOver={e => e.currentTarget.style.opacity = 1}
       onMouseOut={e => e.currentTarget.style.opacity = 0.65}>
      {children}
    </a>
  );
}

// ── D20 dice trigger ───────────────────────────────────────
function D20Trigger({ onUnlock }) {
  const [rolling, setRolling] = useState(false);
  const [shown, setShown] = useState(null); // last face shown
  const [tooltip, setTooltip] = useState(false);

  const roll = () => {
    if (rolling) return;
    setRolling(true);
    setShown(null);
    // ~30% chance natural 20, else 1..19. Force-show 20 if user has clicked >=3 times w/o luck.
    const tries = parseInt(sessionStorage.getItem("d20-tries") || "0") + 1;
    sessionStorage.setItem("d20-tries", String(tries));
    const forced = tries >= 3;
    const value = forced
      ? 20
      : (Math.random() < 0.30 ? 20 : 1 + Math.floor(Math.random() * 19));
    setTimeout(() => {
      setShown(value);
      setRolling(false);
      if (value === 20) {
        sessionStorage.setItem("d20-tries", "0");
        setTimeout(() => onUnlock && onUnlock(), 700);
      }
    }, 1400);
  };

  return (
    <div style={{ display: "flex", alignItems: "center", gap: 12, position: "relative" }}
         onMouseEnter={() => setTooltip(true)}
         onMouseLeave={() => setTooltip(false)}>
      {shown !== null && (
        <div style={{
          fontSize: 13,
          fontFamily: "var(--font-mono)",
          color: shown === 20 ? "var(--weave-teal)" : "rgba(255,255,255,0.5)",
          fontWeight: 700,
          letterSpacing: "0.05em"
        }}>
          {shown === 20 ? "내츄럴 20! 진입 중…" : `굴림 결과: ${shown}`}
        </div>
      )}
      {tooltip && (
        <div style={{
          position: "absolute",
          right: 0, bottom: "calc(100% + 12px)",
          background: "var(--cool-neutral-15)",
          border: "1px solid rgba(255,255,255,0.12)",
          padding: "8px 12px",
          borderRadius: 8,
          fontSize: 11,
          color: "rgba(255,255,255,0.8)",
          whiteSpace: "nowrap",
          letterSpacing: "0.02em"
        }}>
          위더스: "… 굴려 보시겠습니까?"
        </div>
      )}
      <button className={"d20-trigger" + (rolling ? " d20-rolling" : "")}
              onClick={roll} title="D20 굴림">
        <D20Glyph />
      </button>
    </div>
  );
}

function D20Glyph({ size = 28 }) {
  // simple D20 silhouette
  return (
    <svg width={size} height={size} viewBox="0 0 32 32" fill="none">
      <path d="M16 3 L28 11 L24 26 L8 26 L4 11 Z" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
      <path d="M16 3 L16 16 L4 11 M16 16 L8 26 M16 16 L24 26 M16 16 L28 11" stroke="currentColor" strokeWidth="1" opacity="0.7" />
      <text x="16" y="22" fill="currentColor" fontSize="9" textAnchor="middle" fontFamily="ui-monospace, monospace" fontWeight="700">20</text>
    </svg>
  );
}

// ── Portrait card ──────────────────────────────────────────
function Portrait({ character, size = "md", style, showBadge = true }) {
  const sizes = {
    sm: { w: 72, h: 96, font: 36, glyph: 60 },
    md: { w: 200, h: 256, font: 84, glyph: 120 },
    lg: { w: 280, h: 360, font: 112, glyph: 160 },
    xl: { w: 320, h: 440, font: 132, glyph: 180 }
  };
  const s = sizes[size];
  const [c1, c2] = character.portraitColors || ["#7D5EF7", "#6FD5C8"];
  const initial = character.name?.charAt(0) || "?";
  return (
    <div style={{
      width: s.w, height: s.h, borderRadius: 14, overflow: "hidden",
      position: "relative",
      background: `linear-gradient(180deg, transparent 40%, rgba(0,0,0,0.35) 100%), linear-gradient(135deg, ${c1} 0%, ${c2} 100%)`,
      color: "white",
      flexShrink: 0,
      ...style
    }}>
      <span style={{
        position: "absolute",
        top: "50%", left: "50%", transform: "translate(-50%, -55%)",
        fontSize: s.font, fontWeight: 800,
        fontFamily: "var(--font-serif-display)",
        color: "rgba(255,255,255,0.28)",
        letterSpacing: "-0.04em",
        userSelect: "none"
      }}>{initial}</span>
      <div style={{
        position: "absolute", inset: 0,
        backgroundImage: "radial-gradient(circle, rgba(255,255,255,0.20) 1px, transparent 1.5px)",
        backgroundSize: "22px 22px",
        mixBlendMode: "overlay",
        opacity: 0.5
      }} />
      <ClassGlyph cls={character.classKey} size={s.glyph} />
      {showBadge && character.rank && (
        <div style={{
          position: "absolute", top: 12, left: 12,
          background: "rgba(0,0,0,0.55)",
          backdropFilter: "blur(6px)",
          color: "white",
          fontSize: 10,
          fontWeight: 700,
          padding: "3px 8px",
          borderRadius: 4,
          letterSpacing: "0.02em"
        }}>
          {character.rank}
        </div>
      )}
      {character.isDreamju && (
        <div style={{
          position: "absolute", top: 12, right: 12,
          background: "rgba(255,255,255,0.18)",
          color: "white",
          fontSize: 10,
          fontWeight: 700,
          padding: "3px 8px",
          borderRadius: 4,
          letterSpacing: "0.02em",
          border: "1px solid rgba(255,255,255,0.3)"
        }}>드림주</div>
      )}
    </div>
  );
}

// ── Decorative class glyph in portraits ────────────────────
function ClassGlyph({ cls, size = 120 }) {
  const map = {
    sorcerer: "M50 10 L60 40 L90 50 L60 60 L50 90 L40 60 L10 50 L40 40 Z",
    wizard:   "M50 10 L57 35 L82 35 L62 50 L70 75 L50 60 L30 75 L38 50 L18 35 L43 35 Z",
    warlock:  "M50 8 C70 8 88 24 88 50 C88 76 70 92 50 92 C30 92 12 76 12 50 C12 24 30 8 50 8 Z M50 25 L65 45 L50 75 L35 45 Z",
    cleric:   "M48 8 L52 8 L52 38 L82 38 L82 42 L52 42 L52 90 L48 90 L48 42 L18 42 L18 38 L48 38 Z",
    druid:    "M50 10 C70 20 75 40 65 60 C75 50 85 55 80 75 C70 70 60 75 50 90 C40 75 30 70 20 75 C15 55 25 50 35 60 C25 40 30 20 50 10 Z",
    rogue:    "M30 15 L70 15 L65 50 L75 85 L25 85 L35 50 Z",
    fighter:  "M50 10 L60 25 L80 30 L75 50 L85 60 L70 65 L65 85 L50 75 L35 85 L30 65 L15 60 L25 50 L20 30 L40 25 Z",
    bard:     "M30 20 Q50 5 70 20 L70 70 Q50 85 30 70 Z M40 35 L60 35 M40 45 L60 45 M40 55 L60 55",
    paladin:  "M50 10 L65 25 L65 60 Q50 75 50 90 Q50 75 35 60 L35 25 Z",
    ranger:   "M50 5 L55 30 L80 30 L60 45 L70 70 L50 55 L30 70 L40 45 L20 30 L45 30 Z M50 65 L50 95",
    monk:     "M50 15 C65 15 70 30 60 40 C75 45 75 65 60 70 C60 85 40 85 40 70 C25 65 25 45 40 40 C30 30 35 15 50 15 Z",
    barbarian: "M30 15 L45 5 L55 5 L70 15 L75 35 L85 45 L70 50 L75 75 L50 90 L25 75 L30 50 L15 45 L25 35 Z"
  };
  const path = map[cls] || map.wizard;
  return (
    <svg
      width={size} height={size}
      viewBox="0 0 100 100"
      style={{
        position: "absolute",
        right: -10, top: -10,
        opacity: 0.16,
        pointerEvents: "none"
      }}>
      <path d={path} fill="white" />
    </svg>
  );
}

// ── Ability score block ────────────────────────────────────
function AbilityScore({ name, value, compact = false }) {
  const mod = Math.floor((value - 10) / 2);
  const sign = mod >= 0 ? "+" : "";
  if (compact) {
    return (
      <div style={{
        textAlign: "center",
        padding: "10px 8px",
        background: "var(--fill-normal)",
        borderRadius: 10
      }}>
        <div style={{ fontSize: 10, fontWeight: 700, color: "var(--label-alternative)", letterSpacing: "0.08em", textTransform: "uppercase" }}>{name}</div>
        <div style={{ fontSize: 22, fontWeight: 800, fontFamily: "var(--font-mono)", color: "var(--weave-ink)", lineHeight: 1.1, marginTop: 4 }}>{value}</div>
        <div style={{ fontSize: 11, fontWeight: 600, color: "var(--weave-violet)" }}>{sign}{mod}</div>
      </div>
    );
  }
  return (
    <div style={{
      border: "1px solid var(--line-normal)",
      borderRadius: 14,
      padding: 16,
      background: "white",
      textAlign: "center",
      position: "relative"
    }}>
      <div style={{ fontSize: 11, fontWeight: 700, color: "var(--label-alternative)", letterSpacing: "0.1em", textTransform: "uppercase" }}>{name}</div>
      <div style={{ fontSize: 32, fontWeight: 800, fontFamily: "var(--font-mono)", color: "var(--weave-ink)", lineHeight: 1.1, margin: "8px 0 2px" }}>{value}</div>
      <div style={{
        display: "inline-block",
        fontSize: 12, fontWeight: 700,
        color: "white",
        background: "var(--weave-violet)",
        padding: "2px 10px",
        borderRadius: 999,
        fontFamily: "var(--font-mono)"
      }}>
        {sign}{mod}
      </div>
    </div>
  );
}

// ── Section title ──────────────────────────────────────────
function SectionTitle({ eyebrow, title, sub, action }) {
  return (
    <div className="sec-header">
      <div>
        {eyebrow && <div className="sec-eyebrow">{eyebrow}</div>}
        <div className="sec-title">
          {title}
          {sub && <small>{sub}</small>}
        </div>
      </div>
      {action && <div>{action}</div>}
    </div>
  );
}

// ── Spell slot pips ────────────────────────────────────────
function SpellSlotRow({ level, count, used = 0 }) {
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
      <div style={{
        width: 32, height: 32, borderRadius: 8,
        background: "var(--weave-ink)",
        color: "white",
        display: "flex", alignItems: "center", justifyContent: "center",
        fontSize: 12, fontWeight: 700,
        fontFamily: "var(--font-mono)"
      }}>L{level}</div>
      <div style={{ display: "flex", gap: 6 }}>
        {Array.from({ length: count }).map((_, i) => (
          <div key={i} style={{
            width: 18, height: 18,
            borderRadius: 999,
            background: i < used ? "var(--fill-normal)" : "var(--weave-violet)",
            border: i < used ? "1px dashed var(--line-normal)" : "none",
            boxShadow: i < used ? "none" : "0 0 0 2px rgba(101,65,242,0.15)"
          }} />
        ))}
      </div>
      <span style={{ fontSize: 12, color: "var(--label-alternative)", fontFamily: "var(--font-mono)" }}>
        {count - used} / {count}
      </span>
    </div>
  );
}

// Expose to window for cross-file access
Object.assign(window, {
  Icon, Header, Footer, CatStrip, D20Trigger, D20Glyph,
  Portrait, ClassGlyph, AbilityScore, SectionTitle, SpellSlotRow, FootLink
});
