import {
  Layout,
  Modal,
  Input,
  message,
  ConfigProvider,
  theme as antTheme,
  Drawer,
  Button,
} from "antd"
import {
  HomeOutlined,
  SettingOutlined,
  UserOutlined,
  HistoryOutlined,
  SyncOutlined,
  AppstoreAddOutlined,
  ApartmentOutlined,
  UnorderedListOutlined,
  FileTextOutlined,
  MoreOutlined,
  UpOutlined,
  DownOutlined,
  HolderOutlined,
  CrownOutlined,
} from "@ant-design/icons"
import { useCallback, useEffect, useMemo, useRef, useState } from "react"
import { HashRouter, useLocation, useNavigate, Routes, Route } from "react-router-dom"
import { useTranslation } from "react-i18next"
import { Sidebar } from "./components/Sidebar"
import { ContentArea } from "./components/ContentArea"
import { OOBE } from "./components/OOBE/OOBE"
import { OAuthLogin } from "./components/OAuth/OAuthLogin"
import { OAuthCallback } from "./components/OAuth/OAuthCallback"
import { ThemeProvider, useTheme } from "./contexts/ThemeContext"
import { MOBILE_NAV_ITEMS, MobileNavKey, sanitizeMobileNavKeys } from "./shared/mobileNavigation"
import { resolveStoredFontFamily } from "./shared/fontFamily"
import { getPluginRuntime } from "./plugins/runtime"
import { sectlCloudSync } from "./services/sectlCloudSync"
import { sectlAuth } from "./services/sectlAuth"

const DEFAULT_MOBILE_BOTTOM_NAV_ITEMS: MobileNavKey[] = MOBILE_NAV_ITEMS.map((item) => item.key)
const DEFAULT_MOBILE_BOTTOM_PRIMARY_KEYS: MobileNavKey[] = DEFAULT_MOBILE_BOTTOM_NAV_ITEMS.slice(
  0,
  4
)
const applyGlobalFontFamily = (fontValue?: string) => {
  const fontFamily = resolveStoredFontFamily(fontValue)
  document.documentElement.style.setProperty("--ss-font-family", fontFamily)
  document.body.style.fontFamily = fontFamily
}

