import {
  AuditOutlined,
  AccountBookOutlined,
  BellOutlined,
  BugOutlined,
  DashboardOutlined,
  FileSearchOutlined,
  LogoutOutlined,
  MenuFoldOutlined,
  MenuUnfoldOutlined,
  OrderedListOutlined,
  SafetyCertificateOutlined,
  SettingOutlined,
  ShopOutlined,
  SoundOutlined,
  TeamOutlined,
  TrophyOutlined,
  UnorderedListOutlined,
  UserOutlined,
} from '@ant-design/icons'
import {
  App,
  Badge,
  Button,
  Dropdown,
  Layout,
  List,
  Menu,
  Popover,
  Space,
  Tooltip,
  Typography,
} from 'antd'
import type { MenuProps } from 'antd'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { useEffect, useMemo, useState } from 'react'
import { Outlet, useLocation, useNavigate } from 'react-router'

import {
  fetchAdminSession,
  fetchAdminNotifications,
  fetchDevMockStatus,
  logoutAdmin,
  type AdminNotification,
} from '@/services/admin'
import {
  clearAdminSession,
  getAdminRole,
  getAdminToken,
  getAdminTokenExpiresAt,
  getAdminUsername,
  setAdminPermissions,
} from '@/utils/admin-auth'
import { formatAdminDateTime } from '@/utils/admin-time'
import { useRealtimeEvents, type RealtimeEvent } from '@/hooks/useRealtimeEvents'

const SIDEBAR_COLLAPSED_KEY = 'react-admin-sidebar-collapsed'
const NOTIFICATION_SOUND_KEY = 'admin-notification-sound-enabled'

