// shared.jsx — utilities + content used across all 3 variations
// Exposes everything to window so the variation files can pick up.

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

// ─────────────── Content (shared across variations) ───────────────
const HUB_NAME = "SKILLSTACK";
const HUB_NAME_CN = "SKILLSTACK";
const HUB_TAGLINE = "AI Trading Bot Strategy Skills · deployed by a human, ran by machines";
const HUB_TAGLINE_CN = "AI 交易策略技能 · 人类部署，机器执行";

const SKILLS = [
  {
    id: "token_launch",
    code: "SKL_001",
    name: "One-Click Token Launch",
    name_cn: "一键发币",
    blurb: "Launch tokens on Solana & BSC in one click — pump.fun, Moonit, Bags, Flap, LetsBonk, FourMeme.",
    blurb_cn: "一键在 Solana/BSC 发币 · 支持主流发射台",
    tags: ["Python", "Solana", "BSC", "OKX DEX"],
    metric: { label: "launchpads", value: "6" },
    color: "amber",
    status: "LIVE",
    deployed: "2026.03.01",
    notes: "Supports 6 launchpads across Solana and BSC: pump.fun, Moonit, Bags.fm, Flap.sh, LetsBonk, FourMeme. Token metadata (name, symbol, image, description, socials) is configured once — the skill handles IPFS upload, fee config, transaction signing via OKX TEE wallet, and confirmation polling. DRY_RUN mode available for testing without on-chain execution.",
    notes_cn: "支持 Solana 和 BSC 上的 6 个发射台：pump.fun、Moonit、Bags.fm、Flap.sh、LetsBonk、FourMeme。代币元数据（名称、符号、图片、简介、社交链接）一次配置完成 — Skill 自动处理 IPFS 上传、手续费配置、OKX TEE 钱包签名及确认轮询。支持 DRY_RUN 模式，可在不上链的情况下测试全流程。",
    install: "npx skills add okx/plugin-store --skill one-click-token-launch",
  },
  {
    id: "wallet_tracker",
    code: "SKL_002",
    name: "Wallet Tracker",
    name_cn: "钱包追踪器",
    blurb: "Track smart money wallets and auto-copy trades filtered by market cap threshold.",
    blurb_cn: "追踪聪明钱 · 按市值过滤自动跟单",
    tags: ["Python", "OKX DEX", "Solana", "Dashboard"],
    metric: { label: "chains", value: "5" },
    color: "rose",
    status: "LIVE",
    deployed: "2026.03.15",
    notes: "Monitors a configurable list of smart money wallets across chains. Filters copy trades by market cap threshold — only follows positions in tokens above your set Mcap floor. Executes swaps via OKX DEX aggregator with TEE-signed transactions. Live dashboard shows tracked wallets, recent trades, and P&L.",
    notes_cn: "监控一组可配置的聪明钱钱包，跨链追踪。按市值门槛过滤跟单 — 只跟进市值达到设定下限的代币。通过 OKX DEX 聚合器 + TEE 签名执行换币。实时仪表盘展示追踪钱包、近期交易及盈亏情况。",
    install: "npx skills add okx/plugin-store --skill wallet-tracker-mcap",
  },
  {
    id: "mainstream_spot",
    code: "SKL_003",
    name: "Mainstream Spot Order",
    name_cn: "主流现货交易",
    blurb: "Multi-chain spot trading bot with technical signals, auto-sizing, and TEE-signed execution.",
    blurb_cn: "多链现货交易机器人 · 技术信号 + TEE 签名执行",
    tags: ["Python", "OKX DEX", "Multi-chain", "Dashboard"],
    metric: { label: "chains", value: "8+" },
    color: "emerald",
    status: "LIVE",
    deployed: "2026.03.20",
    notes: "Spot trading bot for mainstream tokens across 8+ chains including Solana, Ethereum, BSC, Base, and Arbitrum. Uses candlestick data from OKX DEX API for signal generation. Supports both Solana (2-step sign + broadcast) and EVM (1-step with ERC-20 approval handling). Auto-sizes positions based on available balance.",
    notes_cn: "支持 Solana、以太坊、BSC、Base、Arbitrum 等 8 条以上链的主流代币现货交易机器人。使用 OKX DEX API K线数据生成信号。兼容 Solana（两步签名+广播）和 EVM（一步 + ERC-20 授权处理）。根据可用余额自动计算仓位大小。",
    install: "npx skills add okx/plugin-store --skill mainstream-spot-order",
  },
  {
    id: "rwa_alpha",
    code: "SKL_004",
    name: "RWA Alpha",
    name_cn: "RWA 现货交易",
    blurb: "Real-world asset token trading — spot entries on RWA narratives with on-chain execution.",
    blurb_cn: "真实世界资产代币现货交易 · 链上执行",
    tags: ["Python", "OKX DEX", "Solana", "Dashboard"],
    metric: { label: "strategy", value: "spot" },
    color: "violet",
    status: "LIVE",
    deployed: "2026.03.25",
    notes: "Focused on Real World Asset tokens — identifies momentum entries on RWA narratives using on-chain price data and volume signals. Executes spot buys and sells via OKX DEX with TEE wallet signing. Includes a live dashboard for monitoring open positions, entry prices, and P&L in real time.",
    notes_cn: "专注于真实世界资产代币 — 基于链上价格数据和成交量信号识别 RWA 叙事中的动量入场时机。通过 OKX DEX + TEE 钱包签名执行现货买卖。内置实时仪表盘，监控持仓、入场价及盈亏。",
    install: "npx skills add okx/plugin-store --skill rwa-alpha",
  },
  {
    id: "macro_intel",
    code: "SKL_005",
    name: "Macro Intelligence",
    name_cn: "宏观智能",
    blurb: "Real-time macro news scanner — feeds, sentiment signals, and market structure alerts.",
    blurb_cn: "实时宏观新闻扫描 · 情绪信号 + 市场结构告警",
    tags: ["Python", "News API", "Telegram", "Dashboard"],
    metric: { label: "sources", value: "12+" },
    color: "cyan",
    status: "LIVE",
    deployed: "2026.04.01",
    notes: "Aggregates macro news from 12+ sources including RSS feeds, CryptoPanic, and financial APIs. Classifies sentiment and filters for market-moving events. Pushes alerts to Telegram with signal strength scores. Live dashboard displays news timeline, sentiment trend, and key macro indicators. Zero AI cost — pure keyword + rule-based classification.",
    notes_cn: "聚合来自 12 个以上信源的宏观新闻，包括 RSS、CryptoPanic 及金融 API。自动分类情绪并过滤市场影响事件，通过 Telegram 推送附带信号强度评分的告警。实时仪表盘展示新闻时间轴、情绪趋势及关键宏观指标。零 AI 成本 — 纯关键词 + 规则分类。",
    install: "npx skills add okx/plugin-store --skill macro-intelligence",
  },
  {
    id: "msa",
    code: "SKL_006",
    name: "Market Structure Analyzer",
    name_cn: "市场结构分析",
    blurb: "Multi-timeframe market structure analysis — HH/LL detection, BOS, liquidity sweeps.",
    blurb_cn: "多周期市场结构分析 · 高低点检测 + 流动性清扫",
    tags: ["Python", "OKX API", "Multi-chain", "Dashboard"],
    metric: { label: "timeframes", value: "5" },
    color: "lime",
    status: "LIVE",
    deployed: "2026.04.10",
    notes: "Analyzes market structure across 5 timeframes (5m, 15m, 1H, 4H, 1D). Detects higher highs/lower lows, break of structure (BOS), and liquidity sweep patterns. Supports any token pair across multi-chain via OKX DEX API. Dashboard displays structure overlays, key levels, and alerts when significant BOS events are detected.",
    notes_cn: "跨 5 个时间周期（5m、15m、1H、4H、1D）分析市场结构。检测更高高点/更低低点、结构突破（BOS）及流动性清扫形态。通过 OKX DEX API 支持多链任意交易对。仪表盘展示结构叠加层、关键价位，并在重要 BOS 事件发生时发出告警。",
    install: "npx skills add okx/plugin-store --skill market-structure-analyzer",
  },
];

