import { useCallback, useMemo, useState } from 'react'
import {
  View,
  Text,
  ScrollView,
  TextInput,
  Pressable,
  StyleSheet,
  ActivityIndicator,
  Alert,
  RefreshControl,
  Linking,
  Switch,
} from 'react-native'
import { Picker } from '@react-native-picker/picker'
import { SafeAreaView } from 'react-native-safe-area-context'

import { useBind } from '@/context/BindContext'
import { useRecorder } from '@/hooks/useRecorder'
import { theme } from '@/constants/theme'
import {
  douyinRoomAndHref,
  parseYoutubeStreamKeySuffix,
  proChannelPm2Process,
} from '@/lib/youtubeConfigUtils'

function statusTintBackground(variant: string): string {
  switch (variant) {
    case 'online':
      return 'rgba(61, 214, 140, 0.1)'
    case 'stopped':
    case 'error':
      return 'rgba(245, 101, 101, 0.1)'
    case 'warn':
      return 'rgba(246, 173, 85, 0.12)'
    case 'offline':
      return 'rgba(139, 146, 158, 0.11)'
    case 'loading':
      return 'rgba(91, 157, 255, 0.12)'
    case 'unknown':
    case 'empty':
      return 'rgba(91, 157, 255, 0.09)'
    default:
      return 'rgba(139, 146, 158, 0.1)'
  }
}

function ctrlStyle(action: string): object {
  switch (action) {
    case 'start':
      return styles.ctrl_start
    case 'stop':
      return styles.ctrl_stop
    case 'restart':
      return styles.ctrl_restart
    case 'status':
      return styles.ctrl_status
    default:
      return {}
  }
}

const variantColor: Record<string, string> = {
  online: theme.success,
  stopped: theme.danger,
  error: theme.danger,
  warn: theme.warn,
  offline: theme.danger,
  loading: theme.muted,
  unknown: theme.accent,
  empty: theme.accent,
}

function ProLiveSwitchGrid({
  rows,
  committedChannelKeys,
  proChannelEditors,
  highlightedChannelId,
  loadingAction,
  onSelectChannel,
  onStart,
  onStop,
  onRestart,
}: {
  rows: { channelId: string; channelName: string; douyinUrl: string; live: boolean }[]
  committedChannelKeys: Record<string, string[]>
  proChannelEditors: Record<string, { keys: string[]; urlText: string }>
  highlightedChannelId: string
  loadingAction: string | null
  onSelectChannel: (channelId: string) => void
  onStart: (channelId: string) => void
  onStop: (channelId: string) => void
  onRestart: (channelId: string) => void
}) {
  const pmBusy = loadingAction !== null
  return (
    <View style={styles.proGrid}>
      {rows.map((row) => {
        const saved = committedChannelKeys[row.channelId]?.[0] ?? ''
        const ed = proChannelEditors[row.channelId]
        const activeKey = (saved || ed?.keys?.[0] || '').trim()
        const keySuffix = parseYoutubeStreamKeySuffix(`key = ${activeKey}`)
        const { room, href } = douyinRoomAndHref(row.douyinUrl, ed?.urlText ?? '')
        const stopDisabled = pmBusy || !row.live
        const rowSelected = row.channelId === highlightedChannelId
        return (
          <View
            key={row.channelId}
            style={[styles.proSwitchRow, rowSelected && styles.proSwitchRowOn]}
          >
            <Pressable onPress={() => onSelectChannel(row.channelId)}>
              <Text style={styles.proKeySuffix} numberOfLines={1}>
                {keySuffix || '—'}
              </Text>
              <View style={[styles.proTag, row.live ? styles.proTagOn : styles.proTagOff]}>
                <Text style={styles.proTagText}>{row.live ? '直播中' : '未开播'}</Text>
              </View>
              <Text style={styles.proMid} numberOfLines={2}>
                {room} · {row.channelName}
              </Text>
            </Pressable>
            {href ? (
              <Pressable onPress={() => void Linking.openURL(href)}>
                <Text style={styles.proLink}>在浏览器打开抖音页</Text>
              </Pressable>
            ) : null}
            <View style={styles.proActions}>
              <Pressable
                style={[styles.proBtn, styles.proBtnStart, (pmBusy || row.live) && styles.ctrlDisabled]}
                disabled={pmBusy || row.live}
                onPress={() => onStart(row.channelId)}
              >
                <Text style={styles.proBtnText}>开始</Text>
              </Pressable>
              <Pressable
                style={[styles.proBtn, styles.proBtnStop, stopDisabled && styles.ctrlDisabled]}
                disabled={stopDisabled}
                onPress={() => onStop(row.channelId)}
              >
                <Text style={styles.proBtnText}>停止</Text>
              </Pressable>
              <Pressable
                style={[styles.proBtn, styles.proBtnRestart, pmBusy && styles.ctrlDisabled]}
                disabled={pmBusy}
                onPress={() => onRestart(row.channelId)}
              >
                <Text style={styles.proBtnText}>重新开始</Text>
              </Pressable>
            </View>
          </View>
        )
      })}
    </View>
  )
}