export default function AdminLayout() {
  const navigate = useNavigate()
  const location = useLocation()
  const { message } = App.useApp()
  const queryClient = useQueryClient()
  const [collapsed, setCollapsed] = useState(
    () => window.localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === '1',
  )
  const role = getAdminRole()
  const username = getAdminUsername() || 'admin'
  const expiresAt = getAdminTokenExpiresAt()
  const [soundEnabled, setSoundEnabled] = useState(
    () => window.localStorage.getItem(NOTIFICATION_SOUND_KEY) === '1',
  )

  const adminSessionQuery = useQuery({
    queryKey: ['admin-session'],
    queryFn: fetchAdminSession,
    retry: false,
  })

  useEffect(() => {
    const session = adminSessionQuery.data?.data
    if (session?.authenticated && session.user) {
      setAdminPermissions(session.user.permissions || [])
    }
  }, [adminSessionQuery.data])

  const devMockStatusQuery = useQuery({
    queryKey: ['admin-dev-mock-status'],
    queryFn: () => fetchDevMockStatus(),
    enabled: role !== 'finance',
    retry: false,
  })
  const devMockEnabled = Boolean(devMockStatusQuery.data?.data?.enabled)
  const notificationsQuery = useQuery({
    queryKey: ['admin-notifications'],
    queryFn: fetchAdminNotifications,
    enabled: role !== 'finance',
    retry: false,
  })
  const notifications = notificationsQuery.data?.data.items || []
  const pendingNotificationCount = Number(notificationsQuery.data?.data.pendingCount || 0)

  useRealtimeEvents({
    url: '/api/v1/admin/realtime',
    token: role === 'finance' ? '' : getAdminToken(),
    onEvent: (event) => applyAdminRealtimeEvent(event, queryClient),
    // 会话过期时清登录态回登录页，避免 SSE 带着失效 token 无限重连刷 401。
    onAuthExpired: () => {
      clearAdminSession()
      void navigate('/admin/login', { replace: true, state: { from: location } })
    },
  })

  const menuItems = useMemo<MenuProps['items']>(() => {
    const operationItems: MenuProps['items'] =
      role === 'finance'
        ? [
            { key: '/admin/finance', icon: <AccountBookOutlined />, label: '财务' },
            { key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '资金审核' },
          ]
        : [
            { key: '/admin/dashboard', icon: <DashboardOutlined />, label: '概览' },
            { key: '/admin/orders', icon: <OrderedListOutlined />, label: '订单' },
            ...(role === 'support'
              ? []
              : [{ key: '/admin/finance', icon: <AccountBookOutlined />, label: '财务' }]),
            { key: '/admin/tasks', icon: <UnorderedListOutlined />, label: '任务' },
            { key: '/admin/worker-platform', icon: <TrophyOutlined />, label: '接单平台' },
            {
              key: '/admin/kuaishou-industry',
              icon: <SafetyCertificateOutlined />,
              label: '电子凭证',
            },
            {
              key: '/admin/cloudtentacles-records',
              icon: <FileSearchOutlined />,
              label: '发货记录',
            },
          ]

    const items: MenuProps['items'] = [
      {
        key: 'group-operation',
        type: 'group',
        label: '运营',
        children: operationItems,
      },
    ]

    if (devMockEnabled) {
      items.push({
        key: 'group-dev',
        type: 'group',
        label: '开发',
        children: [{ key: '/admin/dev-mock', icon: <BugOutlined />, label: '开发 Mock' }],
      })
    }

    if (role === 'admin') {
      items.push(
        {
          key: 'group-config',
          type: 'group',
          label: '配置',
          children: [
            { key: '/admin/users', icon: <TeamOutlined />, label: '后台用户' },
            { key: '/admin/platform-shops', icon: <SettingOutlined />, label: '平台配置' },
            { key: '/admin/platform-fulfillment', icon: <ShopOutlined />, label: '履约配置' },
          ],
        },
        {
          key: 'group-system',
          type: 'group',
          label: '系统',
          children: [{ key: '/admin/audit-logs', icon: <AuditOutlined />, label: '审计日志' }],
        },
      )
    }

    return items
  }, [devMockEnabled, role])

  async function submitLogout() {
    try {
      await logoutAdmin()
    } catch {
      // 服务端撤销失败时仍清理本地登录态，避免当前设备继续显示为已登录。
    } finally {
      clearAdminSession()
      message.success('已退出后台')
      void navigate('/admin/login', { replace: true })
    }
  }

  function toggleCollapsed() {
    const next = !collapsed
    setCollapsed(next)
    window.localStorage.setItem(SIDEBAR_COLLAPSED_KEY, next ? '1' : '0')
  }

  function toggleNotificationSound() {
    const next = !soundEnabled
    setSoundEnabled(next)
    window.localStorage.setItem(NOTIFICATION_SOUND_KEY, next ? '1' : '0')
    if (next) {
      playNotificationSound('default')
      message.success('通知声音已开启')
    } else {
      message.info('通知声音已关闭')
    }
  }

  function openNotification(notification: AdminNotification) {
    const actionPath = buildNotificationActionPath(notification)
    if (actionPath) {
      void navigate(actionPath)
    }
  }

  const notificationContent = (
    <div className="admin-notification-popover">
      {notifications.length === 0 ? (
        <Typography.Text type="secondary">暂无待处理事项</Typography.Text>
      ) : (
        <List
          size="small"
          dataSource={notifications.slice(0, 8)}
          renderItem={(notification) => (
            <List.Item
              className="admin-notification-item"
              onClick={() => openNotification(notification)}
            >
              <div>
                <Space size={6} wrap>
                  <Typography.Text strong>{notification.title}</Typography.Text>
                  {notification.priority === 'high' || notification.priority === 'urgent' ? (
                    <span className="admin-notification-priority">紧急</span>
                  ) : null}
                  {notification.reminderCount > 0 ? (
                    <Typography.Text type="secondary">
                      已催 {notification.reminderCount} 次
                    </Typography.Text>
                  ) : null}
                </Space>
                <Typography.Paragraph type="secondary" ellipsis={{ rows: 2 }}>
                  {notification.body}
                </Typography.Paragraph>
                <Typography.Text type="secondary" className="admin-notification-time">
                  {formatAdminDateTime(notification.updatedAt)}
                </Typography.Text>
              </div>
            </List.Item>
          )}
        />
      )}
    </div>
  )

  const selectedKey = resolveSelectedKey(location.pathname)

  return (
    <Layout className="admin-shell">
      <Layout.Sider
        width={232}
        collapsedWidth={72}
        collapsed={collapsed}
        className="admin-sider"
        trigger={null}
      >
        <div className="admin-sider-inner">
          <div className="admin-brand">
            <div className="admin-brand-copy">
              <strong>{collapsed ? 'OS' : '运营后台'}</strong>
              {!collapsed ? <span>订单与交付管理</span> : null}
            </div>
            <Button
              className="admin-collapse-button"
              aria-label={collapsed ? '展开导航' : '折叠导航'}
              icon={collapsed ? <MenuUnfoldOutlined /> : <MenuFoldOutlined />}
              onClick={toggleCollapsed}
            />
          </div>

          {!collapsed ? (
            <div className="admin-sidebar-profile">
              <Typography.Text strong>{username}</Typography.Text>
              <Typography.Text type="secondary">{roleLabel(role)}</Typography.Text>
              {expiresAt ? (
                <Typography.Text type="secondary">
                  {formatAdminDateTime(expiresAt)} 到期
                </Typography.Text>
              ) : null}
            </div>
          ) : null}

          <Menu
            mode="inline"
            inlineIndent={16}
            selectedKeys={[selectedKey]}
            items={menuItems}
            className="admin-menu"
            onClick={({ key }) => navigate(String(key))}
          />

          <div className="admin-sider-footer">
            <Button
              block={!collapsed}
              icon={<LogoutOutlined />}
              onClick={submitLogout}
              aria-label="退出登录"
            >
              {collapsed ? null : '退出登录'}
            </Button>
          </div>
        </div>
      </Layout.Sider>
      <Layout className="admin-main-shell">
        <Layout.Header className="admin-topbar">
          <Space className="admin-user" size={12}>
            <Tooltip title={soundEnabled ? '关闭通知声音' : '开启通知声音'}>
              <Button
                type="text"
                aria-label={soundEnabled ? '关闭通知声音' : '开启通知声音'}
                icon={<SoundOutlined className={soundEnabled ? '' : 'admin-sound-muted'} />}
                onClick={toggleNotificationSound}
              />
            </Tooltip>
            <Popover
              content={notificationContent}
              title="待处理事项"
              trigger="click"
              placement="bottomRight"
            >
              <Badge count={pendingNotificationCount} size="small" overflowCount={99}>
                <Button type="text" aria-label="待处理事项" icon={<BellOutlined />} />
              </Badge>
            </Popover>
            <Dropdown
              menu={{
                items: [{ key: 'logout', label: '退出登录' }],
                onClick: () => {
                  void submitLogout()
                },
              }}
              placement="bottomRight"
            >
              <Button icon={<UserOutlined />}>账号</Button>
            </Dropdown>
          </Space>
        </Layout.Header>
        <Layout.Content className="admin-content">
          <Outlet />
        </Layout.Content>
      </Layout>
    </Layout>
  )
}

