/* ============================================================
   HOME — Luxsin X9 Controller (Main Dashboard)
   Design: Reference luxsin_x8_首页.png
   Layout (top to bottom):
     1. Device area
     2. Device status card (运行中/待机/充电 + 电量/模式/环保参数)
     3. Device render image (hardware photo)
     4. Volume slider with dB display
     5. 4 quick-action circle buttons (电源/HP-EQ/Effect/Bypass；HP-EQ/Effect 支持双击开关)
     6. iOS list rows (输入源/输出端口/音频设置/系统设置/VU表)
   ============================================================ */
import { useDevice } from "@/contexts/DeviceContext";
import ConnectionPlaceholder from "@/components/ConnectionPlaceholder";
import BottomNav from "@/components/BottomNav";
import {
  AlertDialog,
  AlertDialogAction,
  AlertDialogCancel,
  AlertDialogContent,
  AlertDialogDescription,
  AlertDialogFooter,
  AlertDialogHeader,
  AlertDialogTitle,
} from "@/components/ui/alert-dialog";
import { INPUT_LABELS, OUTPUT_LABELS } from "@/lib/luxsinApi";
import { cn } from "@/lib/utils";
import {
  ChevronRight,
  Power,
  Settings,
  Settings2,
  Sliders,
  Activity,
  Gauge,
  Undo2,
  ChevronDown,
  Wifi,
  Bluetooth,
} from "lucide-react";
import { useLocation } from "wouter";
import { useRef, useState, useEffect, useCallback, useMemo } from "react";
import { toast } from "sonner";
import localeZh from "@/locales/data-zh.json";
import localeZhHK from "@/locales/data-zh-HK.json";
import localeEn from "@/locales/data-en.json";

type HomeLocale = NonNullable<(typeof localeZh)["home"]>;

// ── Volume in dB (0-200 → -100dB to 0dB) ──
function volToDB(v: number) {
  if (v === 0) return "-100dB";
  const db = (v - 200) / 2;
  return `${db >= 0 ? "+" : ""}${db.toFixed(1)}dB`;
}

/** 耳机插孔彩蛋：大孔 → 上小孔 → 下小孔 */
const JACK_EGG_SEQUENCE = ["lg", "smTop", "smBot"] as const;
type JackId = (typeof JACK_EGG_SEQUENCE)[number];

const VU_BAR_COUNT = 20;
const EGG_DANCE_DURATION_MS = 5000;
const EGG_WAVE_DURATION_MS = 5000;
const EGG_WAVE_HALF_MS = EGG_WAVE_DURATION_MS / 2;
const EGG_BEAT_MS = 400;
const JACK_SEQUENCE_TIMEOUT_MS = 4000;

/** 旋律主音在 20 段 VU 上的位置（约 5s） */
const EGG_MELODY_LEAD = [
  0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1,
  0, 4, 8, 12, 16, 19, 16, 12, 8, 4, 0, 2, 5, 9, 13, 17, 19, 14, 10, 6, 2,
  0, 3, 7, 11, 15, 18, 15, 11, 7, 3,
];

function idleVuBarHeights(): number[] {
  return Array.from({ length: VU_BAR_COUNT }, (_, i) =>
    Math.max(0.12, Math.sin((i / VU_BAR_COUNT) * Math.PI) * 0.65 + 0.2),
  );
}

/** 彩蛋律动：主峰随节拍移动，每拍起落，全柱叠加波纹 */
function vuHeightsForDance(elapsedMs: number): number[] {
  const t = elapsedMs / 1000;
  const beatIdx = Math.floor(elapsedMs / EGG_BEAT_MS) % EGG_MELODY_LEAD.length;
  const lead = EGG_MELODY_LEAD[beatIdx];
  const beatPhase = (elapsedMs % EGG_BEAT_MS) / EGG_BEAT_MS;
  const kick = Math.sin(beatPhase * Math.PI);

  return Array.from({ length: VU_BAR_COUNT }, (_, i) => {
    const dist = Math.abs(i - lead);
    let peak = 0.06;
    if (dist === 0) peak = 1;
    else if (dist === 1) peak = 0.82;
    else if (dist === 2) peak = 0.62;
    else if (dist === 3) peak = 0.42;
    else if (dist === 4) peak = 0.26;

    const ripple = 0.35 * Math.max(0, Math.sin(t * 2.75 + i * 0.62));
    const sway = 0.1 * Math.sin(t * 1.4 + i * 0.35);
    const h = 0.05 + peak * kick + ripple * (0.55 + kick * 0.45) + sway;
    return Math.min(1, Math.max(0.05, h));
  });
}