function MainContent(): React.JSX.Element {
  const { t } = useTranslation()
  const navigate = useNavigate()
  const location = useLocation()
  const { currentTheme } = useTheme()
  const [messageApi, contextHolder] = message.useMessage()
  const { isIosDevice, isAndroidDevice, defaultPortraitMode } = useMemo(getMobileDeviceInfo, [])
  const [immersiveMode, setImmersiveMode] = useState(false)

  const normalizeStoredBottomKeys = (raw: unknown): MobileNavKey[] => {
    if (Array.isArray(raw)) {
      return sanitizeMobileNavKeys(raw, []).slice(0, 4)
    }
    return DEFAULT_MOBILE_BOTTOM_PRIMARY_KEYS
  }

  useEffect(() => {
    const api = (window as any).api
    if (!api) return

    let disposed = false
    let unlisten: (() => void) | null = null

    api
      .onNavigate((route: string) => {
        const currentPath = location.pathname === "/" ? "/home" : location.pathname
        const targetPath = route === "/" ? "/home" : route

        if (immersiveMode && !targetPath.startsWith("/home")) {
          navigate("/", { replace: true })
          return
        }

        if (currentPath !== targetPath) {
          navigate(route)
        }
      })
      .then((fn: () => void) => {
        if (disposed) {
          fn()
          return
        }
        unlisten = fn
      })
      .catch(() => void 0)

    return () => {
      disposed = true
      if (unlisten) unlisten()
    }
  }, [navigate, location.pathname, immersiveMode])

  const [wizardVisible, setWizardVisible] = useState(false)
  const [permission, setPermission] = useState<"admin" | "points" | "view">("view")
  const [hasAnyPassword, setHasAnyPassword] = useState(false)
  const [authVisible, setAuthVisible] = useState(false)
  const [authPassword, setAuthPassword] = useState("")
  const [authLoading, setAuthLoading] = useState(false)
  const [oauthVisible, setOAuthVisible] = useState(false)
  const [oauthUserName, setOAuthUserName] = useState<string | null>(null)
  const [mobileBottomNavItems, setMobileBottomNavItems] = useState<MobileNavKey[]>(
    DEFAULT_MOBILE_BOTTOM_NAV_ITEMS
  )
  const [moreNavVisible, setMoreNavVisible] = useState(false)
  const [editingNav, setEditingNav] = useState(false)
  const [editingBottomNavKeys, setEditingBottomNavKeys] = useState<MobileNavKey[]>([])
  const [editingMoreNavKeys, setEditingMoreNavKeys] = useState<MobileNavKey[]>([])
  const [draggingNavKey, setDraggingNavKey] = useState<MobileNavKey | null>(null)
  const [draggingFromList, setDraggingFromList] = useState<"bottom" | "more" | null>(null)
  const [dragOverSlot, setDragOverSlot] = useState<{
    list: "bottom" | "more"
    index: number
  } | null>(null)
  const [isPortraitMode] = useState(defaultPortraitMode)
  const [sidebarCollapsed, setSidebarCollapsed] = useState(defaultPortraitMode)
  const [floatingSidebarExpanded, setFloatingSidebarExpanded] = useState(false)
  const [syncConflictVisible, setSyncConflictVisible] = useState(false)
  const [syncConflicts, setSyncConflicts] = useState<
    Array<{ table: string; key: string; local_summary: string; remote_summary: string }>
  >([])
  const [syncApplyLoading, setSyncApplyLoading] = useState(false)
  const syncCheckingRef = useRef(false)
  const syncApplyLoadingRef = useRef(false)
  const lastLocalMutationAtRef = useRef(0)
  const pluginRuntimeRef = useRef(getPluginRuntime())
  const refreshPermissionFromAuth = useCallback(async () => {
    const api = (window as any).api
    if (!api) return

    const authRes = await api.authGetStatus()
    if (authRes?.success && authRes.data) {
      const anyPwd = Boolean(authRes.data.hasAdminPassword || authRes.data.hasPointsPassword)
      setHasAnyPassword(anyPwd)
      setPermission(authRes.data.permission)
      setAuthVisible(anyPwd && authRes.data.permission === "view")
    }
  }, [])

  const activeMenu = useMemo(() => {
    const p = location.pathname
    if (p === "/" || p.startsWith("/home")) return "home"
    if (p.startsWith("/students")) return "students"
    if (p.startsWith("/score")) return "score"
    if (p.startsWith("/boards")) return "boards"
    if (p.startsWith("/leaderboard")) return "leaderboard"
    if (p.startsWith("/settlements")) return "settlements"
    if (p.startsWith("/reasons")) return "reasons"
    if (p.startsWith("/auto-score")) return "auto-score"
    if (p.startsWith("/reward-settings")) return "reward-settings"
    if (p.startsWith("/plugins")) return "plugins"
    if (p.startsWith("/settings")) return "settings"
    return "home"
  }, [location.pathname])

  useEffect(() => {
    const runtime = pluginRuntimeRef.current
    runtime.start().catch((error) => {
      console.error("Failed to start plugin runtime:", error)
    })

    const handlePluginsUpdated = () => {
      runtime.reload().catch((error) => {
        console.error("Failed to reload plugin runtime:", error)
      })
    }
    window.addEventListener("ss:plugins-updated", handlePluginsUpdated)

    return () => {
      window.removeEventListener("ss:plugins-updated", handlePluginsUpdated)
      runtime.stop().catch((error) => {
        console.error("Failed to stop plugin runtime:", error)
      })
    }
  }, [])

  useEffect(() => {
    const normalizedPath = location.pathname === "/" ? "/home" : location.pathname
    window.dispatchEvent(new CustomEvent("ss:route-changed", { detail: { path: normalizedPath } }))
  }, [location.pathname])

  useEffect(() => {
    const checkWizard = async () => {
      if (!(window as any).api) return
      const res = await (window as any).api.getAllSettings()
      if (res.success && res.data && !res.data.is_wizard_completed) {
        setWizardVisible(true)
      }
    }
    checkWizard()
  }, [])

  useEffect(() => {
    const loadAuthAndSettings = async () => {
      const api = (window as any).api
      if (!api) return

      const [authRes, oauthRes, settingsRes] = await Promise.all([
        api.authGetStatus(),
        api.oauthLoadLoginState(),
        api.getAllSettings(),
      ])

      // OAuth 登录仅用于云服务，不影响本地权限
      if (oauthRes?.success && oauthRes.data?.name) {
        setOAuthUserName(String(oauthRes.data.name))
      } else {
        setOAuthUserName(null)
      }

      // 本地权限系统独立于 OAuth
      if (authRes?.success && authRes.data) {
        const anyPwd = Boolean(authRes.data.hasAdminPassword || authRes.data.hasPointsPassword)
        setHasAnyPassword(anyPwd)
        setPermission(authRes.data.permission)
        setAuthVisible(anyPwd && authRes.data.permission === "view")
      }

      if (settingsRes?.success && settingsRes.data) {
        setMobileBottomNavItems(normalizeStoredBottomKeys(settingsRes.data.mobile_bottom_nav_items))
        applyGlobalFontFamily(settingsRes.data.font_family)
      }
    }

    loadAuthAndSettings()
  }, [])

  useEffect(() => {
    const handleOAuthUserUpdated = (event: Event) => {
      const customEvent = event as CustomEvent<{
        user?: { name?: string } | null
      }>
      const userName = customEvent?.detail?.user?.name
      if (typeof userName === "string" && userName.trim()) {
        setOAuthUserName(userName)
      } else {
        setOAuthUserName(null)
        void refreshPermissionFromAuth()
      }
    }

    window.addEventListener("ss:oauth-user-updated", handleOAuthUserUpdated as EventListener)
    return () => {
      window.removeEventListener("ss:oauth-user-updated", handleOAuthUserUpdated as EventListener)
    }
  }, [refreshPermissionFromAuth])

  // 监听 Deep Link 事件（用于 OAuth 回调）
  useEffect(() => {
    const api = (window as any).api
    if (!api || typeof api.onDeepLink !== "function") return

    let disposed = false
    let unlisten: (() => void) | null = null

    api
      .onDeepLink((url: string) => {
        console.log("[DeepLink] Received URL:", url)
        if (url.startsWith("secscore://oauth")) {
          // 解析 OAuth 回调参数
          const urlObj = new URL(url)
          const code = urlObj.searchParams.get("code")
          const state = urlObj.searchParams.get("state")
          const error = urlObj.searchParams.get("error")

          if (error) {
            console.error("[DeepLink] OAuth error:", error)
            messageApi.error("登录失败: " + error)
            return
          }

          if (code && state) {
            // 发送自定义事件，让 OAuthCallback 组件处理
            window.dispatchEvent(
              new CustomEvent("ss:oauth-deep-link", {
                detail: { code, state },
              })
            )
          }
        }
      })
      .then((fn: () => void) => {
        if (disposed) {
          fn()
          return
        }
        unlisten = fn
      })
      .catch((err: any) => {
        console.error("[DeepLink] Failed to listen:", err)
      })

    return () => {
      disposed = true
      if (unlisten) unlisten()
    }
  }, [messageApi])

  useEffect(() => {
    const api = (window as any).api
    if (!api || typeof api.onSettingChanged !== "function") return

    let disposed = false
    let unlisten: (() => void) | null = null

    api
      .onSettingChanged((change: { key?: string; value?: unknown }) => {
        if (change?.key === "mobile_bottom_nav_items") {
          setMobileBottomNavItems(normalizeStoredBottomKeys(change.value))
          return
        }
        if (change?.key === "font_family") {
          applyGlobalFontFamily(String(change.value || "system"))
        }
      })
      .then((fn: () => void) => {
        if (disposed) {
          fn()
          return
        }
        unlisten = fn
      })
      .catch(() => void 0)

    return () => {
      disposed = true
      if (unlisten) unlisten()
    }
  }, [])

  useEffect(() => {
    const api = (window as any).api
    if (!api || !isIosDevice) return

    const fitIosWindow = async () => {
      try {
        await api.windowMaximize()
      } catch {
        void 0
      }

      try {
        await api.windowSetResizable(false)
      } catch {
        void 0
      }

      try {
        // 传入远大于设备尺寸的物理像素，交由系统裁剪为可用全屏区域
        await api.windowResize(10000, 10000)
      } catch {
        void 0
      }
    }

    fitIosWindow().catch(() => void 0)
    const timer = window.setTimeout(() => {
      fitIosWindow().catch(() => void 0)
    }, 300)

    return () => {
      window.clearTimeout(timer)
    }
  }, [isIosDevice])

  useEffect(() => {
    const api = (window as any).api
    if (!api || typeof api.onDataUpdated !== "function") return

    let disposed = false
    let unlisten: (() => void) | null = null

    api
      .onDataUpdated((payload: { category?: string; source?: string }) => {
        const detail = {
          category: payload?.category || "all",
          source: payload?.source || "tauri",
        }
        window.dispatchEvent(new CustomEvent("ss:data-updated", { detail }))
      })
      .then((fn: () => void) => {
        if (disposed) {
          fn()
          return
        }
        unlisten = fn
      })
      .catch(() => void 0)

    return () => {
      disposed = true
      if (unlisten) unlisten()
    }
  }, [])

  const applySyncStrategy = async (strategy: "keep_local" | "keep_remote") => {
    const api = (window as any).api
    if (!api) return
    setSyncApplyLoading(true)
    syncApplyLoadingRef.current = true
    try {
      const res = await api.dbSyncApply(strategy)
      if (res?.success && res?.data?.success) {
        messageApi.success(
          res.data.message ||
            `同步完成（同步 ${res.data.synced_records} 条，解决冲突 ${res.data.resolved_conflicts} 条）`
        )
        window.dispatchEvent(
          new CustomEvent("ss:data-updated", { detail: { category: "all", source: "sync" } })
        )
      } else {
        messageApi.error(res?.data?.message || res?.message || "同步失败")
      }
    } catch (error: any) {
      messageApi.error(error?.message || "同步失败")
    } finally {
      setSyncApplyLoading(false)
      syncApplyLoadingRef.current = false
      setSyncConflictVisible(false)
      setSyncConflicts([])
    }
  }

  useEffect(() => {
    const api = (window as any).api
    if (!api) return
    let disposed = false

    const checkAndSync = async () => {
      if (disposed || syncCheckingRef.current || syncApplyLoadingRef.current) return
      if (permission !== "admin") return
      try {
        syncCheckingRef.current = true

        const settingsRes = await api.getAllSettings()
        const syncMethod = settingsRes?.success ? settingsRes.data?.sync_method : null

        if (syncMethod === "sectl_cloud") {
          if (!sectlAuth.isAuthenticated()) return
          const status = sectlCloudSync.getStatus()
          if (!status.is_configured || status.is_syncing) return

          const now = Date.now()
          const recentLocal = now - lastLocalMutationAtRef.current < 15000
          const direction = recentLocal ? "push" : "bidirectional"
          const result = await sectlCloudSync.fullSync(direction)
          if (result.success) {
            window.dispatchEvent(
              new CustomEvent("ss:data-updated", { detail: { category: "all", source: "sync" } })
            )
          }
          return
        }

        const statusRes = await api.dbGetStatus()
        if (
          !statusRes?.success ||
          statusRes?.data?.type !== "postgresql" ||
          !statusRes?.data?.connected
        ) {
          return
        }

        const previewRes = await api.dbSyncPreview()
        if (!previewRes?.success || !previewRes?.data?.can_sync || !previewRes?.data?.need_sync) {
          return
        }

        const conflicts = previewRes.data.conflicts || []
        if (conflicts.length > 0) {
          const recentLocalMutation = Date.now() - lastLocalMutationAtRef.current < 15000
          if (recentLocalMutation) {
            const autoApplyRes = await api.dbSyncApply("keep_remote")
            if (
              autoApplyRes?.success &&
              autoApplyRes?.data?.success &&
              autoApplyRes?.data?.synced_records > 0
            ) {
              window.dispatchEvent(
                new CustomEvent("ss:data-updated", { detail: { category: "all", source: "sync" } })
              )
            }
            return
          }
          setSyncConflicts(conflicts)
          setSyncConflictVisible(true)
          return
        }

        const applyRes = await api.dbSyncApply("keep_remote")
        if (applyRes?.success && applyRes?.data?.success && applyRes?.data?.synced_records > 0) {
          window.dispatchEvent(
            new CustomEvent("ss:data-updated", { detail: { category: "all", source: "sync" } })
          )
        }
      } catch (error) {
        console.error("Auto sync failed:", error)
      } finally {
        syncCheckingRef.current = false
      }
    }

    checkAndSync()
    const timer = window.setInterval(checkAndSync, 30000)
    const onDataUpdated = (e: Event) => {
      const customEvent = e as CustomEvent<{ source?: string }>
      if (customEvent?.detail?.source !== "sync") {
        lastLocalMutationAtRef.current = Date.now()
      }
      window.setTimeout(() => {
        checkAndSync().catch(() => void 0)
      }, 1200)
    }
    window.addEventListener("ss:data-updated", onDataUpdated)

    return () => {
      disposed = true
      window.clearInterval(timer)
      window.removeEventListener("ss:data-updated", onDataUpdated)
    }
  }, [permission])

  const login = async () => {
    if (!(window as any).api) return
    setAuthLoading(true)
    const res = await (window as any).api.authLogin(authPassword)
    setAuthLoading(false)
    if (res.success && res.data) {
      setPermission(res.data.permission)
      setAuthVisible(false)
      setAuthPassword("")
      messageApi.success(t("auth.unlocked"))
    } else {
      messageApi.error(res.message || t("common.error"))
    }
  }

  const logout = async () => {
    const api = (window as any).api
    if (!api) return

    // 退出时同时清理 OAuth 持久化状态，避免刷新后又自动恢复权限
    try {
      await api.oauthClearLoginState()
      setOAuthUserName(null)
      window.dispatchEvent(new CustomEvent("ss:oauth-user-updated", { detail: { user: null } }))
    } catch (error) {
      console.error("Failed to clear OAuth login state:", error)
    }

    const res = await api.authLogout()
    if (res?.success && res.data) {
      setPermission(res.data.permission)
      messageApi.success(t("auth.logout"))
    }
  }

  const logoutOAuthFromHeader = useCallback(async () => {
    const api = (window as any).api
    if (!api) return

    try {
      await api.oauthClearLoginState()
      setOAuthUserName(null)
      window.dispatchEvent(new CustomEvent("ss:oauth-user-updated", { detail: { user: null } }))
      await refreshPermissionFromAuth()
      messageApi.success("已退出云账号")
    } catch (error: any) {
      messageApi.error(error?.message || t("common.error"))
    }
  }, [messageApi, refreshPermissionFromAuth, t])

  const handleOAuthSuccess = (userInfo: {
    user_id?: string
    id?: string
    email?: string
    name?: string
    avatar?: string
    avatar_url?: string
  }) => {
    // OAuth 登录仅用于云服务，不修改本地权限
    setOAuthUserName(userInfo.name || null)
    messageApi.success(t("auth.oauthSuccess", "登录成功"))
  }

  const onMenuChange = (v: string) => {
    const key = String(v)
    setMoreNavVisible(false)
    setEditingNav(false)
    if (immersiveMode && key !== "home") return
    if (key === "home") navigate("/")
    if (key === "students") navigate("/students")
    if (key === "score") navigate("/score")
    if (key === "auto-score") navigate("/auto-score")
    if (key === "reward-settings") navigate("/reward-settings")
    if (key === "boards") navigate("/boards")
    if (key === "leaderboard") navigate("/leaderboard")
    if (key === "settlements") navigate("/settlements")
    if (key === "reasons") navigate("/reasons")
    if (key === "plugins") navigate("/plugins")
    if (key === "settings") navigate("/settings")
  }

  const toggleSidebar = () => {
    if (immersiveMode) return
    if (isPortraitMode && sidebarCollapsed) {
      setFloatingSidebarExpanded((prev) => !prev)
      return
    }
    setSidebarCollapsed((prev) => !prev)
  }

  useEffect(() => {
    if (!immersiveMode) return
    if (location.pathname !== "/" && !location.pathname.startsWith("/home")) {
      navigate("/", { replace: true })
    }
  }, [immersiveMode, location.pathname, navigate])

  const toggleImmersiveMode = () => {
    setImmersiveMode((prev) => {
      const next = !prev
      if (next) {
        setFloatingSidebarExpanded(false)
        if (location.pathname !== "/" && !location.pathname.startsWith("/home")) {
          navigate("/", { replace: true })
        }
      }
      return next
    })
  }

  const isDark = currentTheme?.mode === "dark"
  const brandColor = currentTheme?.config?.tdesign?.brandColor || "#0052D9"
  const isMobileDevice = isIosDevice || isAndroidDevice
  const showMobileBottomNav = isPortraitMode && !immersiveMode
  const mobileBottomNavIconMap: Record<MobileNavKey, React.ReactNode> = {
    home: <HomeOutlined style={{ fontSize: "18px" }} />,
    students: <UserOutlined style={{ fontSize: "18px" }} />,
    score: <HistoryOutlined style={{ fontSize: "18px" }} />,
    "auto-score": <SyncOutlined style={{ fontSize: "18px" }} />,
    "reward-settings": <AppstoreAddOutlined style={{ fontSize: "18px" }} />,
    boards: <ApartmentOutlined style={{ fontSize: "18px" }} />,
    leaderboard: <UnorderedListOutlined style={{ fontSize: "18px" }} />,
    settlements: <FileTextOutlined style={{ fontSize: "18px" }} />,
    reasons: <UnorderedListOutlined style={{ fontSize: "18px" }} />,
    plugins: <CrownOutlined style={{ fontSize: "18px" }} />,
    settings: <SettingOutlined style={{ fontSize: "18px" }} />,
  }

  const mobileNavAvailableItems = useMemo(
    () => MOBILE_NAV_ITEMS.filter((item) => !(item.adminOnly && permission !== "admin")),
    [permission]
  )
  const mobileBottomSelectedKeys = useMemo(() => {
    const availableKeySet = new Set(mobileNavAvailableItems.map((item) => item.key))
    return sanitizeMobileNavKeys(mobileBottomNavItems, [])
      .filter((key) => availableKeySet.has(key))
      .slice(0, 4)
  }, [mobileBottomNavItems, mobileNavAvailableItems])
  const mobileBottomPrimaryItems = useMemo(
    () =>
      mobileBottomSelectedKeys
        .map((key) => mobileNavAvailableItems.find((item) => item.key === key))
        .filter((item): item is (typeof MOBILE_NAV_ITEMS)[number] => Boolean(item)),
    [mobileBottomSelectedKeys, mobileNavAvailableItems]
  )
  const mobileBottomOverflowItems = useMemo(
    () => mobileNavAvailableItems.filter((item) => !mobileBottomSelectedKeys.includes(item.key)),
    [mobileNavAvailableItems, mobileBottomSelectedKeys]
  )
  const isMoreActive = mobileBottomOverflowItems.some((item) => item.key === activeMenu)

  const openEditNav = () => {
    const savedKeys = mobileBottomSelectedKeys
    const missingKeys = mobileNavAvailableItems
      .map((item) => item.key)
      .filter((key) => !savedKeys.includes(key))
    setEditingBottomNavKeys(savedKeys)
    setEditingMoreNavKeys(missingKeys)
    setEditingNav(true)
    setDraggingNavKey(null)
    setDraggingFromList(null)
    setDragOverSlot(null)
  }

  const dropPlaceholder = (
    <div
      style={{
        border: "1px dashed var(--ant-color-primary)",
        borderRadius: "10px",
        background: "color-mix(in srgb, var(--ant-color-primary) 10%, transparent)",
        minHeight: "44px",
        padding: "8px 10px",
      }}
    />
  )

  const persistMobileBottomKeys = async (nextKeys: MobileNavKey[]) => {
    const api = (window as any).api
    if (!api) return
    const next = sanitizeMobileNavKeys(nextKeys, [])
    const res = await api.setSetting("mobile_bottom_nav_items", next)
    if (res.success) {
      setMobileBottomNavItems(next)
    } else {
      messageApi.error(res.message || t("settings.general.saveFailed"))
    }
  }

  const handleDropToList = (targetList: "bottom" | "more", targetIndex: number) => {
    if (!draggingNavKey || !draggingFromList) return
    const bottom = editingBottomNavKeys.slice()
    const more = editingMoreNavKeys.slice()

    const source = draggingFromList === "bottom" ? bottom : more
    const sourceIndex = source.indexOf(draggingNavKey)
    if (sourceIndex < 0) return
    source.splice(sourceIndex, 1)

    if (targetList === "bottom" && draggingFromList === "more" && bottom.length >= 4) {
      setDraggingNavKey(null)
      setDraggingFromList(null)
      setDragOverSlot(null)
      messageApi.warning(t("settings.mobile.bottomMaxHint", "底栏最多 4 个"))
      return
    }

    const target = targetList === "bottom" ? bottom : more
    const clampedIndex = Math.max(0, Math.min(targetIndex, target.length))
    target.splice(clampedIndex, 0, draggingNavKey)

    setEditingBottomNavKeys(bottom)
    setEditingMoreNavKeys(more)
    void persistMobileBottomKeys(bottom)
    setDraggingNavKey(null)
    setDraggingFromList(null)
    setDragOverSlot(null)
  }

  return (
    <ConfigProvider
      theme={{
        algorithm: isDark ? antTheme.darkAlgorithm : antTheme.defaultAlgorithm,
        token: {
          colorPrimary: brandColor,
          fontFamily:
            '"PingFang SC", "Hiragino Sans GB", "Heiti SC", "Noto Sans SC", "Noto Sans CJK SC", "Microsoft YaHei", "微软雅黑", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',
        },
      }}
    >
      {contextHolder}
      <Layout
        style={{ height: "100%", flexDirection: "row", overflow: "hidden", position: "relative" }}
      >
        {!isPortraitMode && (
          <div
            className={`ss-immersive-sidebar ${immersiveMode ? "is-hidden" : "is-visible"}`}
            style={
              {
                "--ss-sidebar-width": `${sidebarCollapsed ? 64 : 200}px`,
              } as React.CSSProperties
            }
          >
            <Sidebar
              activeMenu={activeMenu}
              permission={permission}
              onMenuChange={onMenuChange}
              collapsed={sidebarCollapsed}
              floatingExpand={isPortraitMode}
              floatingExpanded={floatingSidebarExpanded}
              onFloatingExpandedChange={setFloatingSidebarExpanded}
            />
          </div>
        )}
        <ContentArea
          permission={permission}
          oauthUserName={oauthUserName}
          onOAuthLogout={logoutOAuthFromHeader}
          hasAnyPassword={hasAnyPassword}
          onAuthClick={() => setAuthVisible(true)}
          onLogout={logout}
          showWindowControls={!isIosDevice && !isAndroidDevice}
          isPortraitMode={isPortraitMode}
          isMobileDevice={isMobileDevice}
          sidebarCollapsed={sidebarCollapsed}
          floatingExpand={isPortraitMode}
          floatingExpanded={floatingSidebarExpanded}
          onToggleSidebar={toggleSidebar}
          immersiveMode={immersiveMode}
          isHomePage={activeMenu === "home"}
          onToggleImmersiveMode={toggleImmersiveMode}
          showSidebarToggle={!isPortraitMode}
          bottomInset={showMobileBottomNav ? 84 : 0}
        />
        {showMobileBottomNav && (
          <div
            style={{
              position: "fixed",
              left: 0,
              right: 0,
              bottom: 0,
              background: "var(--ss-header-bg)",
              backdropFilter: "blur(14px)",
              WebkitBackdropFilter: "blur(14px)",
              borderTop: "1px solid var(--ss-border-color)",
              zIndex: 1400,
              paddingBottom: "env(safe-area-inset-bottom, 0px)",
            }}
          >
            <div style={{ display: "flex", height: "60px" }}>
              {mobileBottomPrimaryItems.map((item) => (
                <button
                  key={item.key}
                  type="button"
                  onClick={() => onMenuChange(item.key)}
                  style={{
                    flex: 1,
                    border: "none",
                    background: "transparent",
                    color:
                      activeMenu === item.key
                        ? "var(--ant-color-primary)"
                        : "var(--ss-text-secondary, var(--ss-text-main))",
                    display: "flex",
                    flexDirection: "column",
                    alignItems: "center",
                    justifyContent: "center",
                    gap: "2px",
                    fontSize: "12px",
                  }}
                >
                  {mobileBottomNavIconMap[item.key]}
                  <span>{t(item.labelKey)}</span>
                </button>
              ))}
              {mobileBottomOverflowItems.length > 0 && (
                <button
                  type="button"
                  onClick={() => setMoreNavVisible(true)}
                  style={{
                    flex: 1,
                    border: "none",
                    background: "transparent",
                    color: isMoreActive
                      ? "var(--ant-color-primary)"
                      : "var(--ss-text-secondary, var(--ss-text-main))",
                    display: "flex",
                    flexDirection: "column",
                    alignItems: "center",
                    justifyContent: "center",
                    gap: "2px",
                    fontSize: "12px",
                  }}
                >
                  <MoreOutlined style={{ fontSize: "18px" }} />
                  <span>{t("common.more", "更多")}</span>
                </button>
              )}
            </div>
          </div>
        )}

        <Drawer
          title={editingNav ? t("common.edit") : t("common.more", "更多")}
          placement="bottom"
          open={moreNavVisible}
          onClose={() => {
            setMoreNavVisible(false)
            setEditingNav(false)
            setDraggingNavKey(null)
            setDraggingFromList(null)
            setDragOverSlot(null)
          }}
          height={editingNav ? "calc(100vh - env(safe-area-inset-top, 0px))" : "46vh"}
          styles={{
            content: {
              borderTopLeftRadius: editingNav ? 0 : "14px",
              borderTopRightRadius: editingNav ? 0 : "14px",
              background: "var(--ss-card-bg)",
            },
            header: {
              borderBottom: "1px solid var(--ss-border-color)",
              padding: "12px 16px",
            },
            body: {
              padding: "12px 16px 20px",
              display: "flex",
              flexDirection: "column",
              minHeight: 0,
            },
          }}
          extra={
            permission === "admin" &&
            (editingNav ? (
              <button
                type="button"
                onClick={() => setEditingNav(false)}
                style={{
                  border: "1px solid var(--ss-border-color)",
                  borderRadius: "8px",
                  background: "transparent",
                  color: "var(--ss-text-main)",
                  padding: "4px 10px",
                }}
              >
                {t("common.finish", "完成")}
              </button>
            ) : (
              <button
                type="button"
                onClick={openEditNav}
                style={{
                  border: "1px solid var(--ss-border-color)",
                  borderRadius: "8px",
                  background: "transparent",
                  color: "var(--ss-text-main)",
                  padding: "4px 10px",
                }}
              >
                {t("common.edit")}
              </button>
            ))
          }
        >
          {editingNav ? (
            <div
              style={{
                display: "flex",
                flexDirection: "column",
                gap: "12px",
                flex: 1,
                minHeight: 0,
                overflowY: "auto",
              }}
            >
              <div style={{ fontSize: "12px", color: "var(--ss-text-secondary)" }}>
                {t(
                  "settings.mobile.dragHint",
                  "拖动可调整顺序。前 4 项显示在底栏，其余显示在“更多”。"
                )}
              </div>
              {[
                {
                  list: "bottom" as const,
                  title: t("settings.mobile.bottomSection", "底栏（最多 4 个）"),
                  keys: editingBottomNavKeys,
                },
                {
                  list: "more" as const,
                  title: t("settings.mobile.moreSection", "更多"),
                  keys: editingMoreNavKeys,
                },
              ].map((group) => (
                <div key={group.list} style={{ marginTop: group.list === "more" ? 10 : 0 }}>
                  <div
                    style={{
                      marginBottom: "8px",
                      fontSize: "12px",
                      color: "var(--ss-text-secondary)",
                    }}
                  >
                    {group.title}
                  </div>
                  <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
                    {Array.from({ length: group.keys.length + 1 }, (_, i) => {
                      const key = group.keys[i]
                      const item = key ? MOBILE_NAV_ITEMS.find((it) => it.key === key) : null
                      return (
                        <div key={`${group.list}-slot-${i}`}>
                          <div
                            onDragEnter={() => setDragOverSlot({ list: group.list, index: i })}
                            onDragOver={(e) => {
                              e.preventDefault()
                              setDragOverSlot({ list: group.list, index: i })
                            }}
                            onDrop={() => handleDropToList(group.list, i)}
                          >
                            {dragOverSlot?.list === group.list && dragOverSlot?.index === i ? (
                              dropPlaceholder
                            ) : (
                              <div style={{ height: "8px" }} />
                            )}
                          </div>
                          {key && item && (
                            <button
                              type="button"
                              draggable
                              onDragStart={() => {
                                setDraggingNavKey(key)
                                setDraggingFromList(group.list)
                              }}
                              onDragEnd={() => {
                                setDraggingNavKey(null)
                                setDraggingFromList(null)
                                setDragOverSlot(null)
                              }}
                              style={{
                                border: "1px solid var(--ss-border-color)",
                                borderRadius: "10px",
                                background: "var(--ss-bg-color)",
                                color: "var(--ss-text-main)",
                                minHeight: "44px",
                                padding: "8px 10px",
                                display: "flex",
                                alignItems: "center",
                                justifyContent: "space-between",
                                gap: "8px",
                                fontSize: "14px",
                                opacity: draggingNavKey === key ? 0.35 : 1,
                                transform: draggingNavKey === key ? "scale(0.985)" : "scale(1)",
                                transition: "opacity 120ms ease, transform 120ms ease",
                                cursor: "grab",
                              }}
                            >
                              <span style={{ display: "flex", alignItems: "center", gap: "8px" }}>
                                {mobileBottomNavIconMap[key]}
                                <span>{t(item.labelKey)}</span>
                              </span>
                              <span
                                style={{
                                  display: "inline-flex",
                                  alignItems: "center",
                                  gap: "4px",
                                  color: "var(--ss-text-secondary)",
                                  opacity: 0.9,
                                }}
                              >
                                <UpOutlined style={{ fontSize: "11px" }} />
                                <DownOutlined style={{ fontSize: "11px" }} />
                                <HolderOutlined style={{ fontSize: "12px" }} />
                              </span>
                            </button>
                          )}
                        </div>
                      )
                    })}
                  </div>
                </div>
              ))}
            </div>
          ) : (
            <div style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
              {mobileBottomOverflowItems.map((item) => (
                <button
                  key={item.key}
                  type="button"
                  onClick={() => onMenuChange(item.key)}
                  style={{
                    border: "1px solid var(--ss-border-color)",
                    borderRadius: "10px",
                    background: "var(--ss-bg-color)",
                    color: "var(--ss-text-main)",
                    height: "44px",
                    padding: "0 12px",
                    display: "flex",
                    alignItems: "center",
                    gap: "8px",
                    fontSize: "14px",
                  }}
                >
                  {mobileBottomNavIconMap[item.key]}
                  <span>{t(item.labelKey)}</span>
                </button>
              ))}
            </div>
          )}
        </Drawer>

        <OOBE visible={wizardVisible} onComplete={() => setWizardVisible(false)} />

        <Modal
          title={t("auth.unlock")}
          open={authVisible}
          onCancel={() => setAuthVisible(false)}
          onOk={login}
          confirmLoading={authLoading}
          okText={t("auth.unlockButton")}
          cancelText={t("common.cancel")}
        >
          <div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
            <div style={{ color: "var(--ss-text-secondary)", fontSize: "12px" }}>
              {t("auth.unlockHint")}
            </div>
            <Input
              value={authPassword}
              onChange={(e) => setAuthPassword(e.target.value)}
              placeholder={t("auth.passwordPlaceholder")}
              maxLength={6}
            />
            <div style={{ textAlign: "center", marginTop: "8px" }}>
              <Button
                type="link"
                onClick={() => {
                  setAuthVisible(false)
                  setOAuthVisible(true)
                }}
              >
                {t("auth.useOAuth", "使用 SECTL Auth 登录")}
              </Button>
            </div>
          </div>
        </Modal>

        <OAuthLogin
          visible={oauthVisible}
          onClose={() => setOAuthVisible(false)}
          onSuccess={handleOAuthSuccess}
        />

        <Modal
          title="检测到本地与远程数据冲突"
          open={syncConflictVisible}
          onCancel={() => {
            if (syncApplyLoading) return
            setSyncConflictVisible(false)
          }}
          footer={null}
          closable={!syncApplyLoading}
          maskClosable={false}
          destroyOnHidden
        >
          <div
            style={{ marginBottom: "10px", color: "var(--ss-text-secondary)", fontSize: "12px" }}
          >
            自动同步发现冲突，请选择冲突时优先保留哪一侧的数据。
          </div>
          <div
            style={{
              maxHeight: "280px",
              overflow: "auto",
              border: "1px solid var(--ss-border-color)",
              borderRadius: "6px",
              padding: "8px",
              marginBottom: "12px",
              fontSize: "12px",
            }}
          >
            {syncConflicts.slice(0, 30).map((item) => (
              <div key={`${item.table}-${item.key}`} style={{ marginBottom: "8px" }}>
                <div>
                  <b>{item.table}</b> / <b>{item.key}</b>
                </div>
                <div>本地: {item.local_summary}</div>
                <div>远程: {item.remote_summary}</div>
              </div>
            ))}
            {syncConflicts.length > 30 && <div>仅显示前 30 条冲突...</div>}
          </div>
          <div style={{ display: "flex", justifyContent: "flex-end", gap: "8px" }}>
            <button
              style={{ padding: "6px 10px", cursor: syncApplyLoading ? "not-allowed" : "pointer" }}
              disabled={syncApplyLoading}
              onClick={() => applySyncStrategy("keep_remote")}
            >
              保留远程
            </button>
            <button
              style={{ padding: "6px 10px", cursor: syncApplyLoading ? "not-allowed" : "pointer" }}
              disabled={syncApplyLoading}
              onClick={() => applySyncStrategy("keep_local")}
            >
              保留本地
            </button>
          </div>
        </Modal>

        {import.meta.env.DEV ? (
          <div
            style={{
              position: "fixed",
              display: "flex",
              bottom: "2px",
              left: "20px",
              opacity: 0.75,
              zIndex: 9999,
              pointerEvents: "none",
            }}
          >
            <p
              style={{
                color: "#ff000d",
                fontWeight: "bolder",
                fontSize: "14px",
                pointerEvents: "none",
              }}
            >
              开发中画面,不代表最终品质
            </p>
            <p
              style={{
                color: currentTheme?.mode === "dark" ? "#fff" : "#3e3e3e",
                fontWeight: "bolder",
                fontSize: "13px",
                paddingLeft: "5px",
              }}
            >
              SecScore Dev ({getPlatform()}-{getArchitecture()})
            </p>
          </div>
        ) : null}
      </Layout>
    </ConfigProvider>
  )
}

