'use client'

import { useState, useEffect, useCallback, useRef, useMemo, type ReactNode } from 'react'
import { cn } from '@/lib/utils'
import { generateQrCodeDataUrl } from '@/lib/qrcode'
import { CustomSelect } from '@/components/ui/custom-select'
import { Input } from '@/components/ui/input'
import { Switch } from '@/components/ui/switch'
import { Button } from '@/components/ui/button'
import { Card } from '@/components/ui/card'
import { useI18n } from '@/components/providers/i18n-provider'
import { useImChannels } from '@/hooks/use-im-channels'
import { useSettings } from '@/hooks/use-settings'
import { useModels } from '@/hooks/use-models'
import { useWorkspaces } from '@/hooks/use-workspaces'
import { LayoutDashboard, Send, MessageCircle, Gamepad2, MessageSquare, Plus, Trash2, ChevronDown, ChevronRight, UserPlus } from 'lucide-react'
import type { ImChannel, ImChannelType, ImDmPolicy, ImGroupPolicy, ImTriggerMode } from '@/lib/types'
import type { LucideIcon } from 'lucide-react'
import { BUILTIN_IM_CHANNEL_TYPES } from '@/lib/im-channel-catalog'
import { TeamView } from './team-view'

type PlatformPage = `platform:${ImChannelType}`
// A SubPage is either a known static page, a platform page, or a specific
// channel id (an im_channels row). The previous `| string` erased type
// narrowing for free; we keep the union closed on the static members and
// only leave a template literal for the dynamic channel id.
type SubPage = 'overview' | 'team' | PlatformPage | `channel:${string}`

interface PlatformMeta {
  label: string
  Icon: LucideIcon
  color: string
  credentialFields: { key: string; label: string; placeholder: string; hint?: string; type?: string }[]
}

const PLATFORM_META: Record<ImChannelType, PlatformMeta> = {
  telegram: {
    label: 'im.platformName.telegram',
    Icon: Send,
    color: 'text-indigo',
    credentialFields: [
      { key: 'bot_token', label: 'Bot Token', placeholder: '123456:ABC-DEF1234ghIkl-zyx57W2v...', type: 'password' },
      { key: 'chat_id', label: 'Chat ID', placeholder: '-1001234567890' },
    ],
  },
  feishu: {
    label: 'im.platformName.feishu',
    Icon: MessageCircle,
    color: 'text-green',
    credentialFields: [
      { key: 'app_id', label: 'App ID', placeholder: 'cli_xxxxxxxxxx' },
      { key: 'app_secret', label: 'App Secret', placeholder: 'xxxxxxxxxxxxxxxxxxxxxxxx', type: 'password' },
      { key: 'platform', label: 'Platform', placeholder: 'feishu or lark' },
    ],
  },
  discord: {
    label: 'im.platformName.discord',
    Icon: Gamepad2,
    color: 'text-coral',
    credentialFields: [
      { key: 'bot_token', label: 'Bot Token', placeholder: 'MTIzNDU2...XXXXXX.XXXXXXXXXX', type: 'password' },
      { key: 'server_id', label: 'Server ID', placeholder: '123456789012345678' },
      { key: 'channel_id', label: 'Default Channel ID', placeholder: '123456789012345678' },
    ],
  },
  weixin: {
    label: 'im.platformName.weixin',
    Icon: MessageSquare,
    color: 'text-green',
    credentialFields: [
      { key: 'appid', label: 'App ID', placeholder: 'wx...' },
      { key: 'secret', label: 'App Secret', placeholder: 'xxxxxxxxxxxxxxxxxxxxxxxx', type: 'password' },
    ],
  },
  dingtalk: {
    label: 'im.platformName.dingtalk',
    Icon: MessageCircle,
    color: 'text-indigo',
    credentialFields: [
      { key: 'client_id', label: 'Client ID', placeholder: 'dingxxxxxxxxxxxxxxxx' },
      { key: 'client_secret', label: 'Client Secret', placeholder: 'xxxxxxxxxxxxxxxxxxxxxxxx', type: 'password' },
      { key: 'endpoint', label: 'Endpoint', placeholder: 'https://api.dingtalk.com' },
      { key: 'chatbot_user_id', label: 'Bot User ID', placeholder: 'Optional, for group @ mention detection' },
    ],
  },
  wecom: {
    label: 'im.platformName.wecom',
    Icon: MessageSquare,
    color: 'text-green',
    credentialFields: [
      { key: 'bot_id', label: 'Bot ID', placeholder: 'xxxxxxxxxxxxxxxx' },
      { key: 'secret', label: 'Secret', placeholder: 'xxxxxxxxxxxxxxxxxxxxxxxx', type: 'password' },
    ],
  },
}

const COMMANDS = [
  '/new',
  '/bind <id>',
  '/sessions',
  '/clear',
  '/compact',
  '/projects',
  '/switch <name>',
  '/newproject <path>',
  '/model [name]',
  '/mode [confirm|full]',
  '/status',
  '/stop',
  '/help',
]

function normalizeQrCodeSrc(value?: string | null): string | null {
  if (!value) return null

  const trimmed = value.trim()
  if (!trimmed) return null

  if (
      trimmed.startsWith('data:image/') ||
      trimmed.startsWith('http://') ||
      trimmed.startsWith('https://') ||
      trimmed.startsWith('blob:')
  ) {
    return trimmed
  }

  if (trimmed.startsWith('//')) {
    return `https:${trimmed}`
  }

  if (/^[A-Za-z0-9+/=\r\n]+$/.test(trimmed)) {
    return `data:image/png;base64,${trimmed.replace(/\s+/g, '')}`
  }

  return trimmed
}

function normalizeQrStatus(status?: string | null): 'pending' | 'scanned' | 'confirmed' | 'expired' {
  if (status === 'confirmed' || status === 'expired') return status
  if (status === 'scaned' || status === 'scanned') return 'scanned'
  return 'pending'
}

