// gw2-components.jsx — modern navy dashboard: toolbar, cards, VS banner, KPI, donut.
const { useState: useS2, useRef: useRef2, useEffect: useEff2 } = React;

function Card({ children, style, pad = 18, className = "" }) {
  return (
    <div className={"g2-card " + className} style={{
      background: "var(--surface)", border: "1px solid var(--card-border)",
      borderRadius: 16, padding: pad, ...style,
    }}>{children}</div>
  );
}

function Crest2({ tag, size = 34, tone = "accent" }) {
  const color = tone === "accent" ? "var(--accent)" : tone === "win" ? "var(--win)" : "var(--text-dim)";
  return (
    <div style={{
      width: size, height: size, borderRadius: size * 0.32, flexShrink: 0,
      display: "grid", placeItems: "center",
      background: `color-mix(in oklab, ${color} 16%, var(--surface-2))`,
      border: `1px solid color-mix(in oklab, ${color} 45%, transparent)`,
      color, fontSize: size * 0.33, fontWeight: 700, letterSpacing: "-0.02em",
      fontFamily: "var(--font-mono)",
    }}>{tag}</div>
  );
}

function guildLogoFallbackSrc(guild) {
  if (guild.logo) return guild.logo;
  const name = String(guild.name || "").trim();
  if (!name || name === hwT("addGuild")) return "";
  return encodeURI(name + ".webp");
}

// Guild logo — image-slot keyed per guild, shared between app and admin via the
// .image-slots.state.json sidecar. Shows the placeholder emblem (guild.logo)
// until the user drops a real logo. editable controls only intent/labelling;
// the slot itself is interactive wherever the runtime can write the sidecar.
function GuildLogo({ guild, size = 56, radius = 14, placeholder, readOnly = false, controls }) {
  const id = "guild-logo-" + (guild.id || "home");
  const dim = typeof size === "number" ? size + "px" : size;
  const fallbackSrc = guildLogoFallbackSrc(guild);
  const label = placeholder || hwT("logo");
  if (readOnly) return <ReadOnlyGuildLogo guild={guild} size={size} radius={radius} placeholder={label} />;
  return React.createElement("image-slot", {
    id, src: fallbackSrc, shape: "rounded", radius: String(radius), placeholder: label,
    readonly: readOnly ? "" : undefined,
    controls: controls || undefined,
    style: {
      width: dim, height: dim, display: "block", flexShrink: 0,
      border: "none", borderRadius: radius + "px",
      background: "transparent", overflow: "hidden",
      "--image-slot-bg": "transparent",
    },
  });
}

// Persist resolved logo data-URLs in localStorage so on reload the real logo
// shows immediately instead of flashing the placeholder/fallback first.
function lsLogoGet(id) { try { return localStorage.getItem("gw_logo_" + id) || ""; } catch (e) { return ""; } }
function lsLogoSet(id, v) { try { if (v) localStorage.setItem("gw_logo_" + id, v); } catch (e) { /* quota */ } }

function ReadOnlyGuildLogo({ guild, size, radius, placeholder }) {
  const id = "guild-logo-" + (guild.id || "home");
  const dim = typeof size === "number" ? size + "px" : size;
  const fallbackSrc = guildLogoFallbackSrc(guild);
  const logoCache = window.__GW_LOGO_CACHE || (window.__GW_LOGO_CACHE = {});
  const [url, setUrl] = useS2(() => logoCache[id] || lsLogoGet(id) || fallbackSrc || "");
  useEff2(() => {
    let alive = true;
    const cache = window.__GW_LOGO_CACHE || (window.__GW_LOGO_CACHE = {});
    const cached = cache[id] || lsLogoGet(id) || "";
    if (cached && !cache[id]) cache[id] = cached;
    // React reuses this component when the selected guild changes. Reset the
    // visible URL immediately so the previous guild's logo cannot linger.
    setUrl(cached || fallbackSrc || "");
    // Fetch ONLY this logo's slot (?id=) instead of the whole sidecar, which
    // can be several MB once battle screenshots exist — polling it in full was
    // a memory-pressure / Out-of-Memory source.
    const refresh = () => {
      if (document.hidden) return;
      fetch("/api/image-slots?id=" + encodeURIComponent(id) + "&t=" + Date.now(), { cache: "no-store" })
        .then((r) => (r.ok ? r.json() : {}))
        .then((j) => {
          if (!alive) return;
          const v = j && j[id];
          const u = (window.HW_slotImageUrl && window.HW_slotImageUrl(v)) || null;
          if (u) {
            cache[id] = u;
            // Only data: URLs are worth caching to localStorage (avoids a
            // placeholder flash). An R2 URL is cheap to re-resolve and the
            // browser/CDN caches the image itself.
            if (/^data:image\//i.test(u)) lsLogoSet(id, u);
            setUrl(u);
          } else {
            setUrl(cache[id] || fallbackSrc || "");
          }
        })
        .catch(() => { if (alive) setUrl(cache[id] || fallbackSrc || ""); });
    };
    refresh();
    const timer = setInterval(refresh, 15000);
    const onVis = () => { if (!document.hidden) refresh(); };
    document.addEventListener("visibilitychange", onVis);
    return () => { alive = false; clearInterval(timer); document.removeEventListener("visibilitychange", onVis); };
  }, [id, fallbackSrc]);
  return (
    <span style={{
      width: dim, height: dim, display: "grid", placeItems: "center", flexShrink: 0,
      border: "none", borderRadius: radius + "px", background: "transparent", overflow: "hidden",
    }}>
      {url ? (
        <img src={url} alt="" onError={() => setUrl("")} style={{ width: "100%", height: "100%", objectFit: "contain", display: "block" }} />
      ) : (
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 10, color: "var(--text-faint)" }}>{placeholder}</span>
      )}
    </span>
  );
}