function getArchitecture(): string {
  const userAgent = navigator.userAgent.toLowerCase()

  if (userAgent.includes("arm64") || userAgent.includes("aarch64")) {
    return "ARM64"
  } else if (
    userAgent.includes("x64") ||
    userAgent.includes("amd64") ||
    userAgent.includes("x86_64") ||
    userAgent.includes("intel")
  ) {
    return "x64"
  } else if (userAgent.includes("i386") || userAgent.includes("i686")) {
    return "x86"
  }

  return "Unknown"
}

function getPlatform(): string {
  const userAgent = navigator.userAgent.toLowerCase()

  if (userAgent.includes("iphone") || userAgent.includes("ipad") || userAgent.includes("ipod")) {
    return "iOS"
  } else if (userAgent.includes("android")) {
    return "Android"
  }

  if (userAgent.includes("windows")) {
    return "Windows"
  } else if (userAgent.includes("mac")) {
    return "Mac"
  } else if (userAgent.includes("linux")) {
    return "Linux"
  }

  return "Unknown"
}

function getIosDeviceInfo(): { isIosDevice: boolean; isIosPhone: boolean } {
  const userAgent = navigator.userAgent.toLowerCase()
  const isIosDevice = /iphone|ipad|ipod/.test(userAgent)
  const isIosTablet = userAgent.includes("ipad")
  return {
    isIosDevice,
    isIosPhone: isIosDevice && !isIosTablet,
  }
}

function getMobileDeviceInfo(): {
  isIosDevice: boolean
  isAndroidDevice: boolean
  defaultPortraitMode: boolean
} {
  const { isIosDevice, isIosPhone } = getIosDeviceInfo()
  const isAndroidDevice = navigator.userAgent.toLowerCase().includes("android")
  return {
    isIosDevice,
    isAndroidDevice,
    defaultPortraitMode: isIosPhone || isAndroidDevice,
  }
}
function App(): React.JSX.Element {
  return (
    <ThemeProvider>
      <HashRouter>
        <Routes>
          <Route path="/oauth/callback" element={<OAuthCallback />} />
          <Route path="/*" element={<MainContent />} />
        </Routes>
      </HashRouter>
    </ThemeProvider>
  )
}

export default App