export function ImView() {
  const { t } = useI18n()
  const { channels, updateChannel, createChannel, deleteChannel, error: channelsError, refreshChannels } = useImChannels()
  const [activePage, setActivePage] = useState<SubPage>('overview')
  const [expandedTypes, setExpandedTypes] = useState<Set<ImChannelType>>(new Set())
  const [showCreateMenu, setShowCreateMenu] = useState(false)
  const createMenuRef = useRef<HTMLDivElement>(null)

  // 自动展开当前选中实例所在的平台
  useEffect(() => {
    const activeChannel = channels.find((c) => c.id === activePage)
    if (activeChannel) {
      setExpandedTypes((prev) => {
        const next = new Set(prev)
        next.add(activeChannel.type)
        return next
      })
    }
  }, [activePage, channels])

  // 点击外部关闭创建菜单
  useEffect(() => {
    if (!showCreateMenu) return
    const handler = (e: MouseEvent) => {
      if (createMenuRef.current && !createMenuRef.current.contains(e.target as Node)) {
        setShowCreateMenu(false)
      }
    }
    document.addEventListener('mousedown', handler)
    return () => document.removeEventListener('mousedown', handler)
  }, [showCreateMenu])

  useEffect(() => {
    if (activePage === 'overview') return
    if (activePage === 'team') return
    if (activePage.startsWith('platform:')) return
    if (!channels.some((c) => c.id === activePage)) {
      setActivePage('overview')
    }
  }, [activePage, channels])

  const handleCreate = useCallback(async (type: ImChannelType, name?: string) => {
    const created = await createChannel(type, name)
    setActivePage(created.id as SubPage)
    setExpandedTypes((prev) => {
      const next = new Set(prev)
      next.add(type)
      return next
    })
    return created
  }, [createChannel])

  const toggleExpand = useCallback((type: ImChannelType) => {
    setExpandedTypes((prev) => {
      const next = new Set(prev)
      if (next.has(type)) {
        next.delete(type)
      } else {
        next.add(type)
      }
      return next
    })
  }, [])

  const activeChannel = channels.find((c) => c.id === activePage)
  const activePlatformType = activePage.startsWith('platform:')
      ? (activePage.slice('platform:'.length) as ImChannelType)
      : null
  const activePlatformChannel = activePlatformType
      ? channels.find((c) => c.type === activePlatformType)
      : undefined

  return (
      <div className="flex h-full">
        <div className="w-[260px] shrink-0 bg-surface-1 border-r border-hairline flex flex-col pt-4 overflow-y-auto min-h-0">
          <span className="px-4 text-[11px] font-semibold text-ink-tertiary tracking-wider uppercase">{t('im.bridgeTitle')}</span>

          <nav className="flex flex-col gap-0.5 px-2 mt-2">
            <button
                onClick={() => setActivePage('overview')}
                className={cn(
                    'flex items-center gap-2 h-9 px-3 rounded-md text-[13px] transition-colors text-left',
                    activePage === 'overview' ? 'bg-surface-3 text-ink font-medium' : 'text-ink-muted hover:bg-surface-2 hover:text-ink'
                )}
            >
              <LayoutDashboard className={cn('w-4 h-4', activePage === 'overview' ? 'text-brand-primary' : 'text-ink-tertiary')} />
              {t('im.overview')}
            </button>

            {/* ── Team Management nav item ── */}
            <button
                onClick={() => setActivePage('team')}
                className={cn(
                    'flex items-center gap-2 h-9 px-3 rounded-md text-[13px] transition-colors text-left',
                    activePage === 'team' ? 'bg-surface-3 text-ink font-medium' : 'text-ink-muted hover:bg-surface-2 hover:text-ink'
                )}
            >
              <UserPlus className={cn('w-4 h-4', activePage === 'team' ? 'text-brand-primary' : 'text-ink-tertiary')} />
              {t('im.team')}
            </button>
          </nav>

          <div className="px-4 pt-4 pb-2 text-[11px] font-semibold text-ink-tertiary tracking-wider uppercase">{t('im.platforms')}</div>
          <div className="px-2 space-y-0.5 flex-1">
            {BUILTIN_IM_CHANNEL_TYPES.map((type) => {
              const meta = PLATFORM_META[type]
              const Icon = meta.Icon
              const instances = channels.filter((c) => c.type === type)
              const isExpanded = expandedTypes.has(type)
              const hasActiveInstance = instances.some((c) => c.id === activePage)
              const isActivePlatform = activePage === `platform:${type}` || hasActiveInstance

              return (
                  <div key={type}>
                    <div
                        className={cn(
                            'flex items-center h-10 px-3 rounded-md text-[13px] transition-colors',
                            isActivePlatform && !hasActiveInstance ? 'bg-surface-3 text-ink font-medium' : 'text-ink-muted'
                        )}
                    >
                      <button
                          onClick={() => toggleExpand(type)}
                          className="flex items-center gap-2 flex-1 text-left min-w-0"
                      >
                        <Icon className={cn('w-4 h-4 shrink-0', isActivePlatform ? meta.color : 'text-ink-tertiary')} />
                        <span className="truncate">{t(meta.label)}</span>
                        <span className={cn('text-[11px] ml-auto shrink-0', isActivePlatform ? 'text-ink-muted' : 'text-ink-tertiary')}>
                          {instances.length}
                        </span>
                        <ChevronDown className={cn('w-3.5 h-3.5 shrink-0 transition-transform', isExpanded ? 'rotate-180' : '')} />
                      </button>
                      <button
                          onClick={() => { setShowCreateMenu(false); void handleCreate(type) }}
                          className="ml-1 w-6 h-6 flex items-center justify-center rounded hover:bg-surface-3 shrink-0 transition-colors"
                          title={t('im.newBotTitle').replace('{platform}', t(meta.label))}
                      >
                        <Plus className="w-3.5 h-3.5" />
                      </button>
                    </div>

                    {isExpanded && (
                        <div className="mt-0.5 space-y-0.5">
                          {instances.map((channel) => (
                              <button
                                  key={channel.id}
                                  onClick={() => setActivePage(channel.id as SubPage)}
                                  className={cn(
                                      'w-full flex items-center gap-2 h-9 pl-8 pr-3 rounded-md text-[13px] transition-colors text-left',
                                      activePage === channel.id ? 'bg-surface-3 text-ink font-medium' : 'text-ink-muted hover:bg-surface-2 hover:text-ink'
                                  )}
                              >
                                <span className={cn('w-2 h-2 rounded-full shrink-0',
                                    channel.status === 'connected' ? 'bg-green' :
                                    channel.status === 'error' ? 'bg-coral' :
                                    'bg-ink-tertiary'
                                )} />
                                <span className="truncate">{channel.name || t(meta.label)}</span>
                              </button>
                          ))}
                          {instances.length === 0 && (
                              <button
                                  onClick={() => setActivePage(`platform:${type}` as PlatformPage)}
                                  className="w-full flex items-center gap-2 h-9 pl-8 pr-3 rounded-md text-[12px] text-ink-tertiary hover:bg-surface-2 hover:text-ink transition-colors text-left"
                              >
                                <span className="truncate">{t('im.notConfigured')}</span>
                              </button>
                          )}
                        </div>
                    )}
                  </div>
              )
            })}
          </div>

          <div className="px-4 py-3 relative">
            <button
                onClick={() => setShowCreateMenu(!showCreateMenu)}
                className={cn(
                    'w-full flex items-center justify-center gap-2 h-9 px-3 rounded-md text-[13px] transition-colors',
                    'bg-surface-2 text-ink-muted hover:bg-surface-3 hover:text-ink border border-hairline'
                )}
            >
              <Plus className="w-4 h-4" />
              {t('im.addBot')}
            </button>
            {showCreateMenu && (
                <div
                    ref={createMenuRef}
                    className="absolute bottom-full left-4 right-4 mb-1 bg-surface-1 border border-hairline rounded-lg shadow-forge-lg py-1 z-50"
                >
                  {BUILTIN_IM_CHANNEL_TYPES.map((type) => {
                    const meta = PLATFORM_META[type]
                    const Icon = meta.Icon
                    return (
                        <button
                            key={type}
                            onClick={() => { setShowCreateMenu(false); void handleCreate(type) }}
                            className="w-full flex items-center gap-2 px-3 py-2 text-[13px] text-ink-muted hover:bg-surface-2 hover:text-ink transition-colors text-left"
                        >
                          <Icon className="w-4 h-4 shrink-0" />
                          <span>{t(meta.label)}</span>
                        </button>
                    )
                  })}
                </div>
            )}
          </div>
        </div>

        <div className="flex-1 overflow-y-auto p-6 space-y-6">
          {channelsError && (
              <div className="flex items-center justify-between gap-3 px-4 py-3 rounded-lg bg-coral/10 border border-coral/30 text-coral text-[13px]">
                <span>{t('im.error')}: {channelsError}</span>
                <button className="text-[12px] underline hover:text-coral/80" onClick={() => void refreshChannels()}>{t('common.confirm')}</button>
              </div>
          )}
          {activePage === 'overview' ? (
              <OverviewPage channels={channels} onToggle={updateChannel} onNavigate={setActivePage} onCreate={handleCreate} />
          ) : activePage === 'team' ? (
              <TeamView />
          ) : activePlatformType ? (
              <ChannelPage
                  key={activePlatformType}
                  channel={activePlatformChannel}
                  draftType={activePlatformChannel ? undefined : activePlatformType}
                  onCreate={handleCreate}
                  onUpdate={updateChannel}
                  onDelete={async () => setActivePage('overview')}
              />
          ) : (
              <ChannelPage
                  key={activeChannel?.id || 'missing'}
                  channel={activeChannel}
                  onUpdate={updateChannel}
                  onDelete={async (id) => {
                    await deleteChannel(id)
                    setActivePage('overview')
                  }}
              />
          )}
        </div>
      </div>
  )
}