function LiveSwitchGrid({
  loadingAction,
  onAction,
  disabledAll,
}: {
  loadingAction: string | null
  onAction: (a: 'start' | 'stop' | 'restart' | 'status') => void
  disabledAll?: boolean
}) {
  return (
    <View style={styles.grid}>
      {(
        [
          ['start', '开始直播'],
          ['stop', '停止直播'],
          ['restart', '重新开始'],
          ['status', '刷新状态'],
        ] as const
      ).map(([action, label]) => (
        <Pressable
          key={action}
          style={[
            styles.ctrl,
            ctrlStyle(action),
            loadingAction === action && styles.btnDisabled,
            disabledAll && styles.ctrlDisabled,
          ]}
          disabled={!disabledAll && loadingAction !== null && action !== 'status'}
          onPress={() => {
            if (disabledAll) {
              Alert.alert('未绑定', '请先在「远程」页完成配对后再操作。')
              return
            }
            if (loadingAction !== null && action !== 'status') return
            void onAction(action)
          }}
        >
          <Text style={[styles.ctrlText, action === 'status' && { color: theme.text }]}>{label}</Text>
        </Pressable>
      ))}
    </View>
  )
}

export default function RecordScreen() {
  const { bind } = useBind()
  const r = useRecorder()
  const unbound = !bind
  const [refreshing, setRefreshing] = useState(false)

  const statusColor = useMemo(() => variantColor[r.statusMeta.variant] ?? theme.muted, [r.statusMeta.variant])
  const statusBg = useMemo(() => statusTintBackground(r.statusMeta.variant), [r.statusMeta.variant])

  const onRefresh = useCallback(async () => {
    if (!bind) return
    setRefreshing(true)
    await r.refreshProcessList()
    setRefreshing(false)
  }, [bind, r.refreshProcessList])

  if (r.bootError && bind) {
    return (
      <SafeAreaView style={styles.safe} edges={['bottom']}>
        <View style={styles.fatalWrap}>
          <Text style={styles.err}>无法连接：{r.bootError}</Text>
          <Text style={styles.fatalHint}>
            常见于网络瞬时断连；若「远程」里测试 API 正常，多半是任务列表请求失败，请重试。
          </Text>
          <Pressable style={styles.retryBtn} onPress={() => void r.refreshProcessList()}>
            <Text style={styles.retryBtnText}>重试加载任务列表</Text>
          </Pressable>
        </View>
      </SafeAreaView>
    )
  }

  return (
    <SafeAreaView style={styles.safe} edges={['bottom']}>
      <ScrollView
        contentContainerStyle={styles.scroll}
        refreshControl={
          bind ? (
            <RefreshControl refreshing={refreshing} onRefresh={() => void onRefresh()} tintColor={theme.accent} />
          ) : undefined
        }
      >
        {bind && r.transientLoadError ? (
          <View style={styles.warnBanner}>
            <Text style={styles.warnBannerText}>
              任务列表刷新失败：{r.transientLoadError}
              {'\n'}
              下拉刷新重试；直播状态仍会每 2 秒轮询。
            </Text>
          </View>
        ) : null}

        {bind && (
          <View style={styles.banner}>
            <Text style={styles.bannerText}>
              以下仅远程控制 PC 上的转播与 PM2 进程；推流与 FFmpeg 在电脑端执行，手机不跑拉流/转码。
            </Text>
          </View>
        )}

        <View style={styles.toolbarCard}>
          <View style={styles.toolbarRow}>
            <View style={styles.toolbarLeft}>
              <View
                style={[
                  styles.statusChip,
                  { borderLeftColor: statusColor, backgroundColor: statusBg },
                ]}
              >
                <View style={[styles.statusDotOuter, { borderColor: statusColor }]}>
                  <View style={[styles.statusDotInner, { backgroundColor: statusColor }]} />
                </View>
                <Text style={styles.statusLineText} numberOfLines={1}>
                  {r.statusMeta.line}
                </Text>
              </View>
            </View>
            <View style={[styles.proCluster, unbound && styles.proClusterDisabled]}>
              <View style={styles.proLabelRow}>
                <Text style={styles.proLabelZh}>多频道</Text>
                <View style={[styles.proBadge, r.multiChannelPro && styles.proBadgeOn]}>
                  <Text style={styles.proBadgeText}>PRO</Text>
                </View>
              </View>
              <View style={styles.proSwitchWrap}>
                <Switch
                  value={r.multiChannelPro}
                  disabled={unbound}
                  onValueChange={(v) => {
                    if (unbound) return
                    void r.togglePro(v)
                  }}
                  trackColor={{
                    false: 'rgba(255,255,255,0.12)',
                    true: 'rgba(91, 157, 255, 0.45)',
                  }}
                  thumbColor={r.multiChannelPro ? '#7eb6ff' : '#5c6370'}
                  ios_backgroundColor="rgba(255,255,255,0.1)"
                />
              </View>
            </View>
          </View>
        </View>

        {bind ? (
          <View style={[styles.pickerWrap, r.emptyHint && styles.pickerDisabled, styles.pickerRowBelow]}>
            <Picker
              enabled={!r.emptyHint}
              selectedValue={r.currentProcess}
              onValueChange={(v) => r.setCurrentProcess(String(v))}
              style={styles.picker}
              dropdownIconColor={theme.text}
            >
              {!r.entries.length ? (
                <Picker.Item label="（无任务）" value="" color={theme.muted} />
              ) : (
                r.entries.map((e) => (
                  <Picker.Item key={e.pm2} label={e.label} value={e.pm2} color={theme.text} />
                ))
              )}
            </Picker>
          </View>
        ) : null}

        {bind && r.emptyHint ? (
          <View style={styles.emptyCard}>
            <Text style={styles.emptyTitle}>还没有可用的直播任务</Text>
            <Text style={styles.hint}>{r.emptyHint}</Text>
          </View>
        ) : unbound ? (
          <>
            <View style={[styles.card, styles.cardAccent]}>
              <Text style={styles.cardTitle}>直播开关</Text>
              <LiveSwitchGrid
                loadingAction={r.loadingAction}
                disabledAll
                onAction={() => {}}
              />
            </View>

            <View style={styles.card}>
              <Text style={styles.cardTitle}>YouTube 相关设置</Text>
              <Text style={styles.hintSmall}>配对后从 PC 读取 youtube.ini，与桌面端一致。</Text>
              <TextInput
                style={[styles.area, styles.inputDisabled]}
                multiline
                editable={false}
                value=""
                placeholder="串流密钥、分辨率等"
                placeholderTextColor={theme.muted}
              />
            </View>

            <View style={styles.card}>
              <Text style={styles.cardTitle}>直播地址列表</Text>
              <Text style={styles.hintSmall}>要转播的直播间地址，一行一个。</Text>
              <TextInput
                style={[styles.area, styles.inputDisabled]}
                multiline
                editable={false}
                value=""
                placeholder="配对后显示"
                placeholderTextColor={theme.muted}
              />
            </View>

            <View style={styles.card}>
              <Text style={styles.cardTitle}>OBS 推流</Text>
              <Text style={styles.hintSmall}>OBS 选自定义推流，服务器：</Text>
              <Text style={styles.mono}>srt://live.local:10080/live/obs</Text>
            </View>

            <View style={styles.card}>
              <Text style={styles.cardTitle}>当前直播日志</Text>
              <Text style={styles.hintSmall}>每秒刷新 · 配对后显示进程输出与异常</Text>
              <ScrollView style={styles.logBox} nestedScrollEnabled>
                <Text style={styles.log}>{r.logText || '—'}</Text>
              </ScrollView>
            </View>
          </>
        ) : (
          <>
            <View style={[styles.card, styles.cardAccent]}>
              <Text style={styles.cardTitle}>直播开关</Text>
              {r.isYoutube && r.multiChannelPro && r.proLiveRows.length > 0 ? (
                <ProLiveSwitchGrid
                  rows={r.proLiveRows}
                  committedChannelKeys={r.committedChannelKeys}
                  proChannelEditors={r.proChannelEditors}
                  highlightedChannelId={r.proChannelId}
                  loadingAction={r.loadingAction}
                  onSelectChannel={(cid) => r.setProChannelId(cid)}
                  onStart={(cid) => void r.runProChannelStart(cid)}
                  onStop={(cid) => void r.runPm2Action('stop', proChannelPm2Process(cid))}
                  onRestart={(cid) => void r.runProChannelRestart(cid)}
                />
              ) : r.isYoutube && r.multiChannelPro && r.proLiveRows.length === 0 ? (
                <Text style={styles.hintSmall}>
                  {r.proChannelsList.length > 0
                    ? '暂无已保存的线路密钥。请在「当前频道」中保存串流密钥后，对应行将显示于此。'
                    : '暂无频道。'}
                </Text>
              ) : (
                <LiveSwitchGrid
                  loadingAction={r.loadingAction}
                  onAction={(a) => void r.runPm2Action(a)}
                />
              )}
            </View>

            {r.isYoutube && r.multiChannelPro && (
              <View style={styles.card}>
                <Text style={styles.cardTitle}>当前频道</Text>
                {r.proChannelsError ? (
                  <Text style={styles.err}>{r.proChannelsError}</Text>
                ) : (
                  <>
                    <View style={styles.pickerWrap}>
                      <Picker
                        selectedValue={r.proChannelId}
                        onValueChange={(v) => r.setProChannelId(String(v))}
                        style={styles.picker}
                        dropdownIconColor={theme.text}
                      >
                        {r.proChannelsList.map((c) => (
                          <Picker.Item
                            key={c.id}
                            label={`${c.name} (${c.keyPreview || '无密钥'})`}
                            value={c.id}
                            color={theme.text}
                          />
                        ))}
                      </Picker>
                    </View>
                    <View style={styles.row}>
                      <Pressable
                        style={[styles.btn, styles.btnSec]}
                        disabled={!!r.newChannelDraft || r.addChannelBusy}
                        onPress={() => r.setNewChannelDraft({ keyText: '' })}
                      >
                        <Text style={styles.btnSecText}>新增频道</Text>
                      </Pressable>
                      <Pressable
                        style={[styles.btn, styles.btnSec]}
                        disabled={r.proChannelsList.length <= 1 || r.deleteChannelBusyId !== null}
                        onPress={() => void r.deleteProChannel(r.proChannelId)}
                      >
                        <Text style={styles.btnSecText}>
                          {r.deleteChannelBusyId === r.proChannelId ? '…' : '删除当前频道'}
                        </Text>
                      </Pressable>
                    </View>
                    {r.newChannelDraft ? (
                      <View style={styles.newChBox}>
                        <Text style={styles.fieldLbl}>新频道串流密钥</Text>
                        <TextInput
                          style={styles.inputSingle}
                          value={r.newChannelDraft.keyText}
                          onChangeText={(v) => r.setNewChannelDraft({ keyText: v })}
                          placeholder="粘贴密钥"
                          placeholderTextColor={theme.muted}
                          autoCapitalize="none"
                        />
                        <View style={styles.row}>
                          <Pressable
                            style={[styles.btn, styles.btnSec]}
                            onPress={() => r.setNewChannelDraft(null)}
                          >
                            <Text style={styles.btnSecText}>取消</Text>
                          </Pressable>
                          <Pressable
                            style={[styles.btn, r.addChannelBusy && styles.btnDisabled]}
                            disabled={r.addChannelBusy}
                            onPress={() => void r.submitNewChannelDraft()}
                          >
                            <Text style={styles.btnText}>{r.addChannelBusy ? '…' : '提交'}</Text>
                          </Pressable>
                        </View>
                      </View>
                    ) : null}
                  </>
                )}
              </View>
            )}

            {r.isYoutube && r.multiChannelPro && !r.proChannelsError && r.proChannelsList.length > 0 && (
              <>
                <View style={styles.card}>
                  <Text style={styles.cardTitle}>YouTube 密钥（当前线路）</Text>
                  <TextInput
                    style={styles.area}
                    multiline
                    value={r.proChannelEditors[r.proChannelId]?.keys?.[0] ?? ''}
                    onChangeText={r.setProChannelKeyText}
                    placeholderTextColor={theme.muted}
                  />
                  <Pressable
                    style={[
                      styles.btn,
                      (r.saveProKeyLoading || r.saveProKeyLoadingId === r.proChannelId) && styles.btnDisabled,
                    ]}
                    disabled={r.saveProKeyLoading || r.saveProKeyLoadingId === r.proChannelId}
                    onPress={() => void r.saveProChannelKey()}
                  >
                    {r.saveProKeyLoading || r.saveProKeyLoadingId === r.proChannelId ? (
                      <ActivityIndicator color="#fff" />
                    ) : (
                      <Text style={styles.btnText}>保存密钥</Text>
                    )}
                  </Pressable>
                  <Text style={styles.hintSmall}>{r.youtubeStatus}</Text>
                </View>
                <View style={styles.card}>
                  <Text style={styles.cardTitle}>直播地址列表（当前线路）</Text>
                  <TextInput
                    style={styles.area}
                    multiline
                    value={r.proChannelEditors[r.proChannelId]?.urlText ?? ''}
                    onChangeText={r.setProChannelUrlText}
                    placeholderTextColor={theme.muted}
                  />
                  <View style={styles.row}>
                    <Pressable style={[styles.btn, styles.btnSec]} onPress={() => void r.reloadProUrlFromServer()}>
                      <Text style={styles.btnSecText}>重新读取</Text>
                    </Pressable>
                    <Pressable
                      style={[styles.btn, styles.btnSec]}
                      onPress={() => void r.saveProUrlConfig()}
                      disabled={r.saveUrlLoading}
                    >
                      <Text style={styles.btnSecText}>{r.saveUrlLoading ? '…' : '保存地址'}</Text>
                    </Pressable>
                  </View>
                  <Text style={styles.hintSmall}>{r.urlStatus}</Text>
                </View>
              </>
            )}

            {r.isYoutube && !r.multiChannelPro && (
              <View style={styles.card}>
                <Text style={styles.cardTitle}>YouTube 相关设置</Text>
                <Text style={styles.fieldLbl}>串流密钥</Text>
                <TextInput
                  style={styles.inputSingle}
                  value={r.ytIniModel.keys[0] ?? ''}
                  onChangeText={(v) =>
                    r.setYtIniModel((m) => ({ ...m, keys: [v], activeIndex: 0 }))
                  }
                  placeholder="粘贴或编辑串流密钥"
                  placeholderTextColor={theme.muted}
                  autoCapitalize="none"
                />
                <Text style={styles.fieldLbl}>输出分辨率</Text>
                <View style={styles.pickerWrap}>
                  <Picker
                    selectedValue={r.ytIniModel.options.output_resolution ?? '720p'}
                    onValueChange={(v) =>
                      r.setYtIniModel((m) => ({
                        ...m,
                        options: { ...m.options, output_resolution: String(v) },
                      }))
                    }
                    style={styles.picker}
                    dropdownIconColor={theme.text}
                  >
                    <Picker.Item label="720p" value="720p" color={theme.text} />
                    <Picker.Item label="1080p" value="1080p" color={theme.text} />
                  </Picker>
                </View>
                <Text style={styles.fieldLbl}>是否使用 GPU（NVENC）</Text>
                <View style={styles.pickerWrap}>
                  <Picker
                    selectedValue={r.ytIniModel.options.use_gpu ?? (r.gpuCaps?.nvenc_available ? '是' : '无')}
                    onValueChange={(v) =>
                      r.setYtIniModel((m) => ({
                        ...m,
                        options: { ...m.options, use_gpu: String(v) },
                      }))
                    }
                    style={styles.picker}
                    dropdownIconColor={theme.text}
                  >
                    {r.gpuCaps?.nvenc_available ? (
                      <>
                        <Picker.Item label="是" value="是" color={theme.text} />
                        <Picker.Item label="否" value="否" color={theme.text} />
                        <Picker.Item label="无" value="无" color={theme.text} />
                      </>
                    ) : (
                      <Picker.Item label="无（未检测到 NVENC）" value="无" color={theme.text} />
                    )}
                  </Picker>
                </View>
                <Text style={styles.fieldLbl}>音频降噪</Text>
                <View style={styles.pickerWrap}>
                  <Picker
                    selectedValue={r.ytIniModel.options.audio_denoise ?? '否'}
                    onValueChange={(v) =>
                      r.setYtIniModel((m) => ({
                        ...m,
                        options: { ...m.options, audio_denoise: String(v) },
                      }))
                    }
                    style={styles.picker}
                    dropdownIconColor={theme.text}
                  >
                    <Picker.Item label="否" value="否" color={theme.text} />
                    <Picker.Item label="是" value="是" color={theme.text} />
                  </Picker>
                </View>
                <View style={styles.row}>
                  <Pressable style={[styles.btn, styles.btnSec]} onPress={() => void r.loadYoutubeIniModel()}>
                    <Text style={styles.btnSecText}>重新读取</Text>
                  </Pressable>
                  <Pressable
                    style={[styles.btn, r.saveYoutubeLoading && styles.btnDisabled]}
                    disabled={r.saveYoutubeLoading}
                    onPress={() => void r.saveYoutubeIniModel()}
                  >
                    {r.saveYoutubeLoading ? (
                      <ActivityIndicator color="#fff" />
                    ) : (
                      <Text style={styles.btnText}>保存</Text>
                    )}
                  </Pressable>
                </View>
                <Text style={styles.hintSmall}>{r.youtubeStatus}</Text>
                {!r.UI_SHOW_YOUTUBE_GOOGLE_OAUTH && (
                  <Text style={styles.hintSmall}>Google 授权与拉取密钥请在 PC 端操作。</Text>
                )}
              </View>
            )}

            {((r.isYoutube && !r.multiChannelPro) || r.isTiktok) && (
              <View style={styles.card}>
                <Text style={styles.cardTitle}>直播地址列表</Text>
                <Text style={styles.hintSmall}>要转播的直播间地址写在这里，一行一个。</Text>
                <TextInput
                  style={styles.area}
                  multiline
                  value={r.urlText}
                  onChangeText={r.setUrlText}
                  placeholderTextColor={theme.muted}
                />
                <View style={styles.row}>
                  <Pressable
                    style={[styles.btn, styles.btnSec]}
                    onPress={() => void r.loadConfig('/get_url_config', r.setUrlText, r.setUrlStatus)}
                  >
                    <Text style={styles.btnSecText}>重新读取</Text>
                  </Pressable>
                  <Pressable
                    style={[styles.btn, r.saveUrlLoading && styles.btnDisabled]}
                    disabled={r.saveUrlLoading}
                    onPress={() => {
                      r.setSaveUrlLoading(true)
                      void r.saveConfig('/save_url_config', r.urlText, r.setUrlStatus, () =>
                        r.setSaveUrlLoading(false),
                      )
                    }}
                  >
                    <Text style={styles.btnText}>保存</Text>
                  </Pressable>
                </View>
                <Text style={styles.hintSmall}>{r.urlStatus}</Text>
              </View>
            )}

            {!r.UI_SHOW_YOUTUBE_GOOGLE_OAUTH && r.isObs && (
              <View style={styles.card}>
                <Text style={styles.cardTitle}>OBS 推流</Text>
                <Text style={styles.hintSmall}>在 OBS 里选「自定义」推流，服务器填下面这一行：</Text>
                <Text style={styles.mono}>srt://live.local:10080/live/obs</Text>
              </View>
            )}

            <View style={styles.card}>
              <Text style={styles.cardTitle}>当前直播日志</Text>
              <Text style={styles.hintSmall}>每 2 秒刷新 · 与上方状态同源</Text>
              <ScrollView style={styles.logBox} nestedScrollEnabled>
                <Text style={styles.log}>{r.logText || '—'}</Text>
              </ScrollView>
            </View>
          </>
        )}
      </ScrollView>
    </SafeAreaView>
  )
}

