import { useEffect, useRef, useState, useCallback } from "react"
import { useParams, useNavigate } from "react-router-dom"
import {
  Loader2,
  ChevronLeft,
  RefreshCw,
  Power,
  Clipboard,
  Maximize2,
  Minimize2,
  AlertTriangle,
  MonitorPlay,
  Wifi,
  WifiOff,
  Keyboard,
  Settings as SettingsIcon,
  X,
} from "lucide-react"
import { issueVncTicket, type VncTicket } from "../api/servers"
import { toast } from "../utils/toast"

// noVNC 在浏览器环境运行；用动态 import 避免 SSR 风险与首屏体积。
// 我们只用到三五个方法和事件，类型用 any 包装。
interface RFBLike {
  disconnect: () => void
  sendCtrlAltDel: () => void
  focus: () => void
  clipboardPasteFrom: (text: string) => void
  scaleViewport: boolean
  resizeSession: boolean
  viewOnly: boolean
  qualityLevel: number
  compressionLevel: number
  showDotCursor: boolean
  addEventListener: (event: string, cb: (e: CustomEvent) => void) => void
}

type ConnState = "init" | "connecting" | "connected" | "disconnected" | "error"

const STATE_META: Record<ConnState, { label: string; cls: string; dot: string }> = {
  init:         { label: "准备",   cls: "text-slate-400 border-slate-700 bg-slate-800/60", dot: "bg-slate-500" },
  connecting:   { label: "连接中", cls: "text-sky-300 border-sky-500/40 bg-sky-500/10",   dot: "bg-sky-400 animate-pulse" },
  connected:    { label: "在线",   cls: "text-emerald-300 border-emerald-500/40 bg-emerald-500/10", dot: "bg-emerald-400" },
  disconnected: { label: "已断开", cls: "text-slate-400 border-slate-700 bg-slate-800/60", dot: "bg-slate-500" },
  error:        { label: "异常",   cls: "text-red-300 border-red-500/40 bg-red-500/10",    dot: "bg-red-400" },
}