function OverviewPage({
  channels,
  onToggle,
  onNavigate,
  onCreate,
}: {
  channels: ImChannel[]
  onToggle: (id: string, updates: Record<string, unknown>) => Promise<ImChannel>
  onNavigate: (page: SubPage) => void
  onCreate: (type: ImChannelType) => Promise<ImChannel>
}) {
  const { t } = useI18n()
  const { get, updateSettings } = useSettings()
  const { models } = useModels()
  const { workspaces } = useWorkspaces()
  const [filter, setFilter] = useState<'all' | ImChannelType>('all')
  const defaultModelFallback = models.find((m) => m.status === 'available')?.id || 'claude-sonnet-4-6'

  const stats = useMemo(() => {
    const active = channels.filter((c) => c.status === 'connected').length
    const errors = channels.filter((c) => c.status === 'error').length
    const inactive = channels.filter((c) => c.status === 'disconnected' || !c.enabled).length
    return { active, errors, inactive }
  }, [channels])

  const grouped = useMemo(() => {
    return BUILTIN_IM_CHANNEL_TYPES.map((type) => ({
      type,
      meta: PLATFORM_META[type],
      instances: channels.filter((c) => c.type === type),
    }))
  }, [channels])

  const visibleGroups = useMemo(() => {
    if (filter === 'all') return grouped
    return grouped.filter((g) => g.type === filter)
  }, [grouped, filter])

  const getWorkspaceLabel = (channel: ImChannel) => {
    if (!channel.defaultWorkspaceId) return '—'
    const ws = workspaces.find((w) => w.id === channel.defaultWorkspaceId)
    return ws ? ws.path : '—'
  }

  return (
      <div className="space-y-6">
        <div>
          <h1 className="text-[22px] font-semibold text-ink font-heading tracking-tight">{t('im.bridgeTitle')}</h1>
          <p className="text-[14px] text-ink-muted mt-1">{t('im.bridgeManageDesc')}</p>
        </div>

        {/* Stats */}
        <div className="grid grid-cols-3 gap-3">
          <div className={cn('flex flex-col items-center gap-1 p-4 rounded-[10px] border', stats.active > 0 ? 'bg-surface-1 border-hairline' : 'bg-surface-1 border-hairline opacity-60')}>
            <span className="text-[28px] font-semibold text-ink font-heading">{stats.active}</span>
            <span className="text-[12px] text-ink-muted">Active</span>
          </div>
          <div className={cn('flex flex-col items-center gap-1 p-4 rounded-[10px] border transition-colors', stats.errors > 0 ? 'bg-coral/5 border-coral/30' : 'bg-surface-1 border-hairline opacity-60')}>
            <span className={cn('text-[28px] font-semibold font-heading', stats.errors > 0 ? 'text-coral' : 'text-ink')}>{stats.errors}</span>
            <span className={cn('text-[12px]', stats.errors > 0 ? 'text-coral' : 'text-ink-muted')}>Errors</span>
          </div>
          <div className="flex flex-col items-center gap-1 p-4 rounded-[10px] bg-surface-1 border border-hairline opacity-60">
            <span className="text-[28px] font-semibold text-ink font-heading">{stats.inactive}</span>
            <span className="text-[12px] text-ink-muted">Inactive</span>
          </div>
        </div>

        <Card className="space-y-4">
          <h2 className="text-[15px] font-semibold text-ink">{t('im.globalBridgeDefaults')}</h2>
          <p className="text-[12px] text-ink-tertiary -mt-2">{t('im.globalBridgeDefaultsDesc')}</p>
          <div className="grid grid-cols-2 gap-4">
            <Field label={t('im.fallbackWorkspace')}>
              <CustomSelect
                  size="sm"
                  value={get('bridge_default_work_dir', '')}
                  onChange={(v) => updateSettings({ bridge_default_work_dir: v })}
                  options={[
                    { value: '', label: t('im.autoSelectWorkspace') },
                    ...workspaces.map((ws) => ({ value: ws.path, label: ws.path })),
                  ]}
              />
            </Field>
            <Field label={t('im.fallbackModel')}>
              <CustomSelect
                  size="sm"
                  value={get('bridge_default_model', defaultModelFallback)}
                  onChange={(v) => updateSettings({ bridge_default_model: v })}
                  options={models.map((m) => ({ value: m.id, label: m.label }))}
              />
            </Field>
          </div>
        </Card>

        <Card className="space-y-4">
          <div className="flex items-center justify-between">
            <h2 className="text-[15px] font-semibold text-ink">{t('im.instances')}</h2>
            <span className="text-[12px] text-ink-tertiary">{t('im.instancesActive').replace('{count}', String(channels.length))}</span>
          </div>

          <div className="flex flex-wrap gap-1.5">
            <button
                onClick={() => setFilter('all')}
                className={cn(
                    'px-2.5 py-1 rounded-md text-[12px] transition-colors',
                    filter === 'all' ? 'bg-surface-3 text-ink font-medium' : 'text-ink-muted hover:bg-surface-2'
                )}
            >
              {t('im.filterAll').replace('{count}', String(channels.length))}
            </button>
            {grouped.map(({ type, meta, instances }) => (
                <button
                    key={type}
                    onClick={() => setFilter(filter === type ? 'all' : type)}
                    className={cn(
                        'px-2.5 py-1 rounded-md text-[12px] transition-colors',
                        filter === type ? 'bg-surface-3 text-ink font-medium' : 'text-ink-muted hover:bg-surface-2'
                    )}
                >
                  {t(meta.label)} {instances.length}
                </button>
            ))}
          </div>

          <div className="space-y-4">
            {visibleGroups.map(({ type, meta, instances }) => {
              const Icon = meta.Icon
              return (
                  <div key={type}>
                    <div className="flex items-center gap-2 pb-2 mb-2 border-b border-hairline">
                      <Icon className={cn('w-4 h-4', meta.color)} />
                      <span className="text-[13px] font-medium text-ink">{t(meta.label)}</span>
                      {instances.length > 0 ? (
                          <span className="text-[11px] text-ink-tertiary">— {instances.length} instance{instances.length > 1 ? 's' : ''}</span>
                      ) : (
                          <span className="text-[11px] text-ink-tertiary">— {t('im.notConfigured')}</span>
                      )}
                    </div>

                    {instances.length > 0 ? (
                        <div className="space-y-1">
                          <div className="grid grid-cols-[1fr_1fr_100px_60px] gap-3 px-3 py-1 text-[11px] font-medium text-ink-tertiary uppercase tracking-wider">
                            <span>Name</span>
                            <span>Workspace</span>
                            <span>Status</span>
                            <span className="text-right">On</span>
                          </div>
                          {instances.map((channel) => (
                              <div
                                  key={channel.id}
                                  className="grid grid-cols-[1fr_1fr_100px_60px] gap-3 px-3 py-2 rounded-md items-center hover:bg-surface-2 cursor-pointer transition-colors"
                                  onClick={() => onNavigate(channel.id as SubPage)}
                              >
                                <span className="text-[13px] text-ink truncate font-medium">{channel.name || t(meta.label)}</span>
                                <span className="text-[12px] text-ink-muted truncate" title={getWorkspaceLabel(channel)}>
                                  {getWorkspaceLabel(channel)}
                                </span>
                                <div className="flex items-center gap-1.5">
                                  <span className={cn('w-2 h-2 rounded-full shrink-0',
                                      channel.status === 'connected' ? 'bg-green' :
                                      channel.status === 'error' ? 'bg-coral' :
                                      'bg-ink-tertiary'
                                  )} />
                                  <span className={cn('text-[12px]',
                                      channel.status === 'connected' ? 'text-green' :
                                      channel.status === 'error' ? 'text-coral' :
                                      'text-ink-tertiary'
                                  )}>
                                    {channel.status === 'connected' ? 'Conn.' :
                                     channel.status === 'error' ? 'Error' :
                                     channel.status === 'disconnected' ? 'Disc.' :
                                     '—'}
                                  </span>
                                </div>
                                <div className="flex justify-end" onClick={(e) => e.stopPropagation()}>
                                  <Switch
                                      checked={!!channel.enabled}
                                      onChange={() => {
                                        void onToggle(channel.id, { enabled: !channel.enabled })
                                      }}
                                      aria-label={channel.name || t(meta.label)}
                                  />
                                </div>
                              </div>
                          ))}
                        </div>
                    ) : (
                        <div className="pl-7">
                          <Button
                              variant="secondary"
                              size="sm"
                              onClick={() => onCreate(type)}
                          >
                            <Plus className="w-3.5 h-3.5" />
                            {t('im.newBotTitle').replace('{platform}', t(meta.label))}
                          </Button>
                        </div>
                    )}
                  </div>
              )
            })}
          </div>
        </Card>
      </div>
  )
}