const styles = StyleSheet.create({
  safe: { flex: 1, backgroundColor: theme.bg },
  fatalWrap: { flex: 1, padding: 20, justifyContent: 'center' },
  fatalHint: { fontSize: 13, color: theme.muted, lineHeight: 20, marginTop: 12, marginBottom: 20 },
  retryBtn: {
    alignSelf: 'flex-start',
    backgroundColor: '#3d7eff',
    paddingVertical: 12,
    paddingHorizontal: 18,
    borderRadius: 10,
  },
  retryBtnText: { color: '#fff', fontWeight: '700', fontSize: 15 },
  warnBanner: {
    padding: 12,
    borderRadius: 10,
    backgroundColor: 'rgba(246,173,85,0.12)',
    borderWidth: 1,
    borderColor: 'rgba(246,173,85,0.35)',
    marginBottom: 14,
  },
  warnBannerText: { fontSize: 13, color: theme.warn, lineHeight: 20 },
  scroll: { padding: 16, paddingBottom: 32 },
  banner: {
    padding: 12,
    borderRadius: 10,
    backgroundColor: 'rgba(91,157,255,0.1)',
    borderWidth: 1,
    borderColor: 'rgba(91,157,255,0.25)',
    marginBottom: 14,
  },
  bannerText: { fontSize: 13, color: theme.text, lineHeight: 20 },
  toolbarCard: {
    marginBottom: 12,
    borderRadius: 14,
    borderWidth: 1,
    borderColor: 'rgba(255,255,255,0.07)',
    backgroundColor: theme.bgElevated,
    paddingVertical: 12,
    paddingHorizontal: 12,
  },
  toolbarRow: {
    flexDirection: 'row',
    alignItems: 'center',
    justifyContent: 'space-between',
    gap: 10,
  },
  toolbarLeft: { flex: 1, minWidth: 0, marginRight: 6 },
  statusChip: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 10,
    paddingVertical: 10,
    paddingHorizontal: 12,
    paddingLeft: 11,
    borderRadius: 12,
    borderLeftWidth: 3,
    borderTopWidth: 1,
    borderRightWidth: 1,
    borderBottomWidth: 1,
    borderTopColor: 'rgba(255,255,255,0.06)',
    borderRightColor: 'rgba(255,255,255,0.06)',
    borderBottomColor: 'rgba(255,255,255,0.06)',
  },
  statusDotOuter: {
    width: 14,
    height: 14,
    borderRadius: 7,
    borderWidth: 2,
    alignItems: 'center',
    justifyContent: 'center',
  },
  statusDotInner: { width: 6, height: 6, borderRadius: 3 },
  statusLineText: {
    flex: 1,
    fontSize: 15,
    fontWeight: '600',
    color: theme.text,
    letterSpacing: 0.2,
  },
  proCluster: {
    flexDirection: 'row',
    alignItems: 'center',
    flexShrink: 0,
    gap: 10,
    paddingLeft: 10,
    paddingVertical: 4,
    paddingRight: 4,
    borderRadius: 12,
    backgroundColor: 'rgba(91, 157, 255, 0.06)',
    borderWidth: 1,
    borderColor: 'rgba(91, 157, 255, 0.18)',
  },
  proClusterDisabled: { opacity: 0.45 },
  proLabelRow: { flexDirection: 'row', alignItems: 'center', gap: 6 },
  proLabelZh: { fontSize: 13, fontWeight: '600', color: theme.text },
  proBadge: {
    paddingHorizontal: 7,
    paddingVertical: 3,
    borderRadius: 6,
    backgroundColor: 'rgba(255,255,255,0.08)',
    borderWidth: 1,
    borderColor: 'rgba(255,255,255,0.1)',
  },
  proBadgeOn: {
    backgroundColor: 'rgba(91, 157, 255, 0.22)',
    borderColor: 'rgba(91, 157, 255, 0.45)',
  },
  proBadgeText: {
    fontSize: 10,
    fontWeight: '800',
    color: theme.accent,
    letterSpacing: 0.6,
  },
  proSwitchWrap: { transform: [{ scale: 0.92 }] },
  unpairedText: { fontSize: 15, fontWeight: '600', color: theme.muted },
  proGrid: { gap: 10 },
  proSwitchRow: {
    padding: 12,
    borderRadius: 10,
    borderWidth: 1,
    borderColor: theme.border,
    backgroundColor: theme.bgCard,
    gap: 8,
  },
  proSwitchRowOn: { borderColor: 'rgba(91,157,255,0.55)', backgroundColor: 'rgba(91,157,255,0.08)' },
  proKeySuffix: { fontFamily: 'monospace', fontSize: 13, color: theme.accent, fontWeight: '600' },
  proTag: { alignSelf: 'flex-start', paddingHorizontal: 10, paddingVertical: 4, borderRadius: 999 },
  proTagOn: { backgroundColor: 'rgba(46,160,67,0.2)' },
  proTagOff: { backgroundColor: 'rgba(110,118,129,0.2)' },
  proTagText: { fontSize: 12, fontWeight: '600', color: theme.text },
  proMid: { fontSize: 13, color: theme.text, lineHeight: 20 },
  proLink: { fontSize: 12, color: theme.accent, marginTop: 6, fontWeight: '600' },
  proActions: { flexDirection: 'row', flexWrap: 'wrap', gap: 8, marginTop: 4 },
  proBtn: { paddingVertical: 10, paddingHorizontal: 12, borderRadius: 8, minWidth: 72, alignItems: 'center' },
  proBtnStart: { backgroundColor: '#0e7a0d' },
  proBtnStop: { backgroundColor: '#d1342c' },
  proBtnRestart: { backgroundColor: '#e68600' },
  proBtnText: { color: '#fff', fontWeight: '700', fontSize: 12 },
  newChBox: { marginTop: 12, paddingTop: 12, borderTopWidth: 1, borderTopColor: theme.border },
  pickerWrap: {
    borderWidth: 1,
    borderColor: theme.border,
    borderRadius: 10,
    overflow: 'hidden',
    backgroundColor: theme.bgCard,
  },
  pickerDisabled: { opacity: 0.65 },
  picker: { color: theme.text },
  pickerRowBelow: { marginTop: 0 },
  dot: { width: 8, height: 8, borderRadius: 4 },
  emptyCard: {
    padding: 20,
    borderRadius: 12,
    backgroundColor: theme.bgCard,
    borderWidth: 1,
    borderColor: theme.border,
    alignItems: 'center',
  },
  emptyTitle: { fontSize: 15, fontWeight: '600', color: theme.muted, marginBottom: 10 },
  card: {
    padding: 14,
    borderRadius: 12,
    backgroundColor: theme.bgCard,
    borderWidth: 1,
    borderColor: theme.border,
    marginBottom: 12,
  },
  cardAccent: {
    borderColor: 'rgba(91,157,255,0.35)',
    backgroundColor: 'rgba(91,157,255,0.06)',
  },
  cardTitle: { fontSize: 15, fontWeight: '600', color: theme.text, marginBottom: 10 },
  fieldLbl: { fontSize: 12, fontWeight: '600', color: theme.muted, marginBottom: 6, marginTop: 4 },
  inputSingle: {
    borderWidth: 1,
    borderColor: theme.border,
    borderRadius: 10,
    padding: 10,
    color: theme.text,
    fontSize: 14,
    marginBottom: 10,
    backgroundColor: theme.bg,
  },
  area: {
    minHeight: 100,
    borderWidth: 1,
    borderColor: theme.border,
    borderRadius: 10,
    padding: 10,
    color: theme.text,
    fontFamily: 'monospace',
    fontSize: 12,
    marginBottom: 10,
    backgroundColor: theme.bg,
  },
  inputDisabled: { opacity: 0.85 },
  row: { flexDirection: 'row', gap: 10, flexWrap: 'wrap' },
  btn: {
    backgroundColor: '#3d7eff',
    paddingVertical: 10,
    paddingHorizontal: 18,
    borderRadius: 10,
    minWidth: 100,
    alignItems: 'center',
  },
  btnDisabled: { opacity: 0.55 },
  btnText: { color: '#fff', fontWeight: '700' },
  btnSec: { backgroundColor: theme.bgElevated, borderWidth: 1, borderColor: theme.border },
  btnSecText: { color: theme.text, fontWeight: '600' },
  hint: { color: theme.muted, fontSize: 14, lineHeight: 22, textAlign: 'center' },
  hintSmall: { color: theme.muted, fontSize: 12, marginTop: 8, marginBottom: 8 },
  err: { color: theme.danger, fontSize: 14 },
  mono: { fontFamily: 'monospace', color: theme.accent, fontSize: 13, marginTop: 6 },
  grid: { flexDirection: 'row', flexWrap: 'wrap', gap: 10 },
  ctrl: { flexGrow: 1, minWidth: '42%', paddingVertical: 14, borderRadius: 10, alignItems: 'center' },
  ctrlDisabled: { opacity: 0.5 },
  ctrl_start: { backgroundColor: '#0e7a0d' },
  ctrl_stop: { backgroundColor: '#d1342c' },
  ctrl_restart: { backgroundColor: '#e68600' },
  ctrl_status: { backgroundColor: theme.bgElevated, borderWidth: 1, borderColor: theme.border },
  ctrlText: { color: '#fff', fontWeight: '700', fontSize: 14 },
  logBox: { maxHeight: 280, backgroundColor: '#1e2128', borderRadius: 10, padding: 10 },
  log: { fontFamily: 'monospace', fontSize: 11, color: '#d0d4dc', lineHeight: 18 },
})
