/* Cosmic Bagua compass — dark, glowing, with light-beam effects */

const COSMIC_DATA = {
  trigrams: [
    { sym: "☰", name: "乾" }, { sym: "☱", name: "兑" },
    { sym: "☲", name: "离" }, { sym: "☳", name: "震" },
    { sym: "☴", name: "巽" }, { sym: "☵", name: "坎" },
    { sym: "☶", name: "艮" }, { sym: "☷", name: "坤" },
  ],
  branches: ["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"],
  solarTerms: [
    "冬至","小寒","大寒","立春","雨水","惊蛰",
    "春分","清明","谷雨","立夏","小满","芒种",
    "夏至","小暑","大暑","立秋","处暑","白露",
    "秋分","寒露","霜降","立冬","小雪","大雪"
  ],
  mountains: [
    "壬","子","癸","丑","艮","寅","甲","卯","乙","辰","巽","巳",
    "丙","午","丁","未","坤","申","庚","酉","辛","戌","乾","亥"
  ],
  hexagrams: [
    "乾","坤","屯","蒙","需","讼","师","比",
    "小畜","履","泰","否","同人","大有","谦","豫",
    "随","蛊","临","观","噬嗑","贲","剥","复",
    "无妄","大畜","颐","大过","坎","离","咸","恒",
    "遁","大壮","晋","明夷","家人","睽","蹇","解",
    "损","益","夬","姤","萃","升","困","井",
    "革","鼎","震","艮","渐","归妹","丰","旅",
    "巽","兑","涣","节","中孚","小过","既济","未济"
  ],
  starSigils: [
    "角","亢","氐","房","心","尾","箕",
    "斗","牛","女","虚","危","室","壁",
    "奎","娄","胃","昴","毕","觜","参",
    "井","鬼","柳","星","张","翼","轸"
  ],
};

/* Cosmic Ring */
function CosmicRing({
  items, innerR, outerR, rotation, onRotate, fontSize,
  glowColor, textColor, divider, fillEven, fillOdd,
  zIndex = 1, renderItem, accentIndices = [],
}) {
  const ref = React.useRef(null);
  const drag = React.useRef(null);
  const n = items.length;
  const step = 360 / n;
  const cx = outerR, cy = outerR;

  const onPointerDown = (e) => {
    const rect = ref.current.getBoundingClientRect();
    const ox = rect.left + rect.width / 2;
    const oy = rect.top + rect.height / 2;
    const a0 = Math.atan2(e.clientY - oy, e.clientX - ox) * 180 / Math.PI;
    drag.current = { a0, r0: rotation, ox, oy };
    e.currentTarget.setPointerCapture(e.pointerId);
  };
  const onPointerMove = (e) => {
    if (!drag.current) return;
    const { a0, r0 } = drag.current;
    const a = Math.atan2(e.clientY - drag.current.oy, e.clientX - drag.current.ox) * 180 / Math.PI;
    onRotate(r0 + (a - a0));
  };
  const onPointerUp = (e) => {
    drag.current = null;
    try { e.currentTarget.releasePointerCapture(e.pointerId); } catch {}
  };

  const sectors = [];
  for (let i = 0; i < n; i++) {
    const a1 = (i * step - 90) * Math.PI / 180;
    const a2 = ((i + 1) * step - 90) * Math.PI / 180;
    const x1o = cx + outerR * Math.cos(a1), y1o = cy + outerR * Math.sin(a1);
    const x2o = cx + outerR * Math.cos(a2), y2o = cy + outerR * Math.sin(a2);
    const x1i = cx + innerR * Math.cos(a1), y1i = cy + innerR * Math.sin(a1);
    const x2i = cx + innerR * Math.cos(a2), y2i = cy + innerR * Math.sin(a2);
    const large = step > 180 ? 1 : 0;
    const d = `M ${x1o} ${y1o} A ${outerR} ${outerR} 0 ${large} 1 ${x2o} ${y2o} L ${x2i} ${y2i} A ${innerR} ${innerR} 0 ${large} 0 ${x1i} ${y1i} Z`;
    sectors.push(
      <path key={i} d={d}
        fill={i % 2 === 0 ? fillEven : fillOdd}
        stroke={divider} strokeWidth={0.6} />
    );
  }

  const midR = (innerR + outerR) / 2;
  const labels = items.map((it, i) => {
    const ang = i * step + step / 2 - 90;
    const rad = ang * Math.PI / 180;
    const x = cx + midR * Math.cos(rad);
    const y = cy + midR * Math.sin(rad);
    const rot = ang + 90;
    const isAccent = accentIndices.includes(i);
    const content = renderItem ? renderItem(it, i, isAccent) : it;
    return (
      <g key={i} transform={`translate(${x},${y}) rotate(${rot})`}>
        <g style={{
          fontSize,
          fill: isAccent ? "#ffd87a" : textColor,
          fontFamily: "var(--zh-serif)",
          fontWeight: 500,
          textAnchor: "middle",
          dominantBaseline: "middle",
          filter: `drop-shadow(0 0 4px ${isAccent ? "#ffce5c" : glowColor})`,
        }}>
          {typeof content === "string" ? <text>{content}</text> : content}
        </g>
      </g>
    );
  });

  return (
    <svg ref={ref}
      width={outerR * 2} height={outerR * 2}
      viewBox={`0 0 ${outerR * 2} ${outerR * 2}`}
      style={{
        position: "absolute", left: "50%", top: "50%",
        transform: `translate(-50%, -50%) rotate(${rotation}deg)`,
        cursor: "grab", zIndex, touchAction: "none",
      }}
      onPointerDown={onPointerDown}
      onPointerMove={onPointerMove}
      onPointerUp={onPointerUp}
      onPointerCancel={onPointerUp}
    >
      <circle cx={cx} cy={cy} r={outerR} fill="none" stroke={divider} strokeWidth="1.2"
        style={{ filter: `drop-shadow(0 0 6px ${glowColor})` }} />
      <circle cx={cx} cy={cy} r={innerR} fill="none" stroke={divider} strokeWidth="1.2" />
      {sectors}
      {labels}
    </svg>
  );
}