function applyAdminRealtimeEvent(
  event: RealtimeEvent,
  queryClient: ReturnType<typeof useQueryClient>,
) {
  if (event.type === 'admin_notification.changed' && isRecord(event.data)) {
    queryClient.setQueryData(['admin-notifications'], (current: unknown) => {
      if (!isRecord(current) || !isRecord(current.data)) return current
      const currentData = current.data as { items?: unknown[]; pendingCount?: number }
      const items = Array.isArray(currentData.items) ? currentData.items : []
      const notification = event.data as Record<string, unknown>
      const notificationId = Number(notification.notificationId || event.entityId)
      const nextItems =
        event.operation === 'remove'
          ? items.filter(
              (item) =>
                Number((item as { notificationId?: unknown })?.notificationId) !== notificationId,
            )
          : upsertById(items, notification, 'notificationId', true)
      return {
        ...current,
        data: {
          ...currentData,
          items: nextItems,
          pendingCount:
            event.operation === 'remove'
              ? Math.max(0, Number(currentData.pendingCount || 0) - 1)
              : currentData.pendingCount,
        },
      }
    })
  }

  if (event.type === 'work_order.changed' && event.scopes.includes('admin_work_orders')) {
    // 拼单明细是独立查询，工单列表快照更新时也要同步刷新当前打开的明细。
    void queryClient.invalidateQueries({
      queryKey: ['admin-worker-platform-order-sharing', event.entityId],
    })
    void queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-after-sales-cases'] })
    if (isRecord(event.data)) {
      const data = event.data
      patchAdminWorkOrderCaches(queryClient, { ...event, data })
    } else {
      void queryClient.invalidateQueries({ queryKey: ['admin-worker-platform-orders'] })
    }
  }

  if (
    event.type === 'worker_finance_request.changed' &&
    event.scopes.includes('admin_worker_finance') &&
    isRecord(event.data)
  ) {
    const data = event.data
    queryClient.setQueriesData(
      { queryKey: ['admin-worker-platform-finance-requests'] },
      (current: unknown) => patchFinanceRequestListEnvelope(current, { ...event, data }),
    )
  }

  if (
    event.type === 'order.changed' &&
    event.scopes.includes('admin_orders') &&
    isRecord(event.data)
  ) {
    const payload = event.data
    if (isRecord(payload.order)) {
      patchAdminOrderCaches(queryClient, { ...event, data: payload.order })
    }
    if (isRecord(payload.dashboard)) {
      queryClient.setQueryData(['admin-dashboard-summary'], (current: unknown) => {
        if (!isRecord(current)) return current
        return { ...current, data: payload.dashboard }
      })
    }
    if (Array.isArray(payload.tasks)) {
      patchAdminTaskCaches(queryClient, payload.tasks)
    }
  }
}