function Toolbar({ guilds, value, onChange, botMode, onBotMode }) {
  const lang = window.HW_LANG || "ru";
  return (
    <div className="g2-toolbar" style={{
      display: "flex", alignItems: "center", gap: 18, rowGap: 14, flexWrap: "wrap", padding: "clamp(14px, 3vw, 20px) clamp(16px, 3vw, 26px)",
      background: "var(--surface)", border: "1px solid var(--card-border)", borderRadius: 18,
    }}>
      <div className="g2-brand" onClick={() => onChange && onChange(null)} title={hwT("backHome")} role="button" style={{ display: "flex", alignItems: "center", gap: 16, flexShrink: 0, minWidth: 0, cursor: "pointer" }}>
        <img src="logo.webp" alt="" style={{ width: "clamp(52px, 12vw, 76px)", height: "clamp(52px, 12vw, 76px)", objectFit: "contain", flexShrink: 0, filter: "drop-shadow(0 4px 10px rgba(0,0,0,.20))" }} />
        <div style={{ minWidth: 0 }}>
          <div style={{ fontSize: "clamp(26px, 6vw, 35px)", fontWeight: 800, letterSpacing: "-0.025em", color: "var(--text)", lineHeight: 1, whiteSpace: "nowrap" }}>{hwT("guildWar")}</div>
          <div style={{ fontFamily: "var(--font-mono)", fontSize: 12, letterSpacing: ".16em", textTransform: "uppercase", color: "var(--text-faint)", marginTop: 6 }}>{hwT("dashboard")}</div>
        </div>
      </div>
      <div className="g2-toolbar-spacer" style={{ flex: "1 1 auto" }} />
      <div className="g2-lang-wrap"><LanguageToggle lang={lang} setLang={window.HW_SET_LANG || (() => {})} /></div>
      <div className={"g2-view-toggle" + (!value ? " is-disabled" : "")}
        role="group" aria-label={hwT("dashboard")}>
        <button className={!botMode ? "is-active" : ""} disabled={!value}
          aria-pressed={!botMode} onClick={() => onBotMode(false)}
          title={!value ? hwT("selectGuildFirst") : hwT("attackLog")}>
          {hwT("attackLog")}
        </button>
        <button className={botMode ? "is-active" : ""} disabled={!value}
          aria-pressed={botMode} onClick={() => onBotMode(true)}
          title={!value ? hwT("selectGuildFirst") : hwT("botAttacks")}>
          {hwT("botAttacks")}
        </button>
      </div>
      <GuildSelect2 guilds={guilds} value={value} onChange={onChange} />
    </div>
  );
}