/* Glowing Taiji */
function CosmicTaiji({ size, rotation = 0, hue = "gold" }) {
  const colors = hue === "cyan"
    ? { light: "#9ff5ff", dark: "#0a1a26", glow: "#5ed8ff" }
    : { light: "#ffe9a8", dark: "#1a0e05", glow: "#ffce5c" };

  return (
    <svg width={size} height={size} viewBox="-100 -100 200 200"
      style={{
        position: "absolute", left: "50%", top: "50%",
        transform: `translate(-50%, -50%) rotate(${rotation}deg)`,
        zIndex: 60, pointerEvents: "none",
        filter: `drop-shadow(0 0 24px ${colors.glow}) drop-shadow(0 0 48px ${colors.glow})`,
      }}>
      <defs>
        <radialGradient id="taijiCenterGlow" cx="50%" cy="50%" r="50%">
          <stop offset="0%" stopColor={colors.glow} stopOpacity="0.6" />
          <stop offset="60%" stopColor={colors.glow} stopOpacity="0.1" />
          <stop offset="100%" stopColor={colors.glow} stopOpacity="0" />
        </radialGradient>
      </defs>
      <circle cx="0" cy="0" r="98" fill={colors.light} stroke={colors.glow} strokeWidth="1.5" />
      <path
        d="M 0,-95 A 95,95 0 0,1 0,95 A 47.5,47.5 0 0,1 0,0 A 47.5,47.5 0 0,0 0,-95 Z"
        fill={colors.dark}
      />
      <circle cx="0" cy="-47.5" r="14" fill={colors.dark} />
      <circle cx="0" cy="47.5" r="14" fill={colors.light} />
      <circle cx="0" cy="-47.5" r="4" fill={colors.light} />
      <circle cx="0" cy="47.5" r="4" fill={colors.dark} />
      <circle cx="0" cy="0" r="120" fill="url(#taijiCenterGlow)" />
    </svg>
  );
}