function ChannelPage({
  channel,
  draftType,
  onCreate,
  onUpdate,
  onDelete,
}: {
  channel?: ImChannel
  draftType?: ImChannelType
  onCreate?: (type: ImChannelType, name?: string) => Promise<ImChannel>
  onUpdate: (id: string, updates: Record<string, unknown>) => Promise<ImChannel>
  onDelete: (id: string) => Promise<void>
}) {
  const { t } = useI18n()
  const { models } = useModels()
  const { workspaces } = useWorkspaces()
  const [name, setName] = useState('')
  const [creds, setCreds] = useState<Record<string, string>>({})
  const [cardConfig, setCardConfig] = useState<{ replyMode: string; footerStatus: boolean; footerElapsed: boolean; footerModel: boolean }>({
    replyMode: 'auto',
    footerStatus: true,
    footerElapsed: true,
    footerModel: true,
  })
  const [cardStreamingExpanded, setCardStreamingExpanded] = useState(false)
  const [defaultWorkspaceId, setDefaultWorkspaceId] = useState('')
  const [defaultModel, setDefaultModel] = useState('')
  const [dmPolicy, setDmPolicy] = useState<ImDmPolicy>('open')
  const [groupPolicy, setGroupPolicy] = useState<ImGroupPolicy>('open')
  const [triggerMode, setTriggerMode] = useState<ImTriggerMode>('mention')
  const [senderWl, setSenderWl] = useState('')
  const [groupWl, setGroupWl] = useState('')
  const [dirty, setDirty] = useState(false)
  const [saving, setSaving] = useState(false)
  const [saveError, setSaveError] = useState<string | null>(null)
  const [savedAt, setSavedAt] = useState<number | null>(null)
  const [deleteError, setDeleteError] = useState<string | null>(null)
  const [connecting, setConnecting] = useState(false)
  const [disconnecting, setDisconnecting] = useState(false)
  const [connectError, setConnectError] = useState<string | null>(null)
  const [connectSuccess, setConnectSuccess] = useState(false)
  const [showCommands, setShowCommands] = useState(false)

  // DingTalk QR auth state
  const [dingtalkQrCodeUrl, setDingtalkQrCodeUrl] = useState<string | null>(null)
  const [dingtalkQrCodeImageUrl, setDingtalkQrCodeImageUrl] = useState<string | null>(null)
  const [dingtalkQrCodeKey, setDingtalkQrCodeKey] = useState<string | null>(null)
  const [dingtalkQrPolling, setDingtalkQrPolling] = useState(false)
  const [dingtalkQrStatus, setDingtalkQrStatus] = useState<string | null>(null)
  const [dingtalkQrError, setDingtalkQrError] = useState<string | null>(null)
  const [dingtalkQrInterval, setDingtalkQrInterval] = useState(5000)
  const [dingtalkQrStartedAt, setDingtalkQrStartedAt] = useState<number | null>(null)
  const [dingtalkQrExpiresIn, setDingtalkQrExpiresIn] = useState<number>(300)
  const dingtalkErrorStreakRef = useRef(0)

  // WeChat QR login state
  const [weixinQrCodeUrl, setWeixinQrCodeUrl] = useState<string | null>(null)
  const [weixinQrCodeImageUrl, setWeixinQrCodeImageUrl] = useState<string | null>(null)
  const [weixinQrCodeKey, setWeixinQrCodeKey] = useState<string | null>(null)
  const [weixinQrPolling, setWeixinQrPolling] = useState(false)
  const [weixinQrStatus, setWeixinQrStatus] = useState<string | null>(null)
  const [weixinQrError, setWeixinQrError] = useState<string | null>(null)
  const [weixinQrStartedAt, setWeixinQrStartedAt] = useState<number | null>(null)
  const [weixinQrExpiresIn, setWeixinQrExpiresIn] = useState<number>(120)
  const weixinErrorStreakRef = useRef(0)

  // Feishu QR auth state
  const [feishuQrImageUrl, setFeishuQrImageUrl] = useState<string | null>(null)
  const [feishuQrPolling, setFeishuQrPolling] = useState(false)
  const [feishuQrStatus, setFeishuQrStatus] = useState<string | null>(null)
  const [feishuQrError, setFeishuQrError] = useState<string | null>(null)

  // WeCom CLI init state
  const [wecomCliQrDataUrl, setWecomCliQrDataUrl] = useState<string | null>(null)
  const [wecomCliPolling, setWecomCliPolling] = useState(false)
  const [wecomCliStatus, setWecomCliStatus] = useState<string | null>(null)
  const [wecomCliError, setWecomCliError] = useState<string | null>(null)

  // Guards against effect overwrites on non-id prop changes — see review A4.
  const lastChannelIdRef = useRef<string | undefined>(undefined)

  useEffect(() => {
    // Only reset the form when the channel identity changes (mount or switch).
    // Other prop updates (e.g. status flips to 'connected' after connect) must
    // NOT overwrite in-progress edits — see review A4.
    const channelId = channel?.id
    if (lastChannelIdRef.current === channelId && channelId !== undefined) return
    lastChannelIdRef.current = channelId
    const type = channel?.type || draftType
    if (!type) return
    const meta = PLATFORM_META[type]
    setName(channel?.name || t('im.botFallback').replace('{platform}', t(meta.label)))
    setCreds(channel?.credentials || {})
    // Load card config from credentials.card JSON string
    try {
      const cardJson = channel?.credentials?.card
      const cardObj = cardJson ? JSON.parse(cardJson as string) : {}
      // Legacy `streaming` (removed) mapped to replyMode for backward compat.
      const legacyStreaming = typeof cardObj.streaming === 'boolean' ? cardObj.streaming : null
      const replyMode = cardObj.replyMode ?? (legacyStreaming === false ? 'static' : 'auto')
      setCardConfig({
        replyMode,
        footerStatus: cardObj.footer?.status ?? true,
        footerElapsed: cardObj.footer?.elapsed ?? true,
        footerModel: cardObj.footer?.model ?? true,
      })
    } catch {
      setCardConfig({ replyMode: 'auto', footerStatus: true, footerElapsed: true, footerModel: true })
    }
    setDefaultWorkspaceId(channel?.defaultWorkspaceId || '')
    setDefaultModel(channel?.defaultModel || '')
    setDmPolicy(channel?.dmPolicy || 'open')
    setGroupPolicy(channel?.groupPolicy || 'open')
    setTriggerMode(channel?.triggerMode || 'mention')
    setSenderWl(channel?.senderWhitelist.join(', ') || '')
    setGroupWl(channel?.groupWhitelist.join(', ') || '')
    setDirty(false)
    setConnectError(null)
    setConnectSuccess(false)
    setDingtalkQrCodeUrl(null)
    setDingtalkQrCodeImageUrl(null)
    setDingtalkQrCodeKey(null)
    setDingtalkQrPolling(false)
    setDingtalkQrStatus(null)
    setDingtalkQrError(null)
    setDingtalkQrInterval(5000)
    setWeixinQrCodeUrl(null)
    setWeixinQrCodeImageUrl(null)
    setWeixinQrCodeKey(null)
    setWeixinQrPolling(false)
    setWeixinQrStatus(null)
    setWeixinQrError(null)
    setFeishuQrImageUrl(null)
    setFeishuQrPolling(false)
    setFeishuQrStatus(null)
    setFeishuQrError(null)
    setWecomCliQrDataUrl(null)
    setWecomCliPolling(false)
    setWecomCliStatus(null)
    setWecomCliError(null)
  }, [channel?.id, draftType])

  const channelType = channel?.type || draftType
  const meta = channelType ? PLATFORM_META[channelType] : null
  const isDraft = !channel && !!draftType
  // Draft-mode guard: block "Create Instance" until at least one field has input,
  // so we don't create empty placeholder channels on a misclick.
  const hasDraftInput = isDraft && (
    name.trim().length > 0
    || Object.values(creds).some((v) => v && v.trim().length > 0)
  )

  const updateCred = (key: string, value: string) => {
    setCreds((prev) => ({ ...prev, [key]: value }))
    setDirty(true)
  }

  const handleSave = useCallback(async () => {
    if (!channel && (!draftType || !onCreate)) return
    setSaving(true)
    setSaveError(null)
    try {
      let target = channel
      if (!target) {
        if (!draftType || !onCreate) return
        target = await onCreate(draftType, name)
      }
      // Embed card config into credentials for feishu channels
      const credsToSave = { ...creds }
      if (channelType === 'feishu') {
        credsToSave.card = JSON.stringify({
          replyMode: cardConfig.replyMode,
          footer: {
            status: cardConfig.footerStatus,
            elapsed: cardConfig.footerElapsed,
            model: cardConfig.footerModel,
          },
        })
      }
      await onUpdate(target.id, {
        name,
        credentials: credsToSave,
        default_workspace_id: defaultWorkspaceId,
        default_model: defaultModel,
        dm_policy: dmPolicy,
        group_policy: groupPolicy,
        trigger_mode: triggerMode,
        sender_whitelist: senderWl.split(',').map((s) => s.trim()).filter(Boolean),
        group_whitelist: groupWl.split(',').map((s) => s.trim()).filter(Boolean),
      })
      setDirty(false)
      setSavedAt(Date.now())
      // Auto-clear the "Saved" indicator after 1.5s.
      setTimeout(() => setSavedAt((prev) => (prev && Date.now() - prev >= 1400 ? null : prev)), 1500)
    } catch (err) {
      setSaveError(err instanceof Error ? err.message : t('im.saveFailed').trim())
    } finally {
      setSaving(false)
    }
  }, [channel, creds, cardConfig, channelType, defaultModel, defaultWorkspaceId, dmPolicy, draftType, groupPolicy, groupWl, name, onCreate, onUpdate, senderWl, triggerMode])

  const handleConnect = async (action: 'connect' | 'disconnect') => {
    if (!channel) return
    if (action === 'connect') setConnecting(true)
    if (action === 'disconnect') setDisconnecting(true)
    setConnectError(null)
    setConnectSuccess(false)
    try {
      const res = await fetch(`/api/im-channels/${channel.id}/connect`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action }),
      })
      if (!res.ok) {
        const data = await res.json()
        throw new Error(data.error || `${action} failed`)
      }
      await onUpdate(channel.id, { status: action === 'connect' ? 'connected' : 'disconnected' })
      if (action === 'connect') setConnectSuccess(true)
    } catch (err) {
      setConnectError(err instanceof Error ? err.message : `${action} failed`)
    } finally {
      setConnecting(false)
      setDisconnecting(false)
    }
  }

  // DingTalk QR auth handlers
  const handleStartDingtalkQr = async () => {
    if (!channel) return
    setDingtalkQrError(null)
    setDingtalkQrStatus(null)
    setDingtalkQrCodeUrl(null)
    setDingtalkQrCodeImageUrl(null)
    setDingtalkQrCodeKey(null)
    setDingtalkQrInterval(5000)
    setDingtalkQrStartedAt(null)
    setDingtalkQrExpiresIn(300)
    dingtalkErrorStreakRef.current = 0
    try {
      const initRes = await fetch(`/api/im-channels/${channel.id}/dingtalk-auth`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'init' }),
      })
      const initData = await initRes.json()
      if (!initRes.ok) {
        throw new Error(initData.error || 'Failed to init DingTalk auth')
      }
      const nonce = initData.nonce
      if (!nonce) {
        throw new Error('No nonce returned from DingTalk')
      }

      const beginRes = await fetch(`/api/im-channels/${channel.id}/dingtalk-auth`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'begin', nonce }),
      })
      const beginData = await beginRes.json()
      if (!beginRes.ok) {
        throw new Error(beginData.error || 'Failed to begin DingTalk auth')
      }
      const verificationUri = beginData.verification_uri_complete
      const deviceCode = beginData.device_code
      const userCode = beginData.user_code
      const interval = beginData.interval
      if (!verificationUri || !deviceCode) {
        throw new Error('No verification URI or device code returned')
      }

      const generatedQrCodeImageUrl = generateQrCodeDataUrl(verificationUri)
      setDingtalkQrCodeUrl(verificationUri)
      setDingtalkQrCodeImageUrl(generatedQrCodeImageUrl)
      setDingtalkQrCodeKey(`${deviceCode}|${userCode || ''}`)
      setDingtalkQrStatus('pending')
      if (interval && interval > 0) {
        setDingtalkQrInterval(interval * 1000)
      }
      if (beginData.expires_in && beginData.expires_in > 0) {
        setDingtalkQrExpiresIn(beginData.expires_in)
      }
      setDingtalkQrStartedAt(Date.now())
    } catch (err) {
      setDingtalkQrError(err instanceof Error ? err.message : 'DingTalk auth failed')
    }
  }

  const handlePollDingtalkQr = useCallback(async () => {
    if (!channel || !dingtalkQrCodeKey) return
    setDingtalkQrPolling(true)
    setDingtalkQrError(null)
    try {
      const deviceCode = dingtalkQrCodeKey.split('|')[0]
      const res = await fetch(`/api/im-channels/${channel.id}/dingtalk-auth`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'poll', device_code: deviceCode }),
      })
      const data = await res.json()
      if (!res.ok) {
        throw new Error(data.error || 'Failed to poll DingTalk auth status')
      }
      const nextStatus = normalizeQrStatus(data.status)
      setDingtalkQrStatus(nextStatus)
      dingtalkErrorStreakRef.current = 0
      if (nextStatus === 'confirmed' && data.client_id) {
        if (!data.client_secret) {
          throw new Error('Auth confirmed but client_secret is missing')
        }
        setCreds((prev) => ({
          ...prev,
          client_id: data.client_id,
          client_secret: data.client_secret,
        }))
        setDirty(true)
        setDingtalkQrCodeUrl(null)
        setDingtalkQrCodeImageUrl(null)
        setDingtalkQrCodeKey(null)
      }
    } catch (err) {
      dingtalkErrorStreakRef.current += 1
      if (dingtalkErrorStreakRef.current >= 3) {
        setDingtalkQrStatus('expired')
        setDingtalkQrError('连续多次轮询失败，请刷新二维码重试')
      } else {
        setDingtalkQrError(err instanceof Error ? err.message : 'DingTalk poll failed')
      }
    } finally {
      setDingtalkQrPolling(false)
    }
  }, [channel, dingtalkQrCodeKey])

  useEffect(() => {
    if (!channel || !dingtalkQrCodeKey || !dingtalkQrCodeUrl || creds.client_id) return
    if (dingtalkQrStatus === 'confirmed' || dingtalkQrStatus === 'expired') return

    if (dingtalkQrStartedAt && Date.now() - dingtalkQrStartedAt > dingtalkQrExpiresIn * 1000) {
      setDingtalkQrStatus('expired')
      setDingtalkQrCodeUrl(null)
      setDingtalkQrCodeImageUrl(null)
      setDingtalkQrCodeKey(null)
      return
    }

    const intervalId = window.setInterval(() => {
      void handlePollDingtalkQr()
    }, dingtalkQrInterval)

    return () => window.clearInterval(intervalId)
  }, [channel, creds.client_id, handlePollDingtalkQr, dingtalkQrCodeKey, dingtalkQrCodeUrl, dingtalkQrStatus, dingtalkQrInterval, dingtalkQrStartedAt, dingtalkQrExpiresIn])

  // WeChat QR login handlers
  const handleStartQrLogin = async () => {
    if (!channel) return
    setWeixinQrError(null)
    setWeixinQrStatus(null)
    setWeixinQrCodeUrl(null)
    setWeixinQrCodeImageUrl(null)
    setWeixinQrCodeKey(null)
    setWeixinQrStartedAt(null)
    setWeixinQrExpiresIn(120)
    weixinErrorStreakRef.current = 0
    try {
      const res = await fetch(`/api/im-channels/${channel.id}/weixin-login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'start' }),
      })
      const data = await res.json()
      if (!res.ok) {
        throw new Error(data.error || 'Failed to start QR login')
      }
      const normalizedQrCodeUrl = normalizeQrCodeSrc(data.qrcode_url)
      if (!normalizedQrCodeUrl) {
        throw new Error('QR code payload is empty')
      }
      const generatedQrCodeImageUrl = generateQrCodeDataUrl(normalizedQrCodeUrl)
      setWeixinQrCodeUrl(normalizedQrCodeUrl)
      setWeixinQrCodeImageUrl(generatedQrCodeImageUrl)
      setWeixinQrCodeKey(data.qrcode_key || null)
      setWeixinQrStatus('pending')
      if (data.expires_in && data.expires_in > 0) {
        setWeixinQrExpiresIn(data.expires_in)
      }
      setWeixinQrStartedAt(Date.now())
    } catch (err) {
      setWeixinQrError(err instanceof Error ? err.message : 'QR login failed')
    }
  }

  const handlePollQrStatus = useCallback(async () => {
    if (!channel || !weixinQrCodeKey) return
    setWeixinQrPolling(true)
    setWeixinQrError(null)
    try {
      const res = await fetch(`/api/im-channels/${channel.id}/weixin-login`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'poll', qrcode_key: weixinQrCodeKey }),
      })
      const data = await res.json()
      if (!res.ok) {
        throw new Error(data.error || 'Failed to poll QR status')
      }
      const nextStatus = normalizeQrStatus(data.status)
      setWeixinQrStatus(nextStatus)
      weixinErrorStreakRef.current = 0
      if (nextStatus === 'confirmed' && data.token) {
        const token = data.token as string
        const colonIdx = token.indexOf(':')
        setCreds((prev) => ({
          ...prev,
          token,
          appid: colonIdx !== -1 ? token.substring(0, colonIdx) : (data.ilink_bot_id || prev.appid),
          secret: colonIdx !== -1 ? token.substring(colonIdx + 1) : prev.secret,
          ...(data.baseurl ? { baseurl: data.baseurl } : {}),
          ...(data.ilink_user_id ? { ilink_user_id: data.ilink_user_id } : {}),
        }))
        setDirty(true)
        setWeixinQrCodeUrl(null)
        setWeixinQrCodeImageUrl(null)
        setWeixinQrCodeKey(null)
      }
    } catch (err) {
      weixinErrorStreakRef.current += 1
      if (weixinErrorStreakRef.current >= 3) {
        setWeixinQrStatus('expired')
        setWeixinQrError('连续多次轮询失败，请刷新二维码重试')
      } else {
        setWeixinQrError(err instanceof Error ? err.message : 'QR poll failed')
      }
    } finally {
      setWeixinQrPolling(false)
    }
  }, [channel, weixinQrCodeKey])

  useEffect(() => {
    if (!channel || !weixinQrCodeKey || !weixinQrCodeUrl || creds.token) return
    if (weixinQrStatus === 'confirmed' || weixinQrStatus === 'expired') return

    if (weixinQrStartedAt && Date.now() - weixinQrStartedAt > weixinQrExpiresIn * 1000) {
      setWeixinQrStatus('expired')
      setWeixinQrCodeUrl(null)
      setWeixinQrCodeImageUrl(null)
      setWeixinQrCodeKey(null)
      return
    }

    const intervalMs = weixinQrStatus === 'scanned' ? 1000 : 2000
    const intervalId = window.setInterval(() => {
      void handlePollQrStatus()
    }, intervalMs)

    return () => window.clearInterval(intervalId)
  }, [channel, creds.token, handlePollQrStatus, weixinQrCodeKey, weixinQrCodeUrl, weixinQrStatus, weixinQrStartedAt, weixinQrExpiresIn])

  // ---------------------------------------------------------------------------
  // Feishu QR auth handlers
  // ---------------------------------------------------------------------------

  const handleStartFeishuQr = async () => {
    if (!channel) return
    setFeishuQrError(null)
    setFeishuQrStatus(null)
    setFeishuQrImageUrl(null)
    try {
      const res = await fetch(`/api/im-channels/${channel.id}/feishu-auth`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'start', appName: name }),
      })
      const data = await res.json()
      if (!res.ok) {
        throw new Error(data.error || 'Failed to start Feishu auth')
      }
      const qrDataUrl = data.qrDataUrl
      if (!qrDataUrl) {
        throw new Error('No QR code data returned')
      }
      setFeishuQrImageUrl(qrDataUrl)
      setFeishuQrStatus('pending')
    } catch (err) {
      setFeishuQrError(err instanceof Error ? err.message : 'Feishu auth failed')
    }
  }

  const handlePollFeishuQr = useCallback(async () => {
    if (!channel) return
    setFeishuQrPolling(true)
    setFeishuQrError(null)
    try {
      const res = await fetch(`/api/im-channels/${channel.id}/feishu-auth`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'poll' }),
      })
      const data = await res.json()
      if (!res.ok) {
        throw new Error(data.error || 'Failed to poll Feishu auth status')
      }
      const nextStatus = data.status as string
      setFeishuQrStatus(nextStatus)
      if (nextStatus === 'confirmed' && data.appId && data.appSecret) {
        setCreds((prev) => ({
          ...prev,
          app_id: data.appId,
          app_secret: data.appSecret,
        }))
        setDirty(true)
        setFeishuQrImageUrl(null)
      }
    } catch (err) {
      setFeishuQrError(err instanceof Error ? err.message : 'Feishu poll failed')
    } finally {
      setFeishuQrPolling(false)
    }
  }, [channel])

  useEffect(() => {
    if (!channel || !feishuQrImageUrl || creds.app_id) return
    if (feishuQrStatus === 'confirmed' || feishuQrStatus === 'expired' || feishuQrStatus === 'denied') return

    const intervalId = window.setInterval(() => {
      void handlePollFeishuQr()
    }, 2000)

    return () => window.clearInterval(intervalId)
  }, [channel, creds.app_id, feishuQrImageUrl, feishuQrStatus, handlePollFeishuQr])

  // ---------------------------------------------------------------------------
  // WeCom CLI init handlers
  // ---------------------------------------------------------------------------

  const handleStartWecomCliInit = async () => {
    if (!channel) return
    setWecomCliError(null)
    setWecomCliStatus(null)
    setWecomCliQrDataUrl(null)
    try {
      const res = await fetch(`/api/im-channels/${channel.id}/wecom-cli`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'start' }),
      })
      const data = await res.json()
      if (!res.ok) {
        throw new Error(data.error || 'Failed to start WeCom CLI init')
      }
      const qrDataUrl = data.qrDataUrl
      if (!qrDataUrl) {
        throw new Error('No QR code data returned')
      }
      setWecomCliQrDataUrl(qrDataUrl)
      setWecomCliStatus('pending')
    } catch (err) {
      setWecomCliError(err instanceof Error ? err.message : 'WeCom CLI init failed')
    }
  }

  const handlePollWecomCli = useCallback(async () => {
    if (!channel) return
    setWecomCliPolling(true)
    setWecomCliError(null)
    try {
      const res = await fetch(`/api/im-channels/${channel.id}/wecom-cli`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'poll' }),
      })
      const data = await res.json()
      if (!res.ok) {
        throw new Error(data.error || 'Failed to poll WeCom CLI status')
      }
      const nextStatus = data.status as string
      setWecomCliStatus(nextStatus)
      if (nextStatus === 'completed' && data.configDir) {
        setCreds((prev) => ({
          ...prev,
          wecom_cli_config_dir: data.configDir,
        }))
        setDirty(true)
        setWecomCliQrDataUrl(null)
      }
    } catch (err) {
      setWecomCliError(err instanceof Error ? err.message : 'WeCom poll failed')
    } finally {
      setWecomCliPolling(false)
    }
  }, [channel])

  useEffect(() => {
    if (!channel || !wecomCliQrDataUrl) return
    if (wecomCliStatus === 'completed' || wecomCliStatus === 'failed' || wecomCliStatus === 'cancelled') return

    const intervalId = window.setInterval(() => {
      void handlePollWecomCli()
    }, 2000)

    return () => window.clearInterval(intervalId)
  }, [channel, wecomCliQrDataUrl, wecomCliStatus, handlePollWecomCli])

  const handleCancelWecomCli = useCallback(async () => {
    if (!channel) return
    try {
      await fetch(`/api/im-channels/${channel.id}/wecom-cli`, {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ action: 'cancel' }),
      })
    } catch (err) {
      // Best effort: surface the failure to the user and keep the QR visible
      // so they can retry the cancel — clearing state on failure would leave
      // the server-side init flow dangling without UI feedback.
      setWecomCliError(err instanceof Error ? err.message : 'Failed to cancel WeCom CLI init')
      return
    }
    setWecomCliQrDataUrl(null)
    setWecomCliStatus(null)
    setWecomCliError(null)
  }, [channel])

  if (!meta) {
    return <div className="text-ink-muted text-sm">{t('im.channelNotFound')}</div>
  }

  const statusColor = connecting || disconnecting ? 'bg-amber' : channel?.status === 'connected' ? 'bg-green' : channel?.status === 'error' ? 'bg-coral' : 'bg-ink-tertiary'
  const statusTextColor = connecting || disconnecting ? 'text-amber' : channel?.status === 'connected' ? 'text-green' : channel?.status === 'error' ? 'text-coral' : 'text-ink-tertiary'
  const statusText = connecting ? t('status.connecting') : disconnecting ? t('status.disconnecting') : channel?.status === 'connected' ? t('im.connected') : channel?.status === 'error' ? t('im.error') : channel ? t('im.disconnected') : t('im.notConfigured')

  return (
      <div className="space-y-6" onKeyDown={(e) => {
        if ((e.metaKey || e.ctrlKey) && e.key === 's') { e.preventDefault(); void handleSave() }
      }}>
        {/* 顶部操作栏 */}
        <div className="flex items-start justify-between gap-4">
          <div>
            <h1 className="text-[22px] font-semibold text-ink font-heading tracking-tight">
              {isDraft ? t('im.createBotTitle').replace('{platform}', t(meta.label)) : channel?.name || t('im.botFallback').replace('{platform}', t(meta.label))}
            </h1>
            <p className="text-[14px] text-ink-muted mt-1">
              {isDraft
                  ? t('im.createBotDesc').replace('{platform}', t(meta.label))
                  : t('im.editBotDesc').replace('{platform}', t(meta.label))}
            </p>
          </div>
          <div className="flex items-center gap-2 shrink-0">
            {isDraft ? (
                <Button
                    variant="primary"
                    onClick={() => void handleSave()}
                    disabled={saving || !hasDraftInput}
                    loading={saving}
                    title={hasDraftInput ? t('im.createInstanceTooltip') : t('im.fillInFieldsFirst')}
                >
                  {saving ? t('status.creating') : t('button.createInstance')}
                </Button>
            ) : (
                <>
                  <Button
                      variant="primary"
                      onClick={() => void handleSave()}
                      disabled={saving || !dirty}
                      loading={saving}
                      title="⌘S"
                  >
                    {saving ? t('status.saving') : savedAt ? t('common.saved') : t('common.save')}
                  </Button>
                  <Button
                      variant="danger"
                      leftIcon={<Trash2 className="w-4 h-4" />}
                      onClick={async () => {
                        if (!channel) return
                        setDeleteError(null)
                        const connected = channel.status === 'connected'
                        const msg = t('im.confirmDeleteTitle').replace('{name}', channel.name)
                          + '\n\n' + t('im.confirmDeleteWarning')
                          + '\n' + t('im.confirmDeleteChannel')
                          + (connected ? '\n' + t('im.confirmDeleteConnection') : '')
                          + '\n' + t('im.confirmDeleteWorkspace')
                          + '\n' + t('im.confirmDeleteUserDir')
                          + '\n\n' + t('im.confirmDeleteIrreversible')
                        if (!window.confirm(msg)) return
                        try {
                          await onDelete(channel.id)
                        } catch (err) {
                          setDeleteError(err instanceof Error ? err.message : t('im.deleteFailed').trim())
                        }
                      }}
                  >
                    {t('common.delete')}
                  </Button>
                </>
            )}
          </div>
        </div>

        {(saveError || deleteError) && (
            <div className="flex items-start gap-3 px-4 py-3 rounded-lg bg-coral/10 border border-coral/30 text-coral text-[13px]">
              <span className="flex-1">{deleteError ? t('im.deleteFailed') + deleteError : t('im.saveFailed') + saveError}</span>
              <button
                  className="text-[12px] text-coral/70 hover:text-coral"
                  onClick={() => { setSaveError(null); setDeleteError(null) }}
                  aria-label="dismiss"
              >×</button>
            </div>
        )}

        <Card className="space-y-4">
          <div className="flex items-center justify-between">
            <span className="text-[14px] font-semibold text-ink block">{t('im.instanceSettings')}</span>
            {channel && (
                <div className="flex items-center gap-2">
                  <span className="text-[12px] text-ink-muted">{channel.enabled ? t('common.enabled') : t('common.disabled')}</span>
                  <Switch
                      checked={!!channel.enabled}
                      disabled={dirty}
                      onChange={() => { if (channel) void onUpdate(channel.id, { enabled: !channel.enabled }) }}
                      aria-label={t('common.enabled')}
                      title={dirty ? t('im.switchDisabledDirty') : undefined}
                  />
                </div>
            )}
          </div>

          <Field label={t('im.botName')}>
            <Input
                value={name}
                onChange={(e) => { setName(e.target.value); setDirty(true) }}
                placeholder={t('im.botFallback').replace('{platform}', t(meta.label))}
                suppressAutofill
                className="w-full"
            />
          </Field>

          <div className="grid grid-cols-2 gap-4">
            <Field label={t('im.defaultWorkspace')}>
              <CustomSelect
                  size="sm"
                  value={defaultWorkspaceId}
                  onChange={(v) => { setDefaultWorkspaceId(v); setDirty(true) }}
                  options={[
                    { value: '', label: t('im.useGlobalDefault') },
                    ...workspaces.map((ws) => ({ value: ws.id, label: ws.path })),
                  ]}
              />
            </Field>
            <Field label={t('im.defaultModel')}>
              <CustomSelect
                  size="sm"
                  value={defaultModel}
                  onChange={(v) => { setDefaultModel(v); setDirty(true) }}
                  options={[
                    { value: '', label: t('im.useGlobalDefault') },
                    ...models.map((m) => ({ value: m.id, label: m.label })),
                  ]}
              />
            </Field>
          </div>
        </Card>

        <Card className="space-y-4">
          <span className="text-[14px] font-semibold text-ink block">{t('im.credentials')}</span>

          {meta.credentialFields.map((field) => (
              <Field key={field.key} label={field.label}>
                {field.key === 'platform' ? (
                    <CustomSelect
                        size="sm"
                        value={creds.platform || 'feishu'}
                        onChange={(v) => updateCred('platform', v)}
                        options={[{ value: 'feishu', label: 'Feishu' }, { value: 'lark', label: 'Lark' }]}
                    />
                ) : (
                    <Input
                        type={field.type || 'text'}
                        value={creds[field.key] || ''}
                        onChange={(e) => updateCred(field.key, e.target.value)}
                        placeholder={field.placeholder}
                        suppressAutofill
                        name={`im-cred-${channelType}-${field.key}`}
                        autoCorrect="off"
                        autoCapitalize="off"
                        spellCheck={false}
                        className="w-full"
                    />
                )}
              </Field>
          ))}

          {/* Feishu Card Streaming Config */}
          {channelType === 'feishu' && (
              <Card variant="nested" className="space-y-3">
                <button
                    type="button"
                    className="flex items-center gap-1.5 w-full text-left"
                    onClick={() => setCardStreamingExpanded(prev => !prev)}
                >
                  {cardStreamingExpanded
                    ? <ChevronDown className="w-3.5 h-3.5 text-ink-muted shrink-0" />
                    : <ChevronRight className="w-3.5 h-3.5 text-ink-muted shrink-0" />
                  }
                  <span className="text-[13px] font-medium text-ink">{t('im.cardReplyMode')}</span>
                  {cardConfig.replyMode !== 'static' && (
                      <span className="text-[10px] px-1.5 py-0.5 rounded bg-green/10 text-green font-medium">{t('im.streamingBadge')}</span>
                  )}
                </button>

                {cardStreamingExpanded && (<>
                  <p className="text-[11px] text-ink-muted">{t('im.cardReplyModeDesc')}</p>


                  <Field label={t('im.replyMode')}>
                    <CustomSelect
                        size="sm"
                        value={cardConfig.replyMode}
                        onChange={(v) => { setCardConfig(prev => ({ ...prev, replyMode: v })); setDirty(true) }}
                        options={[
                          { value: 'auto', label: t('im.replyMode.auto') },
                          { value: 'streaming', label: t('im.replyMode.streaming') },
                          { value: 'static', label: t('im.replyMode.static') },
                        ]}
                    />
                  </Field>

                  <div className="space-y-1.5">
                    <span className="text-[12px] font-medium text-ink-muted">{t('im.footerMetrics')}</span>
                    <label className="flex items-center gap-2 cursor-pointer">
                      <input
                          type="checkbox"
                          checked={cardConfig.footerStatus}
                          onChange={(e) => { setCardConfig(prev => ({ ...prev, footerStatus: e.target.checked })); setDirty(true) }}
                          className="rounded border-hairline"
                      />
                      <span className="text-[12px] text-ink">{t('im.footerStatus')}</span>
                    </label>
                    <label className="flex items-center gap-2 cursor-pointer">
                      <input
                          type="checkbox"
                          checked={cardConfig.footerElapsed}
                          onChange={(e) => { setCardConfig(prev => ({ ...prev, footerElapsed: e.target.checked })); setDirty(true) }}
                          className="rounded border-hairline"
                      />
                      <span className="text-[12px] text-ink">{t('im.footerElapsed')}</span>
                    </label>
                    <label className="flex items-center gap-2 cursor-pointer">
                      <input
                          type="checkbox"
                          checked={cardConfig.footerModel}
                          onChange={(e) => { setCardConfig(prev => ({ ...prev, footerModel: e.target.checked })); setDirty(true) }}
                          className="rounded border-hairline"
                      />
                      <span className="text-[12px] text-ink">{t('im.footerModel')}</span>
                    </label>
                  </div>
                </>)}
              </Card>
          )}

          {/* Feishu QR Quick Connect Panel */}
          {channelType === 'feishu' && (
              <Card variant="nested" className="space-y-3">
                <div className="flex items-center justify-between">
                  <span className="text-[13px] font-medium text-ink">{t('im.feishuQuickConnect')}</span>
                  {creds.app_id ? (
                      <span className="text-[11px] text-green font-medium">{t('im.configured')}</span>
                  ) : (
                      <span className="text-[11px] text-amber font-medium">{t('im.notConfigured')}</span>
                  )}
                </div>

                {!creds.app_id && !feishuQrImageUrl && (
                    <Button variant="secondary" onClick={() => void handleStartFeishuQr()}>
                      {t('button.scanQr')}
                    </Button>
                )}

                {feishuQrImageUrl && (
                    <div className="space-y-3">
                      <div className="flex justify-center">
                        <img
                            src={feishuQrImageUrl}
                            alt="Feishu QR Code"
                            className="w-48 h-48 rounded-lg border border-hairline bg-white object-contain"
                            onError={() => {
                              setFeishuQrError('QR code image failed to render. Refresh and try again.')
                            }}
                        />
                      </div>
                      <div className="flex items-center justify-between">
                        <div className="flex items-center gap-1.5">
                          <span className={cn('w-2 h-2 rounded-full',
                            feishuQrStatus === 'confirmed' ? 'bg-green' :
                            feishuQrStatus === 'expired' || feishuQrStatus === 'denied' ? 'bg-coral' :
                            'bg-ink-tertiary'
                          )} />
                          <span className="text-[12px] text-ink-muted">
                            {feishuQrStatus === 'confirmed' ? t('im.qr.confirmed') :
                             feishuQrStatus === 'expired' ? t('im.qr.expired') :
                             feishuQrStatus === 'denied' ? t('im.qr.denied') :
                             t('im.qr.pending')}
                          </span>
                        </div>
                        <Button
                            variant="ghost"
                            size="sm"
                            onClick={() => void handleStartFeishuQr()}
                        >
                          {t('button.refreshQr')}
                        </Button>
                      </div>
                      <p className="text-[11px] text-ink-tertiary text-center">
                        Open Feishu App → Messages → Scan the QR code to create the bot automatically.
                      </p>
                    </div>
                )}

                {creds.app_id && (
                    <Button
                        variant="secondary"
                        onClick={() => {
                          setCreds((prev) => {
                            const { app_id, app_secret, ...rest } = prev
                            return rest
                          })
                          setDirty(true)
                        }}
                    >
                      {t('button.revokeCredentials')}
                    </Button>
                )}

                {feishuQrError && (
                    <div className="px-3 py-2 rounded-lg bg-coral/10 text-coral text-[12px]">
                      {feishuQrError}
                    </div>
                )}
              </Card>
          )}

          {/* DingTalk QR Auth Panel */}
          {channelType === 'dingtalk' && (
              <Card variant="nested" className="space-y-3">
                <div className="flex items-center justify-between">
                  <span className="text-[13px] font-medium text-ink">{t('im.dingtalkQuickConnect')}</span>
                  {creds.client_id ? (
                      <span className="text-[11px] text-green font-medium">{t('im.authorized')}</span>
                  ) : (
                      <span className="text-[11px] text-amber font-medium">{t('im.notAuthorized')}</span>
                  )}
                </div>

                {!creds.client_id && !dingtalkQrCodeUrl && (
                    <Button variant="secondary" onClick={() => void handleStartDingtalkQr()}>
                      {t('button.scanWithDingtalk')}
                    </Button>
                )}

                {dingtalkQrCodeUrl && dingtalkQrCodeImageUrl && (
                    <div className="space-y-3">
                      <div className="flex justify-center">
                        <img
                            src={dingtalkQrCodeImageUrl}
                            alt="DingTalk QR Code"
                            className="w-48 h-48 rounded-lg border border-hairline bg-white object-contain"
                            onError={() => {
                              setDingtalkQrError('QR code image failed to render. Refresh and try again.')
                            }}
                        />
                      </div>
                      {dingtalkQrCodeKey && (
                        <div className="text-center text-[12px] text-ink-muted">
                          {t('im.qr.userCode')}<span className="font-mono font-medium">{dingtalkQrCodeKey.split('|')[1] || dingtalkQrCodeKey}</span>
                        </div>
                      )}
                      <div className="flex items-center justify-between">
                        <div className="flex items-center gap-1.5">
                          <span className={cn('w-2 h-2 rounded-full',
                            dingtalkQrStatus === 'confirmed' ? 'bg-green' :
                            dingtalkQrStatus === 'expired' ? 'bg-coral' :
                            dingtalkQrStatus === 'scanned' ? 'bg-amber' :
                            'bg-ink-tertiary'
                          )} />
                          <span className="text-[12px] text-ink-muted">
                            {dingtalkQrStatus === 'confirmed' ? t('im.qr.confirmed') :
                             dingtalkQrStatus === 'expired' ? t('im.qr.expired') :
                             dingtalkQrStatus === 'scanned' ? t('im.qr.scanned') :
                             t('im.qr.pending')}
                          </span>
                        </div>
                        <Button
                            variant="ghost"
                            size="sm"
                            onClick={() => void handleStartDingtalkQr()}
                        >
                          {t('button.refreshQr')}
                        </Button>
                      </div>
                    </div>
                )}

                {creds.client_id && (
                    <Button
                        variant="secondary"
                        onClick={() => {
                          setCreds((prev) => {
                            const { client_id, client_secret, ...rest } = prev
                            return rest
                          })
                          setDirty(true)
                        }}
                    >
                      {t('button.revokeAuth')}
                    </Button>
                )}

                {dingtalkQrError && (
                    <div className="px-3 py-2 rounded-lg bg-coral/10 text-coral text-[12px]">
                      {dingtalkQrError}
                    </div>
                )}
              </Card>
          )}

          {/* WeChat QR Login Panel */}
          {channelType === 'weixin' && (
              <Card variant="nested" className="space-y-3">
                <div className="flex items-center justify-between">
                  <span className="text-[13px] font-medium text-ink">{t('im.wechatLogin')}</span>
                  {creds.token ? (
                      <span className="text-[11px] text-green font-medium">{t('im.loggedIn')}</span>
                  ) : (
                      <span className="text-[11px] text-amber font-medium">{t('im.notLoggedIn')}</span>
                  )}
                </div>

                {!creds.token && !weixinQrCodeUrl && (
                    <Button variant="secondary" onClick={() => void handleStartQrLogin()}>
                      {t('button.getQr')}
                    </Button>
                )}

                {weixinQrCodeUrl && weixinQrCodeImageUrl && (
                    <div className="space-y-3">
                      <div className="flex justify-center">
                        <img
                            src={weixinQrCodeImageUrl}
                            alt="WeChat QR Code"
                            className="w-48 h-48 rounded-lg border border-hairline bg-white object-contain"
                            onError={() => {
                              setWeixinQrError('QR code image failed to render. Refresh the QR code and try again.')
                            }}
                        />
                      </div>
                      <div className="flex items-center justify-between">
                        <div className="flex items-center gap-1.5">
                          <span className={cn('w-2 h-2 rounded-full',
                            weixinQrStatus === 'confirmed' ? 'bg-green' :
                            weixinQrStatus === 'expired' ? 'bg-coral' :
                            weixinQrStatus === 'scanned' ? 'bg-amber' :
                            'bg-ink-tertiary'
                          )} />
                          <span className="text-[12px] text-ink-muted">
                            {weixinQrStatus === 'confirmed' ? t('im.qr.confirmed') :
                             weixinQrStatus === 'expired' ? t('im.qr.expired') :
                             weixinQrStatus === 'scanned' ? t('im.qr.scanned') :
                             t('im.qr.pending')}
                          </span>
                        </div>
                        <Button
                            variant="ghost"
                            size="sm"
                            onClick={() => void handleStartQrLogin()}
                        >
                          {t('button.refreshQr')}
                        </Button>
                      </div>
                    </div>
                )}

                {creds.token && (
                    <Button
                        variant="secondary"
                        onClick={() => { setCreds((prev) => { const { token, ...rest } = prev; return rest }); setDirty(true) }}
                    >
                      {t('button.revokeLogin')}
                    </Button>
                )}

                {weixinQrError && (
                    <div className="px-3 py-2 rounded-lg bg-coral/10 text-coral text-[12px]">
                      {weixinQrError}
                    </div>
                )}
              </Card>
          )}

          {/* WeCom CLI Init Panel */}
          {channelType === 'wecom' && creds.bot_id && (
              <Card variant="nested" className="space-y-3">
                <div className="flex items-center justify-between">
                  <span className="text-[13px] font-medium text-ink">{t('im.agentCliAccess')}</span>
                  {creds.wecom_cli_config_dir ? (
                      <span className="text-[11px] text-green font-medium">{t('im.initialized')}</span>
                  ) : (
                      <span className="text-[11px] text-amber font-medium">{t('im.notInitialized')}</span>
                  )}
                </div>
                <p className="text-[11px] text-ink-muted">
                  {t('im.wecomCliDesc')}
                  {' '}
                </p>

                {!creds.wecom_cli_config_dir && !wecomCliQrDataUrl && (
                    <Button variant="secondary" onClick={() => void handleStartWecomCliInit()}>
                      {t('button.initializeCli')}
                    </Button>
                )}

                {wecomCliQrDataUrl && (
                    <div className="space-y-3">
                      <div className="flex justify-center">
                        <img
                            src={wecomCliQrDataUrl}
                            alt="WeCom QR Code"
                            className="w-48 h-48 rounded-lg border border-hairline bg-white object-contain"
                            onError={() => {
                              setWecomCliError('QR code image failed to render. Refresh and try again.')
                            }}
                        />
                      </div>
                      <div className="flex items-center justify-between">
                        <div className="flex items-center gap-1.5">
                          <span className={cn('w-2 h-2 rounded-full',
                            wecomCliStatus === 'completed' ? 'bg-green' :
                            wecomCliStatus === 'failed' ? 'bg-coral' :
                            'bg-ink-tertiary'
                          )} />
                          <span className="text-[12px] text-ink-muted">
                            {wecomCliStatus === 'completed' ? t('im.qr.completed') :
                             wecomCliStatus === 'failed' ? t('im.qr.failed') :
                             wecomCliStatus === 'cancelled' ? t('im.qr.cancelled') :
                             t('im.qr.scanWecom')}
                          </span>
                        </div>
                        <Button
                            variant="ghost"
                            size="sm"
                            onClick={() => void handleCancelWecomCli()}
                        >
                          {t('common.cancel')}
                        </Button>
                      </div>
                    </div>
                )}

                {creds.wecom_cli_config_dir && (
                    <Button
                        variant="secondary"
                        onClick={() => {
                          setCreds((prev) => {
                            const { wecom_cli_config_dir, ...rest } = prev
                            return rest
                          })
                          setDirty(true)
                        }}
                    >
                      {t('button.revokeCliAccess')}
                    </Button>
                )}

                {wecomCliError && (
                    <div className="px-3 py-2 rounded-lg bg-coral/10 text-coral text-[12px]">
                      {wecomCliError}
                    </div>
                )}
              </Card>
          )}

          {/* 连接状态和按钮 */}
          {!isDraft && (
              <div className="flex items-center justify-between pt-2 border-t border-hairline">
                <div className="flex items-center gap-2">
                  <div className={cn('w-2 h-2 rounded-full', statusColor)} />
                  <span className={cn('text-[12px] font-medium', statusTextColor)}>{statusText}</span>
                </div>

                <div className="flex items-center gap-2">
                  {channel?.status === 'connected' ? (
                      <Button
                          variant="danger"
                          onClick={() => void handleConnect('disconnect')}
                          disabled={disconnecting}
                          loading={disconnecting}
                      >
                        {disconnecting ? t('status.disconnecting') : t('button.disconnect')}
                      </Button>
                  ) : (
                      <Button
                          variant="secondary"
                          onClick={() => void handleConnect('connect')}
                          disabled={!channel || connecting || !channel.enabled || dirty || (channel.type === 'weixin' && !creds.token) || (channel.type === 'dingtalk' && !creds.client_id) || (channel.type === 'feishu' && !creds.app_id)}
                          loading={connecting}
                      >
                        {connecting ? t('status.connecting') : t('button.connect')}
                      </Button>
                  )}
                </div>
              </div>
          )}

          {connectError && (
              <div className="px-3 py-2 rounded-lg bg-coral/10 text-coral text-[12px]">
                {t('error.connectionError')} {connectError}
              </div>
          )}
          {connectSuccess && !connectError && (
              <div className="px-3 py-2 rounded-lg bg-green/10 text-green text-[12px]">
                {t('message.connectedSuccess')}
              </div>
          )}
        </Card>

        <Card className="space-y-4">
          <span className="text-[14px] font-semibold text-ink block">{t('im.accessControl')}</span>

          <div className="grid grid-cols-2 gap-4">
            <Field label={t('im.dmPolicy')}>
              <CustomSelect
                  size="sm"
                  value={dmPolicy}
                  onChange={(v) => { setDmPolicy(v as ImDmPolicy); setDirty(true) }}
                  options={[
                    { value: 'pairing', label: t('im.policy.pairing') },
                    { value: 'allowlist', label: t('im.policy.allowlist') },
                    { value: 'open', label: t('im.policy.open') },
                    { value: 'disabled', label: t('im.policy.disabled') },
                  ]}
              />
            </Field>
            <Field label={t('im.groupPolicy')}>
              <CustomSelect
                  size="sm"
                  value={groupPolicy}
                  onChange={(v) => { setGroupPolicy(v as ImGroupPolicy); setDirty(true) }}
                  options={[
                    { value: 'allowlist', label: t('im.policy.allowlist') },
                    { value: 'open', label: t('im.policy.open') },
                    { value: 'disabled', label: t('im.policy.disabled') },
                  ]}
              />
            </Field>
          </div>

          <div className="flex items-center justify-between py-1">
            <div>
              <span className="text-[13px] font-medium text-ink">{t('im.requireMention')}</span>
              <p className="text-[11px] text-ink-tertiary mt-0.5">{t('im.requireMentionDesc')}</p>
            </div>
            <Switch
                checked={triggerMode === 'mention'}
                onChange={(next) => { setTriggerMode(next ? 'mention' : 'all'); setDirty(true) }}
                aria-label={t('im.requireMention')}
            />
          </div>

          <div className="grid grid-cols-2 gap-4">
            <Field label={t('im.senderWhitelist')}>
              <Input
                  value={senderWl}
                  onChange={(e) => { setSenderWl(e.target.value); setDirty(true) }}
                  placeholder="user_id_1, user_id_2"
                  suppressAutofill
                  className="w-full"
              />
            </Field>
            <Field label={t('im.groupWhitelist')}>
              <Input
                  value={groupWl}
                  onChange={(e) => { setGroupWl(e.target.value); setDirty(true) }}
                  placeholder="chat_id_1, chat_id_2"
                  suppressAutofill
                  className="w-full"
              />
            </Field>
          </div>
        </Card>

        <Card className="space-y-3">
          <button
              onClick={() => setShowCommands(!showCommands)}
              className="flex items-center justify-between w-full text-left"
          >
            <span className="text-[14px] font-semibold text-ink block">{t('im.commands')}</span>
            <ChevronDown className={cn('w-4 h-4 text-ink-tertiary transition-transform', showCommands && 'rotate-180')} />
          </button>
          {showCommands && (
              <>
                <p className="text-[12px] text-ink-muted">{t('im.commandsOperateDesc')}</p>
                <div className="space-y-1.5">
                  {COMMANDS.map((cmd) => (
                      <div key={cmd} className="flex gap-2 text-[12px]">
                        <code className="shrink-0 px-1.5 py-0.5 rounded bg-surface-2 text-brand-primary font-mono">{cmd}</code>
                      </div>
                  ))}
                </div>
              </>
          )}
        </Card>
      </div>
  )
}

function Field({ label, children }: { label: string; children: ReactNode }) {
  return (
      <div>
        <label className="block text-[12px] font-medium text-ink-muted mb-1.5">{label}</label>
        {children}
      </div>
  )
}