function GuildSelect2({ guilds, value, onChange }) {
  const [open, setOpen] = useS2(false);
  const ref = useRef2(null);
  useEff2(() => {
    const h = (e) => { if (ref.current && !ref.current.contains(e.target)) setOpen(false); };
    document.addEventListener("mousedown", h);
    return () => document.removeEventListener("mousedown", h);
  }, []);
  const sel = guilds.find((g) => g.id === value);
  const cta = value == null; // nothing chosen yet → draw attention
  return (
    <div className="g2-guild-select" ref={ref} style={{ position: "relative", width: "min(280px, 100%)", flex: "0 1 280px" }}>
      <button onClick={() => setOpen((o) => !o)} className={cta ? "g2-guild-cta" : undefined} style={{
        width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "9px 12px",
        background: "var(--surface-2)", border: `1px solid ${(open || cta) ? "var(--accent)" : "var(--card-border)"}`,
        borderRadius: 12, cursor: "pointer", color: "var(--text)", fontFamily: "var(--font-sans)",
        fontSize: "var(--fs-base)", textAlign: "left", transition: "border-color .15s",
      }}>
        {sel ? <GuildLogo guild={sel} size={26} radius={8} readOnly /> : null}
        <span style={{ flex: 1 }}>{sel ? sel.name : hwT("selectGuild")}</span>
        <span style={{ color: "var(--text-faint)", fontSize: 10, transform: open ? "rotate(180deg)" : "none", transition: "transform .15s" }}>▼</span>
      </button>
      {open && (
        <div style={{
          position: "absolute", top: "calc(100% + 6px)", right: 0, left: 0, zIndex: 50,
          background: "var(--surface)", border: "1px solid var(--card-border)", borderRadius: 12,
          padding: 5, boxShadow: "0 20px 50px -16px rgba(0,0,0,.7)",
        }}>
          {guilds.map((g) => (
            <button key={g.id} onClick={() => { onChange(g.id); setOpen(false); }} className="g2-opt" style={{
              width: "100%", display: "flex", alignItems: "center", gap: 10, padding: "8px 9px",
              background: g.id === value ? "var(--surface-2)" : "transparent", border: "none",
              borderRadius: 9, cursor: "pointer", color: "var(--text)", fontFamily: "var(--font-sans)",
              fontSize: "var(--fs-base)", textAlign: "left",
            }}>
              <GuildLogo guild={g} size={24} radius={7} readOnly />
              <span style={{ flex: 1 }}>{g.name}</span>
            </button>
          ))}
        </div>
      )}
    </div>
  );
}

function VSBanner2({ home, away }) {
  const Side = ({ g }) => (
    <div style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: 14, flex: "0 1 170px", minWidth: 0 }}>
      <GuildLogo guild={g} size={108} radius={22} readOnly />
      <div style={{ fontSize: 18, fontWeight: 800, color: "var(--text)", letterSpacing: "-0.02em", textAlign: "center", lineHeight: 1.16, maxWidth: "100%", textWrap: "balance" }}>{g.name}</div>
    </div>
  );
  return (
    <Card className="g2-vs-card" pad="34px 28px" style={{ width: "100%", margin: "0 auto", display: "flex", alignItems: "center", justifyContent: "center", gap: 22, background: "linear-gradient(100deg, var(--surface), var(--surface-2))" }}>
      <Side g={home} />
      <div style={{ fontFamily: "var(--font-mono)", fontWeight: 800, fontSize: 16, color: "#fff", width: 40, height: 40, display: "grid", placeItems: "center", flexShrink: 0, borderRadius: 40, background: "linear-gradient(150deg, var(--accent), color-mix(in oklab, var(--accent) 55%, #000))" }}>VS</div>
      <Side g={away} />
    </Card>
  );
}

// Flowing "mesh gradient" backdrop — the real @paper-design/shaders MeshGradient,
// mounted imperatively onto a container (the core lib is framework-agnostic, loaded
// as a module in index.html → window.__PaperShaders). Tinted to our palette. The
// CSS gradient on .g2-welcome stays as the fallback until/if the lib mounts.
const WELCOME_SHADER_COLORS = ["#0b1626", "#15243c", "#4d96f0", "#cee4ff"];
function ShaderBg2() {
  const ref = useRef2(null);
  useEff2(() => {
    let mount = null, iv = null, cancelled = false;
    const build = () => {
      if (cancelled || mount || !ref.current || !window.__PaperShaders) return;
      const P = window.__PaperShaders;
      const uniforms = {
        u_colors: WELCOME_SHADER_COLORS.map(P.getShaderColorFromString),
        u_colorsCount: WELCOME_SHADER_COLORS.length,
        u_distortion: 1.0, u_swirl: 0.22, u_grainMixer: 0, u_grainOverlay: 0,
        u_fit: (P.ShaderFitOptions && P.ShaderFitOptions.cover) || 2,
        u_rotation: 0, u_scale: 1, u_offsetX: 0, u_offsetY: 0,
        u_originX: 0.5, u_originY: 0.5, u_worldWidth: 0, u_worldHeight: 0,
      };
      try {
        mount = new P.ShaderMount(ref.current, P.meshGradientFragmentShader, uniforms, {}, 0.6);
      } catch (e) { /* leave CSS fallback */ }
      if (mount && iv) { clearInterval(iv); iv = null; }
    };
    build();
    const onReady = () => build();
    window.addEventListener("papershaders-ready", onReady);
    if (!mount) iv = setInterval(build, 200);
    return () => {
      cancelled = true;
      window.removeEventListener("papershaders-ready", onReady);
      if (iv) clearInterval(iv);
      if (mount && typeof mount.dispose === "function") mount.dispose();
    };
  }, []);
  return <div ref={ref} className="g2-welcome-shader" aria-hidden="true" />;
}