/* Vertical light beams rising and descending */
function LightBeams({ baseR, hue = "gold" }) {
  const beams = React.useMemo(() => {
    const arr = [];
    const N = 24;
    for (let i = 0; i < N; i++) {
      const ang = (i * 360 / N) * Math.PI / 180;
      const r = baseR + 20 + Math.random() * 30;
      arr.push({
        x: r * Math.cos(ang - Math.PI / 2),
        y: r * Math.sin(ang - Math.PI / 2),
        delay: Math.random() * 4,
        dur: 2.5 + Math.random() * 2.5,
        height: 80 + Math.random() * 220,
        width: 1.5 + Math.random() * 2.5,
      });
    }
    return arr;
  }, [baseR]);

  const beamColor = hue === "cyan" ? "#5ed8ff" : "#ffce5c";

  return (
    <div style={{
      position: "absolute", left: "50%", top: "50%",
      width: 0, height: 0, pointerEvents: "none", zIndex: 5,
    }}>
      {beams.map((b, i) => (
        <div key={i} style={{
          position: "absolute",
          left: b.x, top: b.y,
          width: b.width, height: b.height,
          background: `linear-gradient(to top, transparent 0%, ${beamColor}cc 40%, ${beamColor} 60%, transparent 100%)`,
          filter: `blur(0.5px) drop-shadow(0 0 8px ${beamColor})`,
          transform: "translate(-50%, -100%)",
          transformOrigin: "bottom center",
          animation: `beamRise ${b.dur}s ease-in-out ${b.delay}s infinite`,
          opacity: 0,
        }} />
      ))}
    </div>
  );
}

/* Constellation dots orbiting outside */
function OrbitingStars({ baseR, hue = "gold" }) {
  const dots = React.useMemo(() => {
    return Array.from({ length: 60 }, () => ({
      ang: Math.random() * 360,
      r: baseR + 60 + Math.random() * 200,
      size: 1 + Math.random() * 2.5,
      blink: Math.random() * 4,
      blinkDur: 2 + Math.random() * 4,
    }));
  }, [baseR]);
  const c = hue === "cyan" ? "#9ff5ff" : "#ffe9a8";
  return (
    <div style={{ position: "absolute", left: "50%", top: "50%", zIndex: 2, pointerEvents: "none" }}>
      {dots.map((d, i) => {
        const rad = d.ang * Math.PI / 180;
        return (
          <div key={i} style={{
            position: "absolute",
            left: d.r * Math.cos(rad),
            top: d.r * Math.sin(rad),
            width: d.size, height: d.size,
            borderRadius: "50%",
            background: c,
            boxShadow: `0 0 ${d.size * 4}px ${c}`,
            transform: "translate(-50%, -50%)",
            animation: `starBlink ${d.blinkDur}s ease-in-out ${d.blink}s infinite`,
          }} />
        );
      })}
    </div>
  );
}