/** 彩蛋律动二：海浪形波从左→右，再右→左 */
function vuHeightsForWave(phaseElapsedMs: number): number[] {
  const half = EGG_WAVE_HALF_MS;
  const ltr = phaseElapsedMs < half;
  const localMs = ltr ? phaseElapsedMs : phaseElapsedMs - half;
  const t = localMs / 1000;
  const direction = ltr ? 1 : -1;
  const speed = 0.75;

  return Array.from({ length: VU_BAR_COUNT }, (_, i) => {
    const phase = t * speed * Math.PI * 2 - direction * i * 0.72;
    const wave = (Math.sin(phase) + 1) / 2;
    const foam = 0.06 + 0.05 * Math.sin(i * 0.28 + t * 0.5);
    return Math.min(1, Math.max(0.06, foam + wave * 0.9));
  });
}

// ── iOS-style Toggle ──
function IOSToggle({ checked, onChange }: { checked: boolean; onChange: (v: boolean) => void }) {
  return (
    <label className="ios-toggle" onClick={(e) => e.stopPropagation()}>
      <input type="checkbox" checked={checked} onChange={(e) => onChange(e.target.checked)} />
      <span className="ios-toggle-track">
        <span className="ios-toggle-thumb" />
      </span>
    </label>
  );
}

// ── List row ──
function ListRow({
  icon, label, value, onClick, toggle, checked, onToggle,
}: {
  icon: React.ReactNode;
  label: string;
  value?: string;
  onClick?: () => void;
  toggle?: boolean;
  checked?: boolean;
  onToggle?: (v: boolean) => void;
}) {
  return (
    <div className="ios-list-row cursor-pointer" onClick={onClick}>
      <div className="w-8 h-8 rounded-[8px] flex items-center justify-center mr-3 flex-shrink-0"
        style={{ background: "rgba(0,255,246,0.1)", border: "1px solid rgba(0,255,246,0.15)" }}>
        <span className="text-[#00FFF6]">{icon}</span>
      </div>
      <div className="flex-1 min-w-0">
        <span className="text-[16px] text-white font-medium">{label}</span>
      </div>
      {toggle ? (
        <IOSToggle checked={!!checked} onChange={onToggle ?? (() => {})} />
      ) : (
        <div className="flex items-center gap-1">
          {value && <span className="ios-row-value">{value}</span>}
          <ChevronRight size={16} className="ios-chevron" />
        </div>
      )}
    </div>
  );
}