// Full-screen welcome shown before a guild is chosen — only the home guild's
// logo (centred, large) over a flowing animated shader backdrop.
function WelcomePanel2({ home }) {
  return (
    <div className="g2-welcome">
      <ShaderBg2 />
      <div className="g2-welcome-inner">
        <div className="g2-welcome-logo">
          <GuildLogo guild={home} size="min(300px, 60vw)" radius={28} readOnly placeholder={home.name} />
        </div>
        <div className="g2-welcome-name">{home.name}</div>
        <div className="g2-welcome-hint"><span className="g2-welcome-arrow">↑</span> {hwT("chooseOpponentGuild")}</div>
      </div>
    </div>
  );
}

function Kpi({ label, value, sub, tone, dir }) {
  const color = tone === "win" ? "var(--win)" : tone === "loss" ? "var(--loss)" : tone === "accent" ? "var(--accent)" : "var(--text)";
  return (
    <Card pad={18} style={{ display: "flex", flexDirection: "column", justifyContent: "space-between", minHeight: 110 }}>
      <div style={{ fontSize: 13, fontWeight: 600, color: "var(--text-dim)" }}>{label}</div>
      <div style={{ display: "flex", alignItems: "baseline", gap: 8, marginTop: 8 }}>
        <span style={{ fontSize: 40, fontWeight: 800, lineHeight: 1, letterSpacing: "-0.03em", color }}>{value}</span>
        {dir && <span style={{ fontSize: 18, color }}>{dir === "up" ? "↑" : "↓"}</span>}
      </div>
      {sub && <div style={{ fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-faint)", marginTop: 6 }}>{sub}</div>}
    </Card>
  );
}

function Donut({ segments, centerTop, centerBig, centerSub }) {
  let acc = 0;
  const total = segments.reduce((s, x) => s + x.value, 0) || 1;
  const stops = segments.map((s) => {
    const from = (acc / total) * 100; acc += s.value;
    const to = (acc / total) * 100;
    return `${s.color} ${from}% ${to}%`;
  }).join(", ");
  return (
    <div style={{ display: "flex", alignItems: "center", gap: 24, flexWrap: "wrap" }}>
      <div style={{ position: "relative", width: 168, height: 168, flexShrink: 0 }}>
        <div style={{ position: "absolute", inset: 0, borderRadius: "50%", background: `conic-gradient(${stops})` }} />
        <div style={{ position: "absolute", inset: 22, borderRadius: "50%", background: "var(--surface)", display: "grid", placeItems: "center", textAlign: "center" }}>
          <div>
            <div style={{ fontSize: 11, letterSpacing: ".12em", textTransform: "uppercase", color: "var(--text-faint)" }}>{centerTop}</div>
            <div style={{ fontSize: 38, fontWeight: 800, letterSpacing: "-0.03em", color: "var(--text)", lineHeight: 1.1 }}>{centerBig}</div>
            <div style={{ fontFamily: "var(--font-mono)", fontSize: 11, color: "var(--text-faint)" }}>{centerSub}</div>
          </div>
        </div>
      </div>
      <div style={{ flex: 1, minWidth: 160, display: "flex", flexDirection: "column", gap: 9 }}>
        {segments.map((s, i) => (
          <div key={i} style={{ display: "flex", alignItems: "center", gap: 10 }}>
            <span style={{ width: 11, height: 11, borderRadius: 3, background: s.color, flexShrink: 0 }} />
            <span style={{ flex: 1, fontSize: 13, color: "var(--text-dim)" }}>{s.label}</span>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 13, fontWeight: 600, color: "var(--text)" }}>{s.value}</span>
            <span style={{ fontFamily: "var(--font-mono)", fontSize: 12, color: "var(--text-faint)", width: 38, textAlign: "right" }}>{Math.round((s.value / total) * 100)}%</span>
          </div>
        ))}
      </div>
    </div>
  );
}

Object.assign(window, { Card, Crest2, GuildLogo, Toolbar, GuildSelect2, VSBanner2, ShaderBg2, WelcomePanel2, Kpi, Donut });