/* ── Gear Sound (Web Audio API) ── */
function playGearSound(totalMs) {
  try {
    const actx = new (window.AudioContext || window.webkitAudioContext)();
    const dur = totalMs / 1000;
    const accelSec = 3;
    const now = actx.currentTime;

    // Pink noise buffer — mechanical rumble
    const noiseFrames = Math.min(Math.ceil(actx.sampleRate * dur), actx.sampleRate * 12);
    const noiseBuf = actx.createBuffer(1, noiseFrames, actx.sampleRate);
    const nd = noiseBuf.getChannelData(0);
    let b0=0,b1=0,b2=0,b3=0,b4=0,b5=0;
    for (let i = 0; i < noiseFrames; i++) {
      const w = Math.random()*2-1;
      b0=0.99886*b0+w*0.0555179; b1=0.99332*b1+w*0.0750759;
      b2=0.96900*b2+w*0.1538520; b3=0.86650*b3+w*0.3104856;
      b4=0.55000*b4+w*0.5329522; b5=-0.7616*b5-w*0.0168980;
      nd[i] = (b0+b1+b2+b3+b4+b5+w*0.5362)*0.05;
    }
    const noiseSrc = actx.createBufferSource();
    noiseSrc.buffer = noiseBuf;
    const rumbleF = actx.createBiquadFilter();
    rumbleF.type = 'bandpass'; rumbleF.Q.value = 1.5;
    rumbleF.frequency.setValueAtTime(80,  now);
    rumbleF.frequency.linearRampToValueAtTime(320, now + accelSec);
    rumbleF.frequency.linearRampToValueAtTime(50,  now + dur);
    const rumbleG = actx.createGain();
    rumbleG.gain.setValueAtTime(0, now);
    rumbleG.gain.linearRampToValueAtTime(0.55, now + accelSec);
    rumbleG.gain.linearRampToValueAtTime(0,    now + dur);
    noiseSrc.connect(rumbleF); rumbleF.connect(rumbleG); rumbleG.connect(actx.destination);
    noiseSrc.start(now); noiseSrc.stop(now + dur);

    // Metallic click buffer — gear teeth
    const clickFrames = Math.floor(actx.sampleRate * 0.02);
    const clickBuf = actx.createBuffer(1, clickFrames, actx.sampleRate);
    const cd = clickBuf.getChannelData(0);
    for (let i = 0; i < clickFrames; i++) {
      cd[i] = (Math.random()*2-1) * Math.exp(-i/(actx.sampleRate*0.003)) * 1.5;
    }
    let t = now + 0.05;
    const end = now + dur - 0.1;
    while (t < end) {
      const el = t - now;
      let ivl;
      if (el < accelSec) {
        const k = Math.pow(el / accelSec, 1.8);
        ivl = 0.30 - 0.26 * k;         // 0.30s → 0.04s
      } else {
        const k = Math.pow((el - accelSec) / (dur - accelSec), 1.5);
        ivl = 0.04 + 0.28 * k;         // 0.04s → 0.32s
      }
      ivl = Math.max(0.02, ivl);
      const env = el < accelSec ? el/accelSec : 1-(el-accelSec)/(dur-accelSec);
      const src = actx.createBufferSource();
      src.buffer = clickBuf;
      const g = actx.createGain();
      g.gain.value = 0.4 * Math.max(0.05, env);
      src.connect(g); g.connect(actx.destination);
      try { src.start(t); } catch {}
      t += ivl;
    }
    setTimeout(() => { try { actx.close(); } catch {} }, (dur + 1) * 1000);
  } catch(e) { /* audio unavailable */ }
}