export default function Home() {
  const { isConnected, deviceState, setVolume, updateSetting, api, disconnect, isDemoMode, ip } = useDevice();
  const [, setLocation] = useLocation();
  const powerActionRef = useRef(false);
  const hpEqLastTapRef = useRef<number | null>(null);
  const hpEqNavigateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const effectLastTapRef = useRef<number | null>(null);
  const effectNavigateTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const [powerConfirmOpen, setPowerConfirmOpen] = useState(false);
  const [easterEggActive, setEasterEggActive] = useState(false);
  const [vuHeights, setVuHeights] = useState<number[]>(idleVuBarHeights);
  const jackProgressRef = useRef(0);
  const jackResetTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
  const eggRafRef = useRef<number | null>(null);
  /** 下次三连击成功时播放的律动：1=旋律，2=海浪（各需单独三连击触发） */
  const jackEggNextRef = useRef<1 | 2>(1);

  const homeText = useMemo((): HomeLocale => {
    if (!deviceState) return {} as HomeLocale;
    const loc =
      deviceState.language === 0 ? localeEn : deviceState.language === 1 ? localeZhHK : localeZh;
    return (loc.home ?? {}) as HomeLocale;
  }, [deviceState]);

  const [localVol, setLocalVol] = useState(deviceState?.volume ?? 100);
  const isDragging = useRef(false);
  const knobDraggingRef = useRef(false);
  const knobActivePointerIdRef = useRef<number | null>(null);
  const knobStartAngleRef = useRef(0);
  const knobStartVolRef = useRef(0);
  const knobLastVolRef = useRef<number>(localVol);

  useEffect(() => {
    if (!isDragging.current && deviceState?.volume !== undefined) {
      setLocalVol(deviceState.volume);
    }
  }, [deviceState?.volume]);

  useEffect(() => {
    knobLastVolRef.current = localVol;
  }, [localVol]);

  const handlePowerOff = useCallback(async () => {
    if (powerActionRef.current) return;
    powerActionRef.current = true;
    try {
      if (isDemoMode) {
        toast.info(homeText.powerOffToastDemo ?? "演示模式：已断开与设备的连接");
        disconnect();
        return;
      }
      if (api) {
        try {
          await api.powerOff();
        } catch {
          // 设备可能已掉线或未返回响应，仍断开本地会话
        }
      }
      disconnect();
      toast.success(homeText.powerOffToastSent ?? "已发送关机指令");
    } finally {
      powerActionRef.current = false;
    }
  }, [api, disconnect, isDemoMode, homeText]);

  /** HP-EQ 圆钮：单击在短延迟后切换 peqEnable（避免与双击抢）；280ms 内第二次点击则取消开关并进入 /eq */
  const HP_EQ_DOUBLE_CLICK_MS = 280;
  const handleHpEqCircleClick = useCallback(() => {
    const now = Date.now();
    const last = hpEqLastTapRef.current;
    if (last !== null && now - last < HP_EQ_DOUBLE_CLICK_MS) {
      hpEqLastTapRef.current = null;
      if (hpEqNavigateTimerRef.current) {
        clearTimeout(hpEqNavigateTimerRef.current);
        hpEqNavigateTimerRef.current = null;
      }
      setLocation("/eq");
      return;
    }
    hpEqLastTapRef.current = now;
    if (hpEqNavigateTimerRef.current) clearTimeout(hpEqNavigateTimerRef.current);
    hpEqNavigateTimerRef.current = setTimeout(() => {
      hpEqNavigateTimerRef.current = null;
      hpEqLastTapRef.current = null;
      const bypassOn = (deviceState?.dsp_enable ?? 0) === 0;
      if (bypassOn) {
        void updateSetting({ peqEnable: 1, dsp_enable: 1 });
      } else {
        const on = (deviceState?.peqEnable ?? 0) === 1;
        void updateSetting({ peqEnable: on ? 0 : 1 });
      }
    }, HP_EQ_DOUBLE_CLICK_MS);
  }, [deviceState?.peqEnable, deviceState?.dsp_enable, updateSetting, setLocation]);

  /** Effect 圆钮：单击短延迟后切换 audio_enable；280ms 内第二次点击则进入 /effects */
  const handleEffectCircleClick = useCallback(() => {
    const now = Date.now();
    const last = effectLastTapRef.current;
    if (last !== null && now - last < HP_EQ_DOUBLE_CLICK_MS) {
      effectLastTapRef.current = null;
      if (effectNavigateTimerRef.current) {
        clearTimeout(effectNavigateTimerRef.current);
        effectNavigateTimerRef.current = null;
      }
      setLocation("/effects");
      return;
    }
    effectLastTapRef.current = now;
    if (effectNavigateTimerRef.current) clearTimeout(effectNavigateTimerRef.current);
    effectNavigateTimerRef.current = setTimeout(() => {
      effectNavigateTimerRef.current = null;
      effectLastTapRef.current = null;
      const bypassOn = (deviceState?.dsp_enable ?? 0) === 0;
      if (bypassOn) {
        void updateSetting({ audio_enable: 1, dsp_enable: 1 });
      } else {
        const on = (deviceState?.audio_enable ?? 0) === 1;
        void updateSetting({ audio_enable: on ? 0 : 1 });
      }
    }, HP_EQ_DOUBLE_CLICK_MS);
  }, [deviceState?.audio_enable, deviceState?.dsp_enable, updateSetting, setLocation]);

  useEffect(() => {
    return () => {
      if (hpEqNavigateTimerRef.current) clearTimeout(hpEqNavigateTimerRef.current);
      if (effectNavigateTimerRef.current) clearTimeout(effectNavigateTimerRef.current);
    };
  }, []);

  const runJackEggAnimation = useCallback(
    (mode: "dance" | "wave") => {
      setEasterEggActive(true);
      setVuHeights(mode === "dance" ? vuHeightsForDance(0) : vuHeightsForWave(0));
      jackProgressRef.current = 0;
      const duration = mode === "dance" ? EGG_DANCE_DURATION_MS : EGG_WAVE_DURATION_MS;
      const start = performance.now();

      const tick = (now: number) => {
        const elapsed = now - start;
        if (elapsed >= duration) {
          setEasterEggActive(false);
          setVuHeights(idleVuBarHeights());
          eggRafRef.current = null;
          return;
        }
        setVuHeights(
          mode === "dance" ? vuHeightsForDance(elapsed) : vuHeightsForWave(elapsed),
        );
        eggRafRef.current = requestAnimationFrame(tick);
      };

      if (eggRafRef.current !== null) cancelAnimationFrame(eggRafRef.current);
      eggRafRef.current = requestAnimationFrame(tick);
    },
    [],
  );

  const onJackClick = useCallback(
    (jack: JackId) => {
      if (easterEggActive) return;

      const expected = JACK_EGG_SEQUENCE[jackProgressRef.current];
      if (jack === expected) {
        jackProgressRef.current += 1;
        if (jackProgressRef.current >= JACK_EGG_SEQUENCE.length) {
          if (jackEggNextRef.current === 1) {
            runJackEggAnimation("dance");
            jackEggNextRef.current = 2;
          } else {
            runJackEggAnimation("wave");
            jackEggNextRef.current = 1;
          }
        }
      } else {
        jackProgressRef.current = jack === JACK_EGG_SEQUENCE[0] ? 1 : 0;
      }

      if (jackResetTimerRef.current) clearTimeout(jackResetTimerRef.current);
      jackResetTimerRef.current = setTimeout(() => {
        jackProgressRef.current = 0;
      }, JACK_SEQUENCE_TIMEOUT_MS);
    },
    [easterEggActive, runJackEggAnimation],
  );

  useEffect(() => {
    return () => {
      if (jackResetTimerRef.current) clearTimeout(jackResetTimerRef.current);
      if (eggRafRef.current !== null) cancelAnimationFrame(eggRafRef.current);
    };
  }, []);

  if (!isConnected) return <ConnectionPlaceholder />;

  const ds = deviceState!;
  const inputLabel = INPUT_LABELS[ds.input] ?? "USB-C";
  const outputLabel = OUTPUT_LABELS[ds.output] ?? "Headset";
  const effectOn = ds.audio_enable === 1;
  const vuLabel = `${homeText.vu ?? "VU表"}${(ds.vu ?? 0) + 1}`;
  const bypassOn = (ds.dsp_enable ?? 0) === 0;

  // Home 的 volume 取值为 0..200，对应 dB = (v - 200) / 2
  // 因此 1 个 volume 单位 = 0.5dB
  // AudioPage 的 soundStep：0->0.5dB, 1->1dB, 2->2dB, 3->3dB
  // 换算为 volume 单位：0.5/1/2/3 dB -> 1/2/4/6
  const fineVolumeStep = [1, 2, 4, 6][ds.soundStep ?? 1] ?? 1;

  // Slider fill % (0-200 → 0-100%)
  const fillPct = (localVol / 200) * 100;
  // Knob angle (map 0..200 -> -135..+135 degrees)
  const knobAngle = -135 + (localVol / 200) * 270;

  const pointAngle = (clientX: number, clientY: number, rect: DOMRect) => {
    const cx = rect.left + rect.width / 2;
    const cy = rect.top + rect.height / 2;
    return Math.atan2(clientY - cy, clientX - cx);
  };

  const normalizeDelta = (delta: number) => {
    // Wrap to [-PI, PI] to avoid jumps across the -PI/PI boundary.
    const pi2 = Math.PI * 2;
    let d = ((delta + Math.PI) % pi2) - Math.PI;
    if (d < -Math.PI) d += pi2;
    return d;
  };

  const beginKnobDrag = (e: React.PointerEvent<HTMLDivElement>) => {
    const el = e.currentTarget;
    const rect = el.getBoundingClientRect();
    knobDraggingRef.current = true;
    knobActivePointerIdRef.current = e.pointerId;
    knobStartAngleRef.current = pointAngle(e.clientX, e.clientY, rect);
    knobStartVolRef.current = knobLastVolRef.current;
    isDragging.current = true;
    el.setPointerCapture(e.pointerId);
  };

  /** 拖动中只更新本地 UI，不调用 setVolume（避免来回旋时频繁打接口） */
  const moveKnobDrag = (e: React.PointerEvent<HTMLDivElement>) => {
    if (!knobDraggingRef.current || knobActivePointerIdRef.current !== e.pointerId) return;
    const el = e.currentTarget;
    const rect = el.getBoundingClientRect();
    const a = pointAngle(e.clientX, e.clientY, rect);
    const delta = normalizeDelta(a - knobStartAngleRef.current);
    // 270deg travel -> full scale; 1 rad maps to ~200/(1.5*pi) volume steps
    const stepsPerRad = 200 / (Math.PI * 1.5);
    const next = Math.max(0, Math.min(200, Math.round(knobStartVolRef.current + delta * stepsPerRad)));
    setLocalVol(next);
    knobLastVolRef.current = next;
  };

  const commitKnobDragEnd = (e: React.PointerEvent<HTMLDivElement>) => {
    if (!knobDraggingRef.current || knobActivePointerIdRef.current !== e.pointerId) return;
    knobDraggingRef.current = false;
    knobActivePointerIdRef.current = null;
    isDragging.current = false;
    setVolume(knobLastVolRef.current);
    try {
      e.currentTarget.releasePointerCapture(e.pointerId);
    } catch {
      // ignore
    }
  };

  const endKnobDrag = (e: React.PointerEvent<HTMLDivElement>) => {
    commitKnobDragEnd(e);
  };

  /** 异常丢失 capture 时仍提交一次，避免只松手未触发 pointerup 时漏提交 */
  const lostKnobPointerCapture = (e: React.PointerEvent<HTMLDivElement>) => {
    if (!knobDraggingRef.current || knobActivePointerIdRef.current !== e.pointerId) return;
    commitKnobDragEnd(e);
  };

  return (
    <div className="min-h-dvh bg-black pb-[calc(5.5rem+env(safe-area-inset-bottom,0px))]">
      {/* ── Device render image ── */}
      <div className="px-4 pb-4">
        <div className="w-full rounded-2xl overflow-hidden flex items-center justify-center"
          style={{ background: "rgba(18,18,20,0.8)", minHeight: 140 }}>
          {/* Stylized device illustration */}
          <div className="w-full relative" style={{ height: 140 }}>
            {/* Device body */}
            <div className="absolute inset-x-4 inset-y-3 rounded-xl flex items-center"
              style={{ background: "linear-gradient(135deg, #1a1a1c 0%, #2a2a2e 50%, #1a1a1c 100%)", border: "1px solid rgba(255,255,255,0.08)" }}>
              {/* Left ports */}
              <div className="px-4">
                <div className="flex items-center gap-3">
                  {/* Big hole (left) — 彩蛋第 1 步 */}
                  <button
                    type="button"
                    className="w-7 h-7 rounded-full border-2 bg-black/25 flex items-center justify-center cursor-pointer active:scale-95 transition-transform"
                    style={{
                      borderColor: "rgba(245, 158, 11, 0.75)",
                      boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.35)",
                    }}
                    aria-label="Headphone jack"
                    onClick={() => onJackClick("lg")}
                  >
                    <div
                      className="w-2 h-2 rounded-full pointer-events-none"
                      style={{ background: "rgba(196, 126, 9, 0.9)" }}
                    />
                  </button>
                  {/* Two small holes (right, stacked) */}
                  <div className="flex flex-col gap-2">
                    <button
                      type="button"
                      className="w-5 h-5 rounded-full border-2 bg-black/25 cursor-pointer active:scale-95 transition-transform"
                      style={{
                        borderColor: "rgba(245, 158, 11, 0.65)",
                        boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.35)",
                      }}
                      aria-label="Headphone jack"
                      onClick={() => onJackClick("smTop")}
                    />
                    <button
                      type="button"
                      className="w-5 h-5 rounded-full border-2 bg-black/25 cursor-pointer active:scale-95 transition-transform"
                      style={{
                        borderColor: "rgba(245, 158, 11, 0.65)",
                        boxShadow: "inset 0 0 0 1px rgba(0,0,0,0.35)",
                      }}
                      aria-label="Headphone jack"
                      onClick={() => onJackClick("smBot")}
                    />
                  </div>
                </div>
              </div>
              {/* VU display area */}
              <div className="flex-1 mx-3 rounded-lg overflow-hidden"
                style={{ background: "#0d1208", border: "1px solid rgba(255,255,255,0.1)", height: 90 }}>
                <div className="flex items-center justify-between px-2 pt-1">
                  <span className="text-[9px] text-white/50">{ds.audioFormat}</span>
                  <span className="text-[9px] text-white/50">{volToDB(localVol)}</span>
                </div>
                {/* Mini VU bars */}
                <div
                  className={cn(
                    "flex gap-0.5 px-2 pt-1 pb-2 h-16 items-end",
                    easterEggActive && "brightness-110",
                  )}
                >
                  {vuHeights.map((h, i) => {
                    const isHot = i > 16;
                    return (
                      <div
                        key={i}
                        className="flex-1 rounded-sm"
                        style={{
                          height: `${h * 100}%`,
                          background: isHot ? "#ef4444" : i > 13 ? "#f59e0b" : "#00FFF6",
                          opacity: easterEggActive ? 0.95 : 0.7,
                          transition: easterEggActive
                            ? "none"
                            : "height 200ms ease-out, opacity 200ms ease-out",
                        }}
                      />
                    );
                  })}
                </div>
              </div>
              {/* Right knob */}
              <div className="pr-4">
                <div
                  className="w-12 h-12 rounded-full flex items-center justify-center touch-none select-none"
                  style={{
                    background: "radial-gradient(circle at 35% 35%, #4a4a4e, #1a1a1c)",
                    border: "2px solid rgba(255,255,255,0.12)",
                    boxShadow: "inset 0 2px 4px rgba(0,0,0,0.5)",
                    transform: `rotate(${knobAngle}deg)`,
                    transition: knobDraggingRef.current ? "none" : "transform 120ms ease-out",
                  }}
                  role="slider"
                  aria-label="Volume"
                  aria-valuemin={0}
                  aria-valuemax={200}
                  aria-valuenow={localVol}
                  onPointerDown={beginKnobDrag}
                  onPointerMove={moveKnobDrag}
                  onPointerUp={endKnobDrag}
                  onPointerCancel={endKnobDrag}
                  onLostPointerCapture={lostKnobPointerCapture}
                >
                  <div className="w-1 h-4 rounded-full bg-white/40" style={{ transform: "rotate(-30deg)", transformOrigin: "bottom center" }} />
                </div>
              </div>
            </div>
            {/* Brand label */}
            <div className="absolute top-5 left-8">
              <span className="text-[11px] font-bold tracking-widest text-white/30">LUXSIN</span>
            </div>
          </div>
        </div>
      </div>

      {/* ── Volume slider ── */}
      <div className="px-4 mb-5">
        <div className="flex items-center gap-3">
          <button onClick={() => { setLocalVol(0); setVolume(0); }}
            className="text-white/50 active:text-white transition-colors flex-shrink-0">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-6 h-6">
              <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
              <line x1="23" y1="9" x2="17" y2="15"/><line x1="17" y1="9" x2="23" y2="15"/>
            </svg>
          </button>

          <div className="flex-1 relative flex items-center touch-pan-y">
            {/* Track fill - 根据按钮位置调整 */}
            <div className="absolute left-0 h-[4px] rounded-full pointer-events-none"
              style={{ 
                width: `${fillPct}%`, 
                background: "#00FFF6", 
                boxShadow: "0 0 8px rgba(0,255,246,0.5)" 
              }} />
            <input
              type="range" min={0} max={200} value={localVol}
              className="cyan-slider relative z-10"
              onChange={(e) => {
                const v = Number(e.target.value);
                setLocalVol(v);
              }}
              onPointerDown={() => {
                isDragging.current = true;
              }}
              onPointerUp={(e) => {
                isDragging.current = false;
                setVolume(Number(e.currentTarget.value));
              }}
              onPointerCancel={(e) => {
                isDragging.current = false;
                setVolume(Number(e.currentTarget.value));
              }}
              onBlur={(e) => {
                isDragging.current = false;
                setVolume(Number(e.currentTarget.value));
              }}
              onKeyUp={(e) => {
                const k = e.key;
                if (
                  k === "ArrowLeft" ||
                  k === "ArrowRight" ||
                  k === "ArrowUp" ||
                  k === "ArrowDown" ||
                  k === "Home" ||
                  k === "End" ||
                  k === "PageUp" ||
                  k === "PageDown"
                ) {
                  setVolume(Number(e.currentTarget.value));
                }
              }}
            />
          </div>

          <button onClick={() => { setLocalVol(200); setVolume(200); }}
            className="text-white/50 active:text-white transition-colors flex-shrink-0">
            <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-6 h-6">
              <polygon points="11 5 6 9 2 9 2 15 6 15 11 19 11 5"/>
              <path d="M15.54 8.46a5 5 0 0 1 0 7.07"/>
              <path d="M19.07 4.93a10 10 0 0 1 0 14.14"/>
            </svg>
          </button>
        </div>
        {/* dB value */}
        <div className="mt-[6px] flex items-center justify-center gap-3">
          <button
            type="button"
            aria-label="Decrease volume"
            disabled={localVol <= 0}
            className="w-10 h-10 rounded-full flex items-center justify-center transition-all active:scale-95 disabled:opacity-30 disabled:cursor-not-allowed"
            style={{
              background: "rgba(44,44,46,0.6)",
              border: "1px solid rgba(255,255,255,0.08)",
              color: "rgba(255,255,255,0.55)",
            }}
            onClick={() => {
              const next = Math.max(0, Math.min(200, localVol - fineVolumeStep));
              setLocalVol(next);
              setVolume(next);
            }}
          >
            <span className="text-[20px] leading-none">-</span>
          </button>

          <span className="text-[22px] font-bold tracking-tight" style={{ color: "#00FFF6" }}>
            {volToDB(localVol)}
          </span>

          <button
            type="button"
            aria-label="Increase volume"
            disabled={localVol >= 200}
            className="w-10 h-10 rounded-full flex items-center justify-center transition-all active:scale-95 disabled:opacity-30 disabled:cursor-not-allowed"
            style={{
              background: "rgba(44,44,46,0.6)",
              border: "1px solid rgba(255,255,255,0.08)",
              color: "rgba(255,255,255,0.55)",
            }}
            onClick={() => {
              const next = Math.max(0, Math.min(200, localVol + fineVolumeStep));
              setLocalVol(next);
              setVolume(next);
            }}
          >
            <span className="text-[20px] leading-none">+</span>
          </button>
        </div>

        {/* Bluetooth info */}
        {ds.bt_status !== 0 && (
          <div className="mt-3 px-3 py-3 text-center">
            <div className="flex items-center justify-center gap-1.5 mb-1">
              <Bluetooth size={14} className="text-[#00FFF6]" />
              <span className="text-[11px] text-white/50">{ds.bt_srcname}</span>
            </div>
            {ds.bt_title && (
              <p className="text-[13px] text-white font-medium truncate mb-2">
                {ds.bt_title}
                {ds.bt_artist && <span className="text-white/50"> - {ds.bt_artist}</span>}
              </p>
            )}
            {/* Playback controls */}
            <div className="flex items-center justify-center gap-4 mt-2">
              <button
                onClick={() => updateSetting({ bt_next: 0 })}
                className="w-10 h-10 rounded-full flex items-center justify-center transition-all active:scale-95"
                style={{ background: "rgba(0,255,246,0.15)", border: "1px solid rgba(0,255,246,0.3)" }}>
                <img src={`${import.meta.env.BASE_URL}player/skip-forward.png`} alt="上一首" className="w-5 h-5" />
              </button>
              <button
                onClick={() => updateSetting({ bt_play: 1 })}
                className="w-12 h-12 rounded-full flex items-center justify-center transition-all active:scale-95"
                style={{ background: "transparent", border: "1px solid rgba(0,255,246,0.5)", boxShadow: "0 0 12px rgba(0,255,246,0.4)" }}>
                <img 
                  src={ds.bt_status === 1
                    ? `${import.meta.env.BASE_URL}player/play.png`
                    : `${import.meta.env.BASE_URL}player/pause.png`} 
                  alt={ds.bt_status === 1 ? "播放" : "暂停"} 
                  className="w-6 h-6" 
                />
              </button>
              <button
                onClick={() => updateSetting({ bt_next: 1 })}
                className="w-10 h-10 rounded-full flex items-center justify-center transition-all active:scale-95"
                style={{ background: "rgba(0,255,246,0.15)", border: "1px solid rgba(0,255,246,0.3)" }}>
                <img src={`${import.meta.env.BASE_URL}player/skip-back.png`} alt="下一首" className="w-5 h-5" />
              </button>
            </div>
          </div>
        )}
      </div>

      {/* ── 4 quick-action circle buttons ── */}
      <div className="flex justify-around px-6 mb-6">
        {/* Power */}
        <div className="flex flex-col items-center gap-2">
          <button
            type="button"
            className="icon-circle-btn"
            onClick={() => setPowerConfirmOpen(true)}
          >
            <Power size={22} className="text-white/60" />
          </button>
          <span className="text-[11px] text-white/35">{homeText.power ?? "电源"}</span>
        </div>

        {/* EQ */}
        <div className="flex flex-col items-center gap-2">
          <button
            type="button"
            className={cn("icon-circle-btn", !bypassOn && ds.peqEnable === 1 && "active")}
            onClick={handleHpEqCircleClick}
          >
            <Sliders size={22} className={(!bypassOn && ds.peqEnable === 1) ? "text-[#00FFF6]" : "text-white/60"} />
          </button>
          <div className="flex flex-col items-center gap-0.5">
            <span className={cn("text-[11px]", (!bypassOn && ds.peqEnable === 1) ? "text-[#00FFF6]" : "text-white/35")}>HP-EQ</span>
            <span className="text-[9px] text-white/25 text-center leading-snug max-w-[76px]">
              {homeText.doubleClickHint ?? "Double-click to enter"}
            </span>
          </div>
        </div>

        {/* Effect */}
        <div className="flex flex-col items-center gap-2">
          <button
            type="button"
            className={cn("icon-circle-btn", !bypassOn && effectOn && "active")}
            onClick={handleEffectCircleClick}
          >
            <Activity size={22} className={(!bypassOn && effectOn) ? "text-[#00FFF6]" : "text-white/60"} />
          </button>
          <div className="flex flex-col items-center gap-0.5">
            <span className={cn("text-[11px]", (!bypassOn && effectOn) ? "text-[#00FFF6]" : "text-white/35")}>Effect</span>
            <span className="text-[9px] text-white/25 text-center leading-snug max-w-[76px]">
              {homeText.doubleClickHint ?? "Double-click to enter"}
            </span>
          </div>
        </div>

        {/* Bypass (DSP) */}
        <div className="flex flex-col items-center gap-2">
          <button
            className={cn("icon-circle-btn", bypassOn && "active")}
            onClick={() => updateSetting({ dsp_enable: bypassOn ? 1 : 0 })}
          >
            <img
              src={`${import.meta.env.BASE_URL}home/bypass.png`}
              alt="Bypass"
              className="w-[25px] h-[25px] object-contain"
              style={{ opacity: bypassOn ? 1 : 0.6, transform: "translateX(1.5px)" }}
            />
          </button>
          <span className={cn("text-[11px]", bypassOn ? "text-[#00FFF6]" : "text-white/35")}>Bypass</span>
        </div>
      </div>

      {/* ── iOS list rows ── */}
      <div className="px-4">
        <div className="ios-list-group">
          <ListRow
            icon={<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-4 h-4"><path d="M9 3H5a2 2 0 0 0-2 2v4m6-6h10a2 2 0 0 1 2 2v4M9 3v18m0 0h10a2 2 0 0 0 2-2V9M9 21H5a2 2 0 0 1-2-2V9m0 0h18"/></svg>}
            label={homeText.input ?? "输入源"}
            value={inputLabel}
            onClick={() => setLocation("/io")}
          />
          <ListRow
            icon={<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8" className="w-4 h-4"><path d="M15 3H9a2 2 0 0 0-2 2v4m8-6h4a2 2 0 0 1 2 2v4M15 3v18m0 0H9a2 2 0 0 1-2-2V9m8 12h4a2 2 0 0 0 2-2V9M7 9H3"/></svg>}
            label={homeText.output ?? "输出端口"}
            value={outputLabel}
            onClick={() => setLocation("/io")}
          />
          <ListRow
            icon={<Settings size={16} />}
            label={homeText.audioSet ?? "音频设置"}
            onClick={() => setLocation("/audio")}
          />
          <ListRow
            icon={<Settings2 size={16} />}
            label={homeText.systemSet ?? "系统设置"}
            onClick={() => setLocation("/system")}
          />
          <ListRow
            icon={<Gauge size={16} />}
            label={homeText.vu ?? "VU表"}
            value={vuLabel}
            onClick={() => setLocation("/vu")}
          />
          <ListRow
            icon={<Undo2 size={16} />}
            label={homeText.backToLegacy ?? "返回旧版"}
            onClick={() => {
              const target = ip.trim();
              if (!target) return;
              window.location.href = `https://am.luxsinaudio.com/x8/i.html?ip=${encodeURIComponent(target)}#/home`;
            }}
          />
        </div>
      </div>

      <AlertDialog open={powerConfirmOpen} onOpenChange={setPowerConfirmOpen}>
        <AlertDialogContent className="border-white/10 bg-zinc-900 text-white sm:max-w-md">
          <AlertDialogHeader>
            <AlertDialogTitle className="text-white">
              {homeText.powerOffConfirmTitle ?? "确认关闭电源？"}
            </AlertDialogTitle>
            <AlertDialogDescription className="text-white/60">
              {homeText.powerOffConfirmDesc ??
                "设备将关机并断开与本应用的连接，是否继续？"}
            </AlertDialogDescription>
          </AlertDialogHeader>
          <AlertDialogFooter>
            <AlertDialogCancel className="border-white/20 bg-transparent text-white hover:bg-white/10">
              {homeText.powerOffConfirmCancel ?? "取消"}
            </AlertDialogCancel>
            <AlertDialogAction
              className="bg-red-600 text-white hover:bg-red-700 focus-visible:ring-red-600"
              onClick={() => void handlePowerOff()}
            >
              {homeText.powerOffConfirmOk ?? "关闭电源"}
            </AlertDialogAction>
          </AlertDialogFooter>
        </AlertDialogContent>
      </AlertDialog>

      {/* Bottom nav */}
      <BottomNav />
    </div>
  );
}
