"use client";

import * as React from "react";
import { cn } from "@/lib/utils";
import { useAudioAnalyser } from "@/hooks/use-audio-analyser";
import {
  adaptPalette,
  cyclicColor,
  isDarkTheme,
  readPalette,
  rgba,
} from "@/lib/visualizer-palette";

export type SpectrumVisualizerProps = {
  /** 是否激活：true 时采集麦克风并随音量律动，false 时显示静态呼吸态 */
  active?: boolean;
  /** 外部分析器；提供后组件不再自行申请麦克风 */
  analyser?: AnalyserNode | null;
  /** 外部音频流；提供后用它构建分析器，而不调用 getUserMedia */
  stream?: MediaStream | null;
  /** 画布直径（px） */
  size?: number;
  /** 环绕的频谱柱数量 */
  barCount?: number;
  /** 申请麦克风失败时回调 */
  onError?: (error: unknown) => void;
  className?: string;
};

/**
 * 径向频谱：左右镜像对称的一圈光柱，从基准环向内外双向伸展，
 * 低频在顶部、高频在底部。静态时沿圆周泛起呼吸涟漪，
 * 激活后随频谱起伏。与其他可视化共用同一套调色与柔光语言。
 */
export function SpectrumVisualizer({
  active = false,
  analyser = null,
  stream = null,
  size = 220,
  barCount = 96,
  onError,
  className,
}: SpectrumVisualizerProps) {
  const canvasRef = React.useRef<HTMLCanvasElement>(null);
  const smoothRef = React.useRef<Float32Array>(new Float32Array(barCount));
  const analyserRef = useAudioAnalyser({ active, analyser, stream, onError });

  React.useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) return;
    const ctx = canvas.getContext("2d");
    if (!ctx) return;

    const dpr = Math.min(window.devicePixelRatio || 1, 2);
    canvas.width = size * dpr;
    canvas.height = size * dpr;
    ctx.scale(dpr, dpr);

    if (smoothRef.current.length !== barCount) {
      smoothRef.current = new Float32Array(barCount);
    }
    const smooth = smoothRef.current;

    const cx = size / 2;
    const cy = size / 2;
    const baseR = size * 0.3;
    const outLen = size * 0.16;
    const inLen = size * 0.055;
    const half = Math.floor(barCount / 2);
    const freq = new Uint8Array(256);
    const TAU = Math.PI * 2;

    let raf = 0;
    let t = 0;
    let energy = 0;

    const draw = () => {
      t += 0.016;
      const dark = isDarkTheme();
      const palette = adaptPalette(readPalette(canvas), dark);
      const { sky, lav } = palette;

      const node = analyserRef.current;
      if (node) node.getByteFrequencyData(freq);

      const breathe = 0.5 + 0.5 * Math.sin(t * 1.1);

      let sum = 0;
      for (let i = 0; i < barCount; i++) {
        // 左右镜像：m 从顶部 0 到底部 1，再原路返回
        const m = (i < half ? i : barCount - i) / half;
        let target: number;
        if (node) {
          // 低频朝上、高频朝下，幂映射拉开低频细节
          const bin = Math.floor(Math.pow(m, 1.6) * freq.length * 0.7);
          target = Math.pow(freq[bin] / 255, 1.25);
        } else {
          // 静态呼吸 + 沿圆周缓慢游走的涟漪
          target =
            0.07 +
            0.05 * breathe +
            0.05 * (0.5 + 0.5 * Math.sin(m * 8.0 - t * 1.8));
        }
        const k = target > smooth[i] ? 0.35 : 0.12;
        smooth[i] += (target - smooth[i]) * k;
        sum += smooth[i];
      }
      energy += (sum / barCount - energy) * 0.1;

      ctx.clearRect(0, 0, size, size);

      // 中心柔光：与其他模式一致的呼吸光晕
      const glowR = baseR * (0.9 + energy * 0.5) + size * 0.03 * breathe;
      const glow = ctx.createRadialGradient(cx, cy, 0, cx, cy, glowR + outLen);
      glow.addColorStop(0, rgba(sky, (dark ? 0.3 : 0.2) + energy * 0.35));
      glow.addColorStop(0.55, rgba(lav, 0.1 + energy * 0.15));
      glow.addColorStop(1, rgba(lav, 0));
      ctx.fillStyle = glow;
      ctx.fillRect(0, 0, size, size);

      // 基准环：一条贯穿所有光柱的发丝细环
      ctx.beginPath();
      ctx.arc(cx, cy, baseR, 0, TAU);
      ctx.lineWidth = 1;
      ctx.strokeStyle = rgba(lav, dark ? 0.28 : 0.32);
      ctx.stroke();

      // 镜像光柱：从基准环向内外双向伸展，圆头、细、带柔光
      const rotation = -Math.PI / 2 + Math.sin(t * 0.11) * 0.06;
      ctx.lineCap = "round";
      ctx.lineWidth = Math.max(1.3, size * 0.007);
      for (let i = 0; i < barCount; i++) {
        const p = i / barCount;
        const angle = p * TAU + rotation;
        const v = smooth[i];
        const r0 = baseR - 1.5 - inLen * v;
        const r1 = baseR + 1.5 + outLen * (0.08 + 0.92 * v);
        const cos = Math.cos(angle);
        const sin = Math.sin(angle);
        const color = cyclicColor(palette, p + t * 0.015);
        ctx.strokeStyle = rgba(color, (dark ? 0.35 : 0.45) + v * 0.5);
        ctx.shadowColor = rgba(color, 0.6);
        ctx.shadowBlur = 4 + v * 16;
        ctx.beginPath();
        ctx.moveTo(cx + cos * r0, cy + sin * r0);
        ctx.lineTo(cx + cos * r1, cy + sin * r1);
        ctx.stroke();
      }
      ctx.shadowBlur = 0;

      raf = requestAnimationFrame(draw);
    };

    raf = requestAnimationFrame(draw);
    return () => cancelAnimationFrame(raf);
  }, [size, barCount, analyserRef]);

  return (
    <canvas
      ref={canvasRef}
      role="img"
      aria-label="麦克风音频可视化（频谱）"
      style={{ width: size, height: size }}
      className={cn("select-none", className)}
    />
  );
}