/* The Cosmic Compass */
function CosmicCompass({ tweaks }) {
  const baseR = 360;
  const accentColors = tweaks.hue === "cyan"
    ? { glow: "#5ed8ff", text: "#cfeeff", divider: "rgba(94,216,255,0.55)", fillA: "rgba(20,60,90,0.55)", fillB: "rgba(8,28,48,0.65)" }
    : { glow: "#ffce5c", text: "#f1dba0", divider: "rgba(255,206,92,0.5)", fillA: "rgba(80,40,8,0.55)", fillB: "rgba(40,20,4,0.65)" };

  // 4 cardinal indices for mountains: 子 卯 午 酉 → indices 1, 7, 13, 19
  const cardinalsMountains = [1, 7, 13, 19];

  const ringSpec = [
    { key: "trigrams", inner: 80,  outer: 115, items: COSMIC_DATA.trigrams.map(t => t.name), font: 18 },
    { key: "branches", inner: 115, outer: 150, items: COSMIC_DATA.branches, font: 20 },
    { key: "terms",    inner: 150, outer: 190, items: COSMIC_DATA.solarTerms, font: 11,
      renderItem: (s) => (
        <text>
          <tspan x="0" dy="-0.5em">{s[0]}</tspan>
          <tspan x="0" dy="1.1em">{s[1]}</tspan>
        </text>
      )
    },
    { key: "mountains", inner: 190, outer: 232, items: COSMIC_DATA.mountains, font: 22, accentIndices: cardinalsMountains },
    { key: "hexagrams", inner: 232, outer: 295, items: COSMIC_DATA.hexagrams, font: 13,
      renderItem: (s) => s.length === 1 ? <text>{s}</text> : (
        <text>
          <tspan x="0" dy="-0.5em">{s[0]}</tspan>
          <tspan x="0" dy="1.1em">{s[1]}</tspan>
        </text>
      )
    },
    { key: "stars",    inner: 295, outer: baseR, items: [...COSMIC_DATA.starSigils, ...COSMIC_DATA.starSigils.slice(0, 36)].slice(0, 36), font: 16 },
  ];

  const [rotations, setRotations] = React.useState(
    Object.fromEntries(ringSpec.map(r => [r.key, 0]))
  );
  const burstRef     = React.useRef(null);
  const stoppedRef   = React.useRef(false);   // compass-stop 事件设为 true，burst 时清除
  // 用 ref 持有最新的 tweaks，避免 RAF 闭包捕获旧值
  const autoSpinRef  = React.useRef(tweaks.autoSpin);
  const spinSpeedRef = React.useRef(tweaks.spinSpeed);
  React.useEffect(() => { autoSpinRef.current  = tweaks.autoSpin;  }, [tweaks.autoSpin]);
  React.useEffect(() => { spinSpeedRef.current = tweaks.spinSpeed; }, [tweaks.spinSpeed]);

  React.useEffect(() => {
    const onBurst = (e) => {
      stoppedRef.current = false;             // 新一轮起卦，清除停止标记
      const dur  = (e.detail && e.detail.duration) || 30000;
      const maxM = (e.detail && e.detail.maxMult)  || 80;
      burstRef.current = { start: performance.now(), duration: dur, maxMult: maxM };
      playGearSound(dur);                     // 齿轮声音
    };
    const onStop = () => {
      stoppedRef.current = true;              // 起卦结束，标记停止
      burstRef.current   = null;
    };
    window.addEventListener('compass-burst', onBurst);
    window.addEventListener('compass-stop',  onStop);
    return () => {
      window.removeEventListener('compass-burst', onBurst);
      window.removeEventListener('compass-stop',  onStop);
    };
  }, []);

  React.useEffect(() => {
    let raf, last = performance.now();
    const tick = (t) => {
      const dt = Math.min(0.05, (t - last) / 1000);
      last = t;
      const burst = burstRef.current;
      let burstMult = 1;
      if (burst) {
        const elapsed = t - burst.start;
        if (elapsed < burst.duration) {
          const ACCEL = 3000; // 前 3s 加速
          const maxM  = burst.maxMult || 80;
          if (elapsed < ACCEL) {
            // 加速阶段：ease-in，从 1x → maxMult
            const k = elapsed / ACCEL;
            burstMult = 1 + maxM * Math.pow(k, 2);
          } else {
            // 减速阶段：ease-out，从 maxMult → 1
            const k = (elapsed - ACCEL) / (burst.duration - ACCEL);
            burstMult = 1 + maxM * Math.pow(1 - k, 3.2);
          }
        } else {
          burstRef.current = null;
        }
      }
      // stopped 优先级最高；其次 burst；最后 autoSpin
      const active = !stoppedRef.current && (burstRef.current || autoSpinRef.current);
      if (active) {
        setRotations(prev => {
          const next = { ...prev };
          ringSpec.forEach((r, i) => {
            const dir   = i % 2 === 0 ? 1 : -1;
            const base  = (spinSpeedRef.current || 6) * (1 - i * 0.08);
            const speed = base * burstMult;
            next[r.key] = (prev[r.key] || 0) + dir * speed * dt;
          });
          return next;
        });
      }
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []); // 依赖为空——通过 ref 读取最新值，不需要重建闭包

  // Pulsing aura
  const [pulse, setPulse] = React.useState(0);
  React.useEffect(() => {
    let raf;
    const tick = () => {
      setPulse(performance.now() / 1000);
      raf = requestAnimationFrame(tick);
    };
    raf = requestAnimationFrame(tick);
    return () => cancelAnimationFrame(raf);
  }, []);
  const pulseScale = 1 + Math.sin(pulse * 1.4) * 0.04;
  const pulseOpacity = 0.45 + Math.sin(pulse * 1.4) * 0.2;

  return (
    <div style={{
      position: "relative",
      width: baseR * 2 + 200,
      height: baseR * 2 + 200,
      margin: "0 auto",
      display: "flex", alignItems: "center", justifyContent: "center",
    }}>
      {/* Pulsing aura behind everything */}
      <div style={{
        position: "absolute",
        width: baseR * 2.4, height: baseR * 2.4,
        borderRadius: "50%",
        background: `radial-gradient(circle, ${accentColors.glow}33 0%, ${accentColors.glow}11 30%, transparent 60%)`,
        transform: `scale(${pulseScale})`,
        opacity: pulseOpacity,
        zIndex: 1,
      }} />

      {/* Orbiting starfield */}
      {tweaks.showStars && <OrbitingStars baseR={baseR} hue={tweaks.hue} />}

      {/* Light beams rising/descending */}
      {tweaks.showBeams && <LightBeams baseR={baseR} hue={tweaks.hue} />}

      <div style={{ position: "relative", width: baseR * 2, height: baseR * 2 }}>
        {/* Outer glowing rings */}
        <div style={{
          position: "absolute", inset: -24,
          borderRadius: "50%",
          border: `1px solid ${accentColors.divider}`,
          boxShadow: `0 0 40px ${accentColors.glow}55, inset 0 0 40px ${accentColors.glow}33`,
        }} />
        <div style={{
          position: "absolute", inset: -8,
          borderRadius: "50%",
          border: `1.5px solid ${accentColors.glow}`,
          boxShadow: `0 0 20px ${accentColors.glow}aa`,
        }} />
        {/* Tick marks */}
        <svg width={baseR * 2} height={baseR * 2} style={{ position: "absolute", inset: 0, pointerEvents: "none", zIndex: 4 }}>
          {Array.from({ length: 72 }).map((_, i) => {
            const ang = (i * 5 - 90) * Math.PI / 180;
            const r1 = baseR - 4;
            const r2 = baseR + 12;
            const major = i % 9 === 0;
            return (
              <line key={i}
                x1={baseR + r1 * Math.cos(ang)} y1={baseR + r1 * Math.sin(ang)}
                x2={baseR + (major ? r2 + 8 : r2) * Math.cos(ang)} y2={baseR + (major ? r2 + 8 : r2) * Math.sin(ang)}
                stroke={accentColors.glow}
                strokeWidth={major ? 1.5 : 0.6}
                opacity={major ? 0.9 : 0.4}
              />
            );
          })}
        </svg>

        {ringSpec.map((r, i) => (
          <CosmicRing
            key={r.key}
            items={r.items}
            innerR={r.inner} outerR={r.outer}
            rotation={rotations[r.key]}
            onRotate={(v) => setRotations(p => ({ ...p, [r.key]: v }))}
            fontSize={r.font}
            glowColor={accentColors.glow}
            textColor={accentColors.text}
            divider={accentColors.divider}
            fillEven={accentColors.fillA}
            fillOdd={accentColors.fillB}
            renderItem={r.renderItem}
            accentIndices={r.accentIndices || []}
            zIndex={10 + (ringSpec.length - i)}
          />
        ))}

        {/* Cardinal cross */}
        <svg width={baseR * 2} height={baseR * 2}
          style={{ position: "absolute", inset: 0, pointerEvents: "none", zIndex: 30, opacity: tweaks.showCross ? 0.6 : 0 }}>
          <defs>
            <linearGradient id="crossGrad" x1="0" y1="0" x2="0" y2="1">
              <stop offset="0%" stopColor={accentColors.glow} stopOpacity="0" />
              <stop offset="50%" stopColor={accentColors.glow} stopOpacity="0.9" />
              <stop offset="100%" stopColor={accentColors.glow} stopOpacity="0" />
            </linearGradient>
          </defs>
          <line x1={baseR} y1={0} x2={baseR} y2={baseR * 2} stroke="url(#crossGrad)" strokeWidth="1.5" />
          <line x1={0} y1={baseR} x2={baseR * 2} y2={baseR} stroke="url(#crossGrad)" strokeWidth="1.5" transform={`rotate(90 ${baseR} ${baseR})`} />
        </svg>

        <CosmicTaiji size={120} rotation={tweaks.spinTaiji ? rotations.trigrams * 0.3 : 0} hue={tweaks.hue} />
      </div>
    </div>
  );
}

window.CosmicCompass = CosmicCompass;