// ─────────────── Hooks ───────────────

// Window width hook for responsive layouts
function useWindowWidth() {
  const [w, setW] = useState(typeof window !== "undefined" ? window.innerWidth : 1200);
  useEffect(() => {
    const handler = () => setW(window.innerWidth);
    window.addEventListener("resize", handler);
    return () => window.removeEventListener("resize", handler);
  }, []);
  return w;
}

// Time hook — returns ms since mount, ticking every frame.
function useFrame() {
  const [t, setT] = useState(0);
  useEffect(() => {
    let raf, start = performance.now();
    const tick = (now) => { setT(now - start); raf = requestAnimationFrame(tick); };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);
  return t;
}

// Decrypt scramble — characters resolve into the target string left-to-right.
function useScramble(target, opts = {}) {
  const { speed = 60, chars = "!<>-_\\/[]{}—=+*^?#________ABCDEF0123456789" } = opts;
  const [out, setOut] = useState("");
  const targetRef = useRef(target);
  useEffect(() => { targetRef.current = target; }, [target]);

  useEffect(() => {
    let i = 0, raf, last = 0;
    const tick = (now) => {
      if (now - last > speed) {
        last = now;
        const t = targetRef.current;
        let s = "";
        for (let k = 0; k < t.length; k++) {
          if (k < i) s += t[k];
          else if (t[k] === " ") s += " ";
          else s += chars[Math.floor(Math.random() * chars.length)];
        }
        setOut(s);
        if (i < t.length) i += 0.5;
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, [target, speed]);
  return out;
}

// Typewriter — types `text` one char at a time, with optional looping/erase.
function useTypewriter(text, { speed = 60, startDelay = 0 } = {}) {
  const [out, setOut] = useState("");
  useEffect(() => {
    let i = 0, to;
    const start = setTimeout(() => {
      const tick = () => {
        if (i <= text.length) {
          setOut(text.slice(0, i));
          i++;
          to = setTimeout(tick, speed);
        }
      };
      tick();
    }, startDelay);
    return () => { clearTimeout(start); clearTimeout(to); };
  }, [text, speed, startDelay]);
  return out;
}

// Persistent ticker — visit count, latency etc.
function useLiveStat(seed = 1234, drift = 0.1) {
  const [v, setV] = useState(seed);
  useEffect(() => {
    const id = setInterval(() => setV((x) => Math.max(0, x + Math.round((Math.random() - 0.4) * drift * 100))), 1800);
    return () => clearInterval(id);
  }, [drift]);
  return v;
}

// ─────────────── Backgrounds (canvas) ───────────────
// Single shared canvas component — variation/pattern picks the renderer.

function FXCanvas({ pattern = "matrix", color = "#00ff88", intensity = 0.6, style }) {
  const ref = useRef(null);
  const wrapRef = useRef(null);
  const stateRef = useRef({});

  useEffect(() => {
    const cv = ref.current; if (!cv) return;
    const ctx = cv.getContext("2d");
    let raf, dpr = Math.min(2, window.devicePixelRatio || 1);

    const resize = () => {
      const r = wrapRef.current.getBoundingClientRect();
      cv.width = Math.max(1, Math.floor(r.width * dpr));
      cv.height = Math.max(1, Math.floor(r.height * dpr));
      cv.style.width = r.width + "px";
      cv.style.height = r.height + "px";
      stateRef.current = {}; // reset per-pattern state on resize
    };
    resize();
    const ro = new ResizeObserver(resize);
    ro.observe(wrapRef.current);

    const render = () => {
      const W = cv.width, H = cv.height;
      const st = stateRef.current;

      if (pattern === "matrix") {
        if (!st.cols) {
          const cell = 16 * dpr;
          st.cell = cell;
          st.cols = Math.ceil(W / cell);
          st.drops = Array.from({length: st.cols}, () => Math.random() * (H / cell));
          st.glyphs = "ｱｲｳｴｵｶｷｸｹｺｻｼｽｾｿﾀﾁﾂﾃﾄﾅﾆﾇﾈﾉﾊﾋﾌﾍﾎ0123456789#$@%&*+=<>".split("");
        }
        ctx.fillStyle = `rgba(0,0,0,${0.08 + 0.08 * intensity})`;
        ctx.fillRect(0, 0, W, H);
        ctx.font = `${Math.floor(st.cell * 0.95)}px Geist Mono, monospace`;
        ctx.textBaseline = "top";
        for (let i = 0; i < st.cols; i++) {
          const x = i * st.cell;
          const y = st.drops[i] * st.cell;
          ctx.fillStyle = color;
          ctx.globalAlpha = 0.85 * intensity;
          ctx.fillText(st.glyphs[(Math.random() * st.glyphs.length) | 0], x, y);
          ctx.globalAlpha = 0.25 * intensity;
          ctx.fillText(st.glyphs[(Math.random() * st.glyphs.length) | 0], x, y - st.cell * 2);
          if (y > H && Math.random() > 0.985) st.drops[i] = 0;
          st.drops[i] += 0.07 + Math.random() * 0.09;
        }
        ctx.globalAlpha = 1;
      }

      else if (pattern === "grid") {
        ctx.clearRect(0, 0, W, H);
        const cell = 32 * dpr;
        const t = (performance.now() / 1000);
        ctx.lineWidth = 1 * dpr;
        for (let x = 0; x < W; x += cell) {
          for (let y = 0; y < H; y += cell) {
            // pulse based on distance from center
            const dx = (x - W/2) / W, dy = (y - H/2) / H;
            const d = Math.sqrt(dx*dx + dy*dy);
            const pulse = 0.5 + 0.5 * Math.sin(t * 1.6 - d * 8);
            ctx.strokeStyle = color;
            ctx.globalAlpha = (0.06 + 0.18 * pulse) * intensity;
            ctx.strokeRect(x, y, cell, cell);
          }
        }
        ctx.globalAlpha = 1;
      }

      else if (pattern === "dots") {
        ctx.clearRect(0, 0, W, H);
        const cell = 24 * dpr;
        const t = performance.now() / 1000;
        for (let x = cell/2; x < W; x += cell) {
          for (let y = cell/2; y < H; y += cell) {
            const phase = (x * 0.02 + y * 0.018);
            const a = 0.1 + 0.5 * (0.5 + 0.5 * Math.sin(t * 2 + phase));
            ctx.fillStyle = color;
            ctx.globalAlpha = a * intensity;
            ctx.beginPath();
            ctx.arc(x, y, 1.2 * dpr, 0, Math.PI * 2);
            ctx.fill();
          }
        }
        ctx.globalAlpha = 1;
      }

      else if (pattern === "scanlines") {
        ctx.clearRect(0, 0, W, H);
        const t = performance.now() / 1000;
        for (let y = 0; y < H; y += 3 * dpr) {
          ctx.fillStyle = color;
          ctx.globalAlpha = 0.04 * intensity;
          ctx.fillRect(0, y, W, 1 * dpr);
        }
        // sweeping bright line
        const sweepY = ((t * 60 * dpr) % (H + 200 * dpr)) - 100 * dpr;
        const grad = ctx.createLinearGradient(0, sweepY - 80*dpr, 0, sweepY + 80*dpr);
        grad.addColorStop(0, "rgba(255,255,255,0)");
        grad.addColorStop(0.5, color);
        grad.addColorStop(1, "rgba(255,255,255,0)");
        ctx.fillStyle = grad;
        ctx.globalAlpha = 0.12 * intensity;
        ctx.fillRect(0, sweepY - 80*dpr, W, 160*dpr);
        ctx.globalAlpha = 1;
      }

      else if (pattern === "wireframe") {
        // Rotating wireframe torus / icosphere-ish
        ctx.fillStyle = "rgba(0,0,0,0.18)";
        ctx.fillRect(0, 0, W, H);
        const t = performance.now() / 1000;
        const cx = W/2, cy = H/2;
        const R = Math.min(W, H) * 0.32;
        ctx.strokeStyle = color;
        ctx.lineWidth = 1 * dpr;
        ctx.globalAlpha = 0.5 * intensity;
        // longitudes
        for (let i = 0; i < 24; i++) {
          ctx.beginPath();
          for (let j = 0; j <= 64; j++) {
            const u = (i / 24) * Math.PI * 2 + t * 0.3;
            const v = (j / 64) * Math.PI;
            const x3 = R * Math.sin(v) * Math.cos(u);
            const y3 = R * Math.cos(v);
            const z3 = R * Math.sin(v) * Math.sin(u);
            // rotate around X
            const rot = t * 0.4;
            const y2 = y3 * Math.cos(rot) - z3 * Math.sin(rot);
            const z2 = y3 * Math.sin(rot) + z3 * Math.cos(rot);
            const persp = 1.5 / (1.5 + z2 / R);
            const px = cx + x3 * persp;
            const py = cy + y2 * persp;
            if (j === 0) ctx.moveTo(px, py); else ctx.lineTo(px, py);
          }
          ctx.stroke();
        }
        ctx.globalAlpha = 1;
      }

      raf = requestAnimationFrame(render);
    };
    raf = requestAnimationFrame(render);
    return () => { cancelAnimationFrame(raf); ro.disconnect(); };
  }, [pattern, color, intensity]);

  return (
    <div ref={wrapRef} style={{position: "absolute", inset: 0, overflow: "hidden", ...style}}>
      <canvas ref={ref} style={{display: "block", width: "100%", height: "100%"}} />
    </div>
  );
}

// ─────────────── Pulsing primitives ───────────────

function PulseDot({ color = "#00ff88", size = 8, intensity = 1 }) {
  const t = useFrame();
  const scale = 1 + 0.4 * Math.sin(t / 600) * intensity;
  const glow = 4 + 8 * intensity * (0.5 + 0.5 * Math.sin(t / 400));
  return (
    <span style={{
      display: "inline-block",
      width: size, height: size,
      borderRadius: "50%",
      background: color,
      boxShadow: `0 0 ${glow}px ${color}`,
      transform: `scale(${scale})`,
      transition: "background .2s",
    }} />
  );
}

function BlinkCursor({ color = "#00ff88", w = 10, h = 18 }) {
  const t = useFrame();
  const on = Math.floor(t / 500) % 2 === 0;
  const size = h * 0.22;
  return <span style={{
    display: "inline-block", width: size, height: size,
    background: on ? color : "transparent",
    boxShadow: on ? `0 0 8px ${color}` : "none",
    verticalAlign: "baseline", marginLeft: 6,
  }} />;
}

// EQ bars — fake audio-reactive
function EQBars({ count = 24, color = "#00ff88", height = 36, intensity = 1 }) {
  const t = useFrame();
  const bars = [];
  for (let i = 0; i < count; i++) {
    const v = (Math.sin(t / 200 + i * 0.6) + Math.sin(t / 130 + i * 0.31) + 2) / 4;
    const h = (0.2 + 0.8 * v * intensity) * height;
    bars.push(<div key={i} style={{
      width: 3, height: h, background: color, borderRadius: 1,
      boxShadow: `0 0 6px ${color}`,
    }} />);
  }
  return <div style={{display: "flex", alignItems: "flex-end", gap: 2, height}}>{bars}</div>;
}

// Glitch text — randomly flickers chars
function GlitchText({ children, color = "#fff", intensity = 0.5, style }) {
  const [glitched, setGlitched] = useState(children);
  useEffect(() => {
    let id;
    const tick = () => {
      if (Math.random() < intensity * 0.3) {
        const arr = String(children).split("");
        for (let i = 0; i < Math.max(1, intensity * 3); i++) {
          const idx = Math.floor(Math.random() * arr.length);
          arr[idx] = "!@#$%&*<>".charAt(Math.floor(Math.random() * 9));
        }
        setGlitched(arr.join(""));
        setTimeout(() => setGlitched(children), 60 + Math.random() * 80);
      }
      id = setTimeout(tick, 200 + Math.random() * 800);
    };
    tick();
    return () => clearTimeout(id);
  }, [children, intensity]);
  return <span style={{color, ...style}}>{glitched}</span>;
}

// ─────────────── Export to global ───────────────
Object.assign(window, {
  useFrame, useWindowWidth, useScramble, useTypewriter, useLiveStat,
  FXCanvas, PulseDot, BlinkCursor, EQBars, GlitchText,
  HUB_NAME, HUB_NAME_CN, HUB_TAGLINE, HUB_TAGLINE_CN, SKILLS,
});