function patchAdminWorkOrderCaches(
  queryClient: ReturnType<typeof useQueryClient>,
  event: RealtimeEvent,
) {
  const item = event.data as {
    workOrderId?: unknown
    status?: unknown
    productName?: unknown
    platformOrderId?: unknown
    worker?: { workerId?: unknown } | null
  }
  for (const [queryKey, current] of queryClient.getQueriesData({
    queryKey: ['admin-worker-platform-orders'],
  })) {
    const statuses = String(queryKey[1] || '')
      .split(',')
      .filter(Boolean)
    const keyword = String(queryKey[2] || '')
    const workerId = Number(queryKey[3] || 0)
    const workerLevelIds = String(queryKey[4] || '')
      .split(',')
      .map((value) => Number(value))
      .filter(Boolean)
    const targetWorkOrderId = Number(queryKey[5] || 0)
    const page = Number(queryKey[6] || 1)
    // 等级筛选的匹配逻辑只在服务端（接单打手或拼单参与者等级），快照不含等级，
    // 无法在前端复现：已有行视为匹配（拉取时已通过服务端过滤），但不再插入新行。
    const levelFilterActive = workerLevelIds.length > 0
    const matches =
      (!statuses.length || statuses.includes(String(item.status || ''))) &&
      matchesWorkOrderKeyword(item, keyword) &&
      (!workerId || workerId === Number(item.worker?.workerId || 0)) &&
      (!targetWorkOrderId || targetWorkOrderId === Number(item.workOrderId || 0))
    const shouldInsert = event.operation === 'upsert' && page === 1 && matches && !levelFilterActive
    const operation = event.operation === 'remove' || !matches ? 'remove' : 'upsert'
    queryClient.setQueryData(
      queryKey,
      patchWorkOrderListEnvelope(current, { ...event, operation }, shouldInsert),
    )
  }
}

function patchAdminTaskCaches(queryClient: ReturnType<typeof useQueryClient>, tasks: unknown[]) {
  queryClient.setQueriesData({ queryKey: ['admin-tasks'] }, (current: unknown) => {
    if (!isRecord(current) || !isRecord(current.data) || !Array.isArray(current.data.items)) {
      return current
    }
    const items = current.data.items as unknown[]
    const nextItems = items.map((item) => {
      const taskId = Number((item as { taskId?: unknown }).taskId || 0)
      return (
        tasks.find((task) => Number((task as { taskId?: unknown }).taskId || 0) === taskId) || item
      )
    })
    return { ...current, data: { ...current.data, items: nextItems } }
  })
}

function patchAdminOrderCaches(
  queryClient: ReturnType<typeof useQueryClient>,
  event: RealtimeEvent,
) {
  const item = event.data as { orderId?: unknown; platformOrderId?: unknown; payStatus?: unknown }
  for (const [queryKey, current] of queryClient.getQueriesData({ queryKey: ['admin-orders'] })) {
    const params = isRecord(queryKey[1]) ? queryKey[1] : {}
    const page = Number(params.page || 1)
    const matches =
      (!String(params.platformOrderId || '').trim() ||
        String(item.platformOrderId || '') === String(params.platformOrderId || '').trim()) &&
      (!String(params.payStatus || '').trim() ||
        String(item.payStatus || '') === String(params.payStatus || '').trim())
    const shouldInsert = event.operation === 'upsert' && page === 1 && matches && !params.skuCode
    const operation = event.operation === 'remove' || !matches ? 'remove' : 'upsert'
    queryClient.setQueryData(
      queryKey,
      patchAdminOrderListEnvelope(current, { ...event, operation }, shouldInsert),
    )
  }
}

function patchAdminOrderListEnvelope(current: unknown, event: RealtimeEvent, insert = false) {
  if (!isRecord(current) || !isRecord(current.data) || !Array.isArray(current.data.items)) {
    return current
  }
  const id = Number((event.data as { orderId?: unknown }).orderId || event.entityId)
  const items = current.data.items as unknown[]
  const nextItems =
    event.operation === 'remove'
      ? items.filter((entry) => Number((entry as { orderId?: unknown })?.orderId) !== id)
      : upsertById(items, event.data, 'orderId', insert)
  return { ...current, data: { ...current.data, items: nextItems } }
}