export default function VncConsole() {
  const { id } = useParams<{ id: string }>()
  const navigate = useNavigate()
  const containerRef = useRef<HTMLDivElement | null>(null)
  const rfbRef = useRef<RFBLike | null>(null)
  const [state, setState] = useState<ConnState>("init")
  const [errMsg, setErrMsg] = useState<string | null>(null)
  const [meta, setMeta] = useState<VncTicket | null>(null)
  const [fullscreen, setFullscreen] = useState(false)
  const [settingsOpen, setSettingsOpen] = useState(false)
  // 用户偏好
  const [scaleMode, setScaleMode] = useState<"fit" | "native">("fit")
  const [pixelated, setPixelated] = useState(true)
  const [showDotCursor, setShowDotCursor] = useState(false)
  // 画质预设：smooth=流畅优先（低延迟、高压缩、低画质），sharp=画质优先（高画质、低压缩）
  const [perfMode, setPerfMode] = useState<"smooth" | "sharp">("smooth")
  const [latencyMs, setLatencyMs] = useState<number | null>(null)
  const connectStartRef = useRef<number>(0)

  const applyPerfMode = useCallback((rfb: RFBLike, mode: "smooth" | "sharp") => {
    if (mode === "smooth") {
      // 优先低延迟：低画质 + 高压缩 → 帧小、编码快、网络快
      rfb.qualityLevel = 3
      rfb.compressionLevel = 6
    } else {
      // 优先画质：高画质 + 低压缩 → 帧大但解码快、清晰
      rfb.qualityLevel = 9
      rfb.compressionLevel = 0
    }
  }, [])

  const applyScale = useCallback((rfb: RFBLike, mode: "fit" | "native") => {
    if (mode === "fit") {
      rfb.scaleViewport = false
      rfb.resizeSession = true
    } else {
      rfb.scaleViewport = false
      rfb.resizeSession = false
    }
  }, [])

  const connect = useCallback(async () => {
    if (!id) return
    setState("connecting")
    setErrMsg(null)
    setLatencyMs(null)
    // 释放旧连接
    if (rfbRef.current) {
      try { rfbRef.current.disconnect() } catch { /* noop */ }
      rfbRef.current = null
    }
    if (containerRef.current) containerRef.current.innerHTML = ""

    let ticketResp: VncTicket
    try {
      const res = await issueVncTicket(Number(id))
      const data = res.data.data
      if (!data) throw new Error("no ticket")
      ticketResp = data
      setMeta(data)
    } catch (e: unknown) {
      const detail = (e as { response?: { data?: { detail?: string } } })?.response?.data?.detail
      setErrMsg(detail ?? "获取 VNC 票据失败")
      setState("error")
      return
    }

    if (ticketResp.direct_url) {
      window.location.replace(ticketResp.direct_url)
      return
    }
    if (!ticketResp.ticket) {
      setErrMsg("VNC 票据为空")
      setState("error")
      return
    }

    const RFB = (await import("@novnc/novnc")).default
    // 优先用 VITE_VNC_WS_BASE（直连后端，绕过 vite proxy 一跳）；否则同站走 nginx/vite
    const envBase = (import.meta.env.VITE_VNC_WS_BASE as string | undefined)?.trim()
    const base = envBase
      ? envBase.replace(/\/+$/, "")
      : `${window.location.protocol === "https:" ? "wss" : "ws"}://${window.location.host}`
    const wsUrl = `${base}/api/v1/vnc/${encodeURIComponent(ticketResp.ticket)}`

    try {
      connectStartRef.current = performance.now()
      const rfb = new RFB(containerRef.current!, wsUrl, {
        credentials: { password: ticketResp.vnc_pass ?? "" },
        wsProtocols: ["binary"],
      }) as unknown as RFBLike
      // 性能与体验默认值
      applyScale(rfb, scaleMode)
      rfb.viewOnly = false
      applyPerfMode(rfb, perfMode)
      rfb.showDotCursor = showDotCursor
      rfb.addEventListener("connect", () => {
        setState("connected")
        setLatencyMs(Math.round(performance.now() - connectStartRef.current))
        try { rfb.focus() } catch { /* noop */ }
      })
      rfb.addEventListener("disconnect", (e: CustomEvent) => {
        const detail = e.detail as { clean?: boolean; reason?: string } | undefined
        console.warn("[VNC] disconnect", detail)
        const clean = detail?.clean
        setState(clean ? "disconnected" : "error")
        if (!clean) {
          setErrMsg(detail?.reason ? `连接已中断：${detail.reason}` : "连接已中断")
        }
      })
      rfb.addEventListener("credentialsrequired", () => {
        console.warn("[VNC] credentialsrequired")
        setErrMsg("VNC 凭据被拒绝")
        setState("error")
      })
      rfb.addEventListener("securityfailure", (e: CustomEvent) => {
        const detail = e.detail as { reason?: string; status?: number } | undefined
        console.warn("[VNC] securityfailure", detail)
        setErrMsg(detail?.reason ? `安全验证失败：${detail.reason}` : "安全验证失败")
        setState("error")
      })
      rfbRef.current = rfb
    } catch (e) {
      console.error("VNC init failed", e)
      setErrMsg("VNC 初始化失败")
      setState("error")
    }
  }, [id, scaleMode, showDotCursor, perfMode, applyScale, applyPerfMode])

  // 切换缩放模式时实时应用
  useEffect(() => {
    if (rfbRef.current && state === "connected") {
      applyScale(rfbRef.current, scaleMode)
    }
  }, [scaleMode, state, applyScale])

  useEffect(() => {
    if (rfbRef.current && state === "connected") {
      rfbRef.current.showDotCursor = showDotCursor
    }
  }, [showDotCursor, state])

  // 切画质预设时实时应用
  useEffect(() => {
    if (rfbRef.current && state === "connected") {
      applyPerfMode(rfbRef.current, perfMode)
    }
  }, [perfMode, state, applyPerfMode])

  // 给 canvas 强制关闭浏览器抗锯齿。
  // - canvas 上 imageRendering: pixelated 控制 CSS 层缩放算法（拉伸时不平滑）
  // - ctx.imageSmoothingEnabled = false 控制 canvas 内部绘制时的插值（位图复制时不平滑）
  // - canvas 不是固定 DOM，noVNC 连上后才创建，所以用 MutationObserver 等它
  useEffect(() => {
    const root = containerRef.current
    if (!root) return

    const apply = (canvas: HTMLCanvasElement) => {
      canvas.style.imageRendering = pixelated ? "pixelated" : "auto"
      try {
        const ctx = canvas.getContext("2d")
        if (ctx) {
          ctx.imageSmoothingEnabled = !pixelated
        }
      } catch { /* noop */ }
    }

    const existing = root.querySelector("canvas")
    if (existing) apply(existing as HTMLCanvasElement)

    const mo = new MutationObserver(() => {
      const c = root.querySelector("canvas")
      if (c) apply(c as HTMLCanvasElement)
    })
    mo.observe(root, { childList: true, subtree: true })
    return () => mo.disconnect()
  }, [pixelated])

  // React Strict Mode 在 dev 下会 mount → unmount → mount 一遍。
  // VNC ticket 一次性 + 上游对同 token 二次连接敏感，必须按 id 防抖：
  // 同一个实例 id 在本组件生命周期内只允许真正发起一次连接。
  const connectedIdRef = useRef<string | null>(null)
  useEffect(() => {
    if (!id) return
    if (connectedIdRef.current === id) return
    connectedIdRef.current = id
    void connect()
    return () => {
      if (rfbRef.current) {
        try { rfbRef.current.disconnect() } catch { /* noop */ }
        rfbRef.current = null
      }
      // 注意：不在这里清 connectedIdRef，避免 StrictMode 第二次 mount 时重连
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [id])

  const toggleFullscreen = async () => {
    const el = document.documentElement
    if (!document.fullscreenElement) {
      try { await el.requestFullscreen(); setFullscreen(true) }
      catch { toast.error("浏览器拒绝了全屏请求") }
    } else {
      try { await document.exitFullscreen(); setFullscreen(false) }
      catch { /* noop */ }
    }
  }
  useEffect(() => {
    const onFs = () => setFullscreen(!!document.fullscreenElement)
    document.addEventListener("fullscreenchange", onFs)
    return () => document.removeEventListener("fullscreenchange", onFs)
  }, [])

  const handleCtrlAltDel = () => rfbRef.current?.sendCtrlAltDel()

  const handlePasteClipboard = async () => {
    try {
      const text = await navigator.clipboard.readText()
      if (text && rfbRef.current) {
        rfbRef.current.clipboardPasteFrom(text)
        toast.success("已粘贴剪贴板")
      }
    } catch {
      toast.error("浏览器拒绝读取剪贴板")
    }
  }

  // "刷新画面" —— noVNC 没有公开 redraw API，但断开重连最有效
  const handleRefreshFrame = () => {
    void connect()
  }

  const stateMeta = STATE_META[state]
  const title = meta?.hostname || `实例 #${id}`

  return (
    <div className="flex h-screen flex-col bg-[#0b0f17] text-slate-100 select-none overflow-hidden">
      {/* ─── 顶栏 ──────────────────────────────────────────────── */}
      <div className="flex h-11 shrink-0 items-center justify-between gap-3 border-b border-slate-800/80 bg-[#0e1422]/95 backdrop-blur px-3">
        {/* 左：返回 + 标题 + 状态 */}
        <div className="flex items-center gap-2 min-w-0">
          <button
            onClick={() => navigate(`/control/server/${id}`)}
            title="返回详情"
            className="inline-flex h-7 w-7 items-center justify-center rounded text-slate-400 hover:bg-slate-800 hover:text-white transition-colors"
          >
            <ChevronLeft className="w-4 h-4" />
          </button>
          <div className="h-4 w-px bg-slate-800" />
          <MonitorPlay className="w-4 h-4 text-purple-400 shrink-0" />
          <span className="text-sm font-medium text-slate-200 truncate max-w-[280px]">{title}</span>
          <span className={`inline-flex items-center gap-1.5 rounded-full border px-2 py-0.5 text-[10px] font-semibold tracking-wide uppercase ${stateMeta.cls}`}>
            <span className={`w-1.5 h-1.5 rounded-full ${stateMeta.dot}`} />
            {stateMeta.label}
          </span>
          {state === "connected" && latencyMs != null && (
            <span className="inline-flex items-center gap-1 text-[10px] text-slate-500 font-mono ml-1">
              <Wifi className="w-3 h-3 text-emerald-400" />
              {latencyMs}ms
            </span>
          )}
          {state === "error" && (
            <span className="inline-flex items-center gap-1 text-[10px] text-red-400 font-mono ml-1">
              <WifiOff className="w-3 h-3" />
              链路异常
            </span>
          )}
        </div>

        {/* 右：操作 */}
        <div className="flex items-center gap-0.5">
          <ToolBtn
            icon={<Keyboard className="w-3.5 h-3.5" />}
            label="Ctrl+Alt+Del"
            hint="发送特殊键"
            onClick={handleCtrlAltDel}
            disabled={state !== "connected"}
          />
          <ToolBtn
            icon={<Clipboard className="w-3.5 h-3.5" />}
            label="粘贴"
            hint="把本机剪贴板贴入虚拟机"
            onClick={handlePasteClipboard}
            disabled={state !== "connected"}
          />
          <ToolBtn
            icon={<RefreshCw className="w-3.5 h-3.5" />}
            label="刷新画面"
            hint="重连并请求全屏帧（卷帘 / 黑屏时用）"
            onClick={handleRefreshFrame}
            disabled={state === "connecting"}
          />
          <div className="mx-0.5 h-4 w-px bg-slate-800" />
          <ToolBtn
            icon={<SettingsIcon className="w-3.5 h-3.5" />}
            label="设置"
            hint="缩放 / 渲染选项"
            onClick={() => setSettingsOpen((v) => !v)}
            active={settingsOpen}
          />
          <ToolBtn
            icon={fullscreen ? <Minimize2 className="w-3.5 h-3.5" /> : <Maximize2 className="w-3.5 h-3.5" />}
            label={fullscreen ? "退出全屏" : "全屏"}
            hint="F11 也可"
            onClick={toggleFullscreen}
          />
          <button
            onClick={() => {
              if (rfbRef.current) try { rfbRef.current.disconnect() } catch { /* noop */ }
              window.close()
            }}
            title="断开并关闭"
            className="ml-1 inline-flex h-7 items-center gap-1 rounded border border-red-500/40 bg-red-500/10 px-2 text-xs font-medium text-red-300 hover:bg-red-500/20 transition-colors"
          >
            <Power className="w-3 h-3" />
            断开
          </button>
        </div>
      </div>

      {/* ─── 设置抽屉 ──────────────────────────────────────────── */}
      {settingsOpen && (
        <div className="absolute right-2 top-12 z-30 w-72 rounded-xl border border-slate-800 bg-[#0e1422]/98 backdrop-blur shadow-2xl shadow-black/40">
          <div className="px-4 py-3 flex items-center justify-between border-b border-slate-800/80">
            <span className="text-xs font-semibold tracking-wider text-slate-300 uppercase">显示选项</span>
            <button
              onClick={() => setSettingsOpen(false)}
              className="text-slate-500 hover:text-slate-200 transition-colors"
            >
              <X className="w-3.5 h-3.5" />
            </button>
          </div>
          <div className="p-4 space-y-4 text-xs">
            <div>
              <div className="text-slate-400 mb-1.5">缩放模式</div>
              <div className="grid grid-cols-2 gap-1.5">
                <SegBtn active={scaleMode === "fit"} onClick={() => setScaleMode("fit")} label="自适应" desc="VM 跟随窗口" />
                <SegBtn active={scaleMode === "native"} onClick={() => setScaleMode("native")} label="原始" desc="VM 原分辨率" />
              </div>
            </div>
            <div>
              <div className="text-slate-400 mb-1.5">画面策略</div>
              <div className="grid grid-cols-2 gap-1.5">
                <SegBtn active={perfMode === "smooth"} onClick={() => setPerfMode("smooth")} label="流畅优先" desc="降低输入延迟" />
                <SegBtn active={perfMode === "sharp"} onClick={() => setPerfMode("sharp")} label="清晰优先" desc="提高画面质量" />
              </div>
            </div>
            <SettingSwitch
              title="像素化渲染"
              desc="关闭缩放抗锯齿，让远程文字边缘更锐利"
              checked={pixelated}
              onChange={setPixelated}
            />
            <SettingSwitch
              title="显示鼠标点光标"
              desc="虚拟机不显示光标时，用小白点辅助定位"
              checked={showDotCursor}
              onChange={setShowDotCursor}
            />
          </div>
        </div>
      )}

      {/* ─── 画布 ──────────────────────────────────────────────── */}
      <div className="relative flex-1 overflow-hidden bg-black">
        <div ref={containerRef} className="absolute inset-0" />

        {(state === "connecting" || state === "init") && (
          <div className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-[#0b0f17]/90 backdrop-blur-sm">
            <div className="relative">
              <div className="absolute inset-0 rounded-full bg-purple-500/30 blur-xl" />
              <Loader2 className="relative w-7 h-7 animate-spin text-purple-300" />
            </div>
            <span className="text-xs text-slate-400">正在建立 VNC 通道…</span>
          </div>
        )}
        {state === "error" && (
          <div className="absolute inset-0 flex flex-col items-center justify-center gap-4 bg-[#0b0f17]/95 backdrop-blur-sm">
            <div className="flex h-11 w-11 items-center justify-center rounded-full bg-red-500/15 border border-red-500/40">
              <AlertTriangle className="w-5 h-5 text-red-400" />
            </div>
            <div className="text-center max-w-md">
              <p className="text-sm font-semibold text-red-300">连接异常</p>
              <p className="mt-1 text-xs text-slate-400">{errMsg || "未知错误"}</p>
            </div>
            <button
              onClick={() => void connect()}
              className="inline-flex items-center gap-1.5 rounded-md bg-purple-500/90 px-4 py-1.5 text-xs font-medium text-white hover:bg-purple-500"
            >
              <RefreshCw className="w-3.5 h-3.5" />
              重新连接
            </button>
          </div>
        )}
        {state === "disconnected" && (
          <div className="absolute inset-0 flex flex-col items-center justify-center gap-3 bg-[#0b0f17]/95 backdrop-blur-sm">
            <p className="text-xs text-slate-400">会话已断开</p>
            <button
              onClick={() => void connect()}
              className="inline-flex items-center gap-1.5 rounded-md bg-purple-500/90 px-4 py-1.5 text-xs font-medium text-white hover:bg-purple-500"
            >
              <RefreshCw className="w-3.5 h-3.5" />
              重新连接
            </button>
          </div>
        )}
      </div>
    </div>
  )
}

// ─── 工具组件 ───────────────────────────────────────────────────

function ToolBtn({
  icon, label, hint, onClick, disabled, active,
}: {
  icon: React.ReactNode
  label: string
  hint?: string
  onClick: () => void
  disabled?: boolean
  active?: boolean
}) {
  return (
    <button
      onClick={onClick}
      disabled={disabled}
      title={hint || label}
      className={`inline-flex items-center gap-1.5 h-7 rounded px-2 text-xs font-medium transition-colors disabled:opacity-30 disabled:cursor-not-allowed ${
        active
          ? "bg-slate-700/70 text-white"
          : "text-slate-300 hover:bg-slate-800 hover:text-white"
      }`}
    >
      {icon}
      <span className="hidden md:inline">{label}</span>
    </button>
  )
}

function SegBtn({
  active, onClick, label, desc,
}: {
  active: boolean
  onClick: () => void
  label: string
  desc: string
}) {
  return (
    <button
      onClick={onClick}
      className={`flex flex-col items-start rounded-md border px-2.5 py-2 text-left transition-colors ${
        active
          ? "border-purple-500/50 bg-purple-500/10 text-purple-200"
          : "border-slate-800 bg-slate-900/60 text-slate-400 hover:border-slate-700 hover:text-slate-200"
      }`}
    >
      <span className="text-xs font-semibold">{label}</span>
      <span className={`text-[10px] mt-0.5 ${active ? "text-purple-300/80" : "text-slate-500"}`}>{desc}</span>
    </button>
  )
}

function SettingSwitch({
  title, desc, checked, onChange,
}: {
  title: string
  desc: string
  checked: boolean
  onChange: (v: boolean) => void
}) {
  return (
    <button
      type="button"
      role="switch"
      aria-checked={checked}
      onClick={() => onChange(!checked)}
      className={`flex w-full items-center justify-between gap-3 rounded-lg border px-3 py-3 text-left transition-colors ${
        checked
          ? "border-purple-500/45 bg-purple-500/10"
          : "border-slate-800 bg-slate-900/60 hover:border-slate-700"
      }`}
    >
      <span className="min-w-0">
        <span className={`block text-xs font-semibold ${checked ? "text-purple-100" : "text-slate-200"}`}>{title}</span>
        <span className="mt-1 block text-[10px] leading-4 text-slate-500">{desc}</span>
      </span>
      <span className={`relative h-5 w-9 shrink-0 rounded-full border transition-colors ${
        checked ? "border-purple-400 bg-purple-500" : "border-slate-700 bg-slate-800"
      }`}>
        <span
          className={`absolute top-0.5 h-3.5 w-3.5 rounded-full bg-white shadow-sm transition-transform ${checked ? "translate-x-4" : "translate-x-0.5"}`}
        />
      </span>
    </button>
  )
}