function patchWorkOrderListEnvelope(current: unknown, event: RealtimeEvent, insert = false) {
  if (!isRecord(current) || !isRecord(current.data) || !Array.isArray(current.data.items)) {
    return current
  }
  const item = event.data
  const id = Number((item as { workOrderId?: unknown }).workOrderId || event.entityId)
  const items = current.data.items as unknown[]
  const nextItems =
    event.operation === 'remove'
      ? items.filter((entry) => Number((entry as { workOrderId?: unknown })?.workOrderId) !== id)
      : upsertById(items, item, 'workOrderId', insert)
  return { ...current, data: { ...current.data, items: nextItems } }
}

function patchFinanceRequestListEnvelope(current: unknown, event: RealtimeEvent) {
  if (!isRecord(current) || !isRecord(current.data) || !Array.isArray(current.data.items)) {
    return current
  }
  const id = Number((event.data as { requestId?: unknown }).requestId || event.entityId)
  const items = current.data.items as unknown[]
  const nextItems =
    event.operation === 'remove'
      ? items.filter((entry) => Number((entry as { requestId?: unknown })?.requestId) !== id)
      : upsertById(items, event.data, 'requestId')
  return { ...current, data: { ...current.data, items: nextItems } }
}

function upsertById(items: unknown[], nextItem: unknown, idKey: string, insert = false) {
  const id = Number((nextItem as Record<string, unknown>)[idKey] || 0)
  const index = items.findIndex(
    (item) => Number((item as Record<string, unknown>)?.[idKey] || 0) === id,
  )
  if (index < 0) return insert ? [nextItem, ...items] : items
  return items.map((item, itemIndex) => (itemIndex === index ? nextItem : item))
}

function matchesWorkOrderKeyword(
  item: { productName?: unknown; platformOrderId?: unknown },
  keyword: string,
) {
  const normalizedKeyword = keyword.trim().toLowerCase()
  if (!normalizedKeyword) return true
  return [item.productName, item.platformOrderId]
    .map((value) => String(value || '').toLowerCase())
    .some((value) => value.includes(normalizedKeyword))
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return Boolean(value && typeof value === 'object' && !Array.isArray(value))
}

type NotificationAlertKind =
  | 'withdraw'
  | 'recharge'
  | 'acceptance'
  | 'material'
  | 'reminder'
  | 'default'

function playNotificationSound(kind: NotificationAlertKind) {
  try {
    const audio = new AudioContext()
    const oscillator = audio.createOscillator()
    const gain = audio.createGain()
    oscillator.type = 'sine'
    const notes: Record<NotificationAlertKind, number[]> = {
      withdraw: [740, 988],
      recharge: [587, 740],
      acceptance: [660, 784],
      material: [523, 659],
      reminder: [880, 1047, 1319],
      default: [880],
    }
    const duration = 0.12
    const sequence = notes[kind]
    sequence.forEach((frequency, index) => {
      oscillator.frequency.setValueAtTime(frequency, audio.currentTime + index * duration)
    })
    gain.gain.setValueAtTime(0.08, audio.currentTime)
    gain.gain.exponentialRampToValueAtTime(0.001, audio.currentTime + sequence.length * duration)
    oscillator.connect(gain)
    gain.connect(audio.destination)
    oscillator.start()
    oscillator.stop(audio.currentTime + sequence.length * duration)
    oscillator.addEventListener('ended', () => void audio.close())
  } catch {
    // 部分浏览器禁止音频或不支持 Web Audio，不影响待办展示。
  }
}

function resolveSelectedKey(pathname: string) {
  if (pathname.startsWith('/admin/orders')) return '/admin/orders'
  if (pathname.startsWith('/admin/finance')) return '/admin/finance'
  if (pathname.startsWith('/admin/tasks')) return '/admin/tasks'
  if (pathname.startsWith('/admin/worker-platform')) return '/admin/worker-platform'
  if (pathname.startsWith('/admin/kuaishou-industry')) return '/admin/kuaishou-industry'
  if (pathname === '/admin/platform-shops') return '/admin/platform-shops'
  return pathname
}

function buildNotificationActionPath(notification: AdminNotification) {
  if (!notification.entityId) {
    return notification.actionPath
  }
  const [pathname, query = ''] = notification.actionPath.split('?')
  const searchParams = new URLSearchParams(query)
  if (notification.entityType === 'work_order') {
    searchParams.set('tab', 'orders')
    searchParams.set('workOrderId', String(notification.entityId))
  } else if (notification.entityType === 'worker_finance_request') {
    searchParams.set('tab', 'finance')
    searchParams.set('financeRequestId', String(notification.entityId))
  } else {
    return notification.actionPath
  }
  return `${pathname}?${searchParams.toString()}`
}

function roleLabel(role: string) {
  if (role === 'admin') return '管理员'
  if (role === 'operator') return '普通运营'
  if (role === 'finance') return '财务'
  return '客服'
}
