import React, { useState, useEffect } from 'react'
import { Link } from 'react-router-dom'
import { useSelector } from 'react-redux'
import { api } from '../services/api'
import { selectIsAdmin } from '../store/appSlice'
import './GlobalConfig.css'
import './ConfigPanel.css' // 复用 ConfigPanel 的样式

// 复用 ConfigPanel 的 ConfigItem 组件
// 部分配置项使用“数值原样”（如 RSI 0–100、24h 涨跌幅 25 表示 25%），不做 0–1 比例转换
  const NUMBER_AS_IS_KEYS = new Set([
  'MAX_RSI_FOR_LONG',
  'MIN_RSI_FOR_SHORT',
  'MAX_CHANGE_PERCENT_FOR_LONG',
  'MAX_CHANGE_PERCENT_FOR_SHORT',
  'MIN_RR_FOR_TP1',
  'POSITION_SCALE_FACTOR',
  // 滞涨相关百分比配置使用“数值原样”，10 表示 10%
  'STAGNATION_MIN_RUNUP_PCT',
  'STAGNATION_NO_NEW_HIGH_HOURS',
  'STAGNATION_PARTIAL_CLOSE_PCT',
  'STAGNATION_LOCK_PCT',
])
// 配置项中文标签（便于识别）
const KEY_LABELS = {
  MAX_RSI_FOR_LONG: '做多 RSI 上限',
  MIN_RSI_FOR_SHORT: '做空 RSI 下限',
  MAX_CHANGE_PERCENT_FOR_LONG: '做多 24h 涨跌幅上限(%)',
  MAX_CHANGE_PERCENT_FOR_SHORT: '做空 24h 涨跌幅上限(%)',
  TAKE_PROFIT_1_PERCENT: '第一目标止盈(%)',
  SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT: '智能补单候选数',
  SYMBOL_LOSS_COOLDOWN_ENABLED: '连续亏损冷却',
  SYMBOL_MAX_CONSECUTIVE_LOSSES: '连续亏损次数阈值',
  SYMBOL_LOSS_COOLDOWN_SEC: '冷却时间(秒)',
  RSI_EXTREME_REVERSE_ENABLED: 'RSI 极端反向（超买反空/超卖反多）',
  RSI_EXTREME_REVERSE_ONLY_NEUTRAL_4H: 'RSI 反向仅允许 4H 中性',
  MIN_RR_FOR_TP1: '第一目标最小盈亏比(相对止损)',
  AUTO_TRADE_ONLY_TRENDING: '仅趋势市自动交易',
  AUTO_TRADE_ALLOW_RANGING: '允许震荡市自动交易',
  AUTO_TRADE_ALLOW_UNKNOWN: '允许未判定市场自动交易',
  AUTO_TRADE_ALLOW_4H_NEUTRAL: '允许 4H 中性时自动交易',
  RANGING_MARKET_SIGNAL_BOOST: '震荡市额外信号门槛',
  POSITION_SCALE_FACTOR: '仓位放大系数',
  SYNC_RECOVER_MISSING_POSITIONS: '同步时补建缺失持仓',
  SYNC_RECOVER_ONLY_WHEN_HAS_SLTP: '仅当有止损/止盈单时补建',
  SYSTEM_ORDER_ID_PREFIX: '系统单 clientOrderId 前缀',
  SUNDAY_MAX_OPENS: '周日开仓上限',
  SUNDAY_MIN_SIGNAL_STRENGTH: '周日最低信号强度',
  NIGHT_HOURS_NO_OPEN_ENABLED: '晚间禁止开新仓',
  NIGHT_HOURS_START: '禁止开仓开始(时)',
  NIGHT_HOURS_END: '禁止开仓结束(时)',
  NIGHT_HOURS_ONLY_SUNDAY: '仅周六21点～周日06点',
  NO_OPEN_HOURS_BJ: '禁止开仓小时(北京)',
  MANUAL_REDUCED_SYMBOLS: '手动减仓交易对',
  MANUAL_BLOCKED_SYMBOLS: '手动停做交易对',
  MANUAL_REDUCED_SYMBOL_POSITION_FACTOR: '减仓名单仓位系数',
  MANUAL_REDUCED_SYMBOL_SIGNAL_BOOST: '减仓名单额外信号门槛',
  LOCK_PROFIT_STAGE1_TRIGGER_PCT: '第一层锁盈触发阈值',
  LOCK_PROFIT_STAGE1_PCT: '第一层锁盈比例',
  LOCK_PROFIT_STAGE2_TRIGGER_PCT: '第二层锁盈触发阈值',
  LOCK_PROFIT_STAGE2_PCT: '第二层锁盈比例',
  SCAN_STAGGER_BY_ACCOUNT: '多账号错峰扫描',
  SCAN_STAGGER_RANDOM: '错峰随机模式',
  SCAN_STAGGER_MIN_SEC: '错峰最小延迟(秒)',
  SCAN_STAGGER_MAX_SEC: '错峰最大延迟(秒)',
  SCAN_STAGGER_SEC: '固定错峰步长(秒)',
  STAGNATION_EARLY_EXIT_ENABLED: '滞涨早止盈开关',
  STAGNATION_MIN_RUNUP_PCT: '滞涨判定最低历史浮盈(%)',
  STAGNATION_NO_NEW_HIGH_HOURS: '滞涨判定未创新高时长(小时)',
  STAGNATION_PARTIAL_CLOSE_PCT: '滞涨触发时先平仓位比例',
  STAGNATION_LOCK_PCT: '滞涨后抬止损至少锁利(%)',
  ENTRY_PULLBACK_FILTER_ENABLED: '回撤区间入场过滤',
  ENTRY_PULLBACK_INTERVAL: '回撤过滤K线周期(空=跟ENTRY)',
  ENTRY_PULLBACK_LOOKBACK_BARS: '回撤统计K线根数',
  ENTRY_PULLBACK_MIN_BARS: '回撤过滤最少K线数',
  ENTRY_PULLBACK_MAX_LONG_IN_RANGE: '做多区间位置上限(0~1)',
  ENTRY_PULLBACK_MIN_SHORT_IN_RANGE: '做空区间位置下限(0~1)',
  SHADOW_MODE_AUTO_APPLY: '影子模式自动应用建议',
  SHADOW_MODE_MIN_CONFIDENCE: '影子模式最小置信度',
  SHADOW_MODE_SUGGESTIONS_PATH: '影子建议JSON路径',
  SHADOW_MODE_TRACKING_PATH: '影子跟踪准确率路径',
  SHADOW_MODE_INCREASE_LEVERAGE_MULT: '影子加仓名单杠杆乘数',
  SHADOW_MODE_DECREASE_LEVERAGE_MULT: '影子减仓名单杠杆乘数',
}

// 配置项详细说明（用于导出的策略配置分析，含建议与参数说明）
const CONFIG_GUIDE_DETAILS = {
  SCAN_INTERVAL: '扫描间隔（秒）。系统每隔多长时间扫描一次市场寻找交易机会。值越小扫描越频繁，能更快捕捉波动，但更容易产生噪音与过度交易。建议：低频波段1800秒(30分钟)或更长；稳健/保守可3600秒(1小时)；高频不建议长期使用。',
  MIN_CHANGE_PERCENT: '最小涨跌幅阈值（%）。只有24小时涨跌幅达到此值的交易对才会被考虑交易。值越小捕捉机会越多，但可能包含噪音和假信号。值越大只捕捉大幅波动，信号质量更高但机会更少。建议：保守策略2.0-3.0%，平衡策略1.5-2.0%，激进策略1.0-1.5%。',
  MIN_SIGNAL_STRENGTH: '最小信号强度（0-10）。技术指标综合评分，只有达到此强度的信号才会执行交易。值越小机会越多但噪音更大；值越大更偏"精选高质量"。建议：低频波段建议≥8；一般保守5-7；平衡4-5；高频3-4（不推荐长期）。',
  TOP_N_SYMBOLS: '每次扫描后处理的交易对数量。从符合条件的交易对中选择涨跌幅最大的前N个进行详细分析。值越大机会越多，但计算量增加，API请求增多。建议：保守策略8-10个，平衡策略12-15个，激进策略15-20个。',
  MAX_SCAN_SYMBOLS: '扫描的最大交易对数量（0表示扫描所有）。限制每次扫描时处理的交易对总数，减少API请求和计算量。值越小扫描越快，但可能错过一些机会。值越大覆盖更全面。建议：保守策略100-200个，平衡策略200-300个，激进策略300-500个。',
  MIN_VOLATILITY: '最小波动率（小数形式，如0.02表示2%）。过滤掉波动率低于此值的交易对，确保只交易有足够波动的币种。建议：0.015-0.025（1.5%-2.5%）。',
  MIN_VOLUME_24H: '最小24小时成交量（USDT）。过滤掉24小时交易量低于此值的交易对，确保只交易流动性好的币种，避免滑点和流动性风险。建议：≥500万USDT，主流币种可设置1000万以上。',
  KLINE_INTERVAL: 'K线数据周期。获取K线数据的基础周期，影响技术指标的计算粒度。建议：5m-1h，通常与PRIMARY_INTERVAL保持一致。',
  PRIMARY_INTERVAL: '主周期。策略分析的主要时间周期，用于计算技术指标（RSI、MACD、布林带等）。建议：保守策略4h-1d，平衡策略1h-4h，激进策略15m-1h。',
  CONFIRM_INTERVAL: '确认周期。用于确认交易信号的更长时间周期，增加信号的可靠性。建议：保守策略1d，平衡策略4h-1d，激进策略1h-4h。',
  ENTRY_INTERVAL: '入场周期。用于精确入场的短时间周期，优化入场点。建议：保守策略1h，平衡策略15m-30m，激进策略5m-15m。',
  MAX_POSITION_PERCENT: '单笔最大保证金占用（账户余额的百分比，如0.02表示2%）。值越小越保守，适合回归波段交易、降低单笔波动对账户的伤害。注意：币安合约有最小名义价值要求（通常≥5 USDT）。',
  MAX_TOTAL_POSITION_PERCENT: '总保证金上限（账户余额的百分比，如0.20表示20%）。所有持仓占用的保证金之和不能超过该上限，防止过度分散和风险集中。',
  MIN_POSITION_PERCENT: '单笔最小保证金占用（账户余额的百分比）。0表示不限制。用于避免过小仓位。',
  STOP_LOSS_PERCENT: '止损百分比（如0.08表示8%，相对于保证金）。当亏损达到此百分比时自动平仓止损。建议：保守策略10-15%，平衡策略8-10%，激进策略5-8%。盈亏比建议至少1:1.5。',
  TAKE_PROFIT_PERCENT: '止盈百分比（如0.15表示15%，相对于保证金）。建议：保守策略20-30%，平衡策略15-20%，激进策略10-15%。应大于止损，盈亏比至少1:1.5。',
  MIN_STOP_LOSS_PRICE_PCT: '最小止损价格变动百分比。用于防止止损过紧。建议：1.5%-3%。',
  MIN_TAKE_PROFIT_PRICE_PCT: '最小止盈价格变动百分比。用于防止止盈过紧。建议：2%-4%。',
  LEVERAGE: '交易杠杆倍数。杠杆越高，相同仓位下需要的保证金越少，但风险越大。建议：保守策略5-10倍，平衡策略10倍，激进策略10-15倍。高杠杆会增加爆仓风险。',
  USE_TRAILING_STOP: '是否启用移动止损。启用后，当盈利达到激活阈值时，止损会自动跟踪价格，保护利润。适合趋势行情。建议：平衡和激进策略启用，保守策略可关闭。',
  SMART_ENTRY_ENABLED: '智能入场开关。开启时会进行"限价回调 + 追价 +（趋势强时）市价兜底"；关闭时回归"纯限价单模式"，更适合低频波段与控频。',
  AUTO_TRADE_ONLY_TRENDING: '自动交易仅在市场状态=trending时执行（ranging/unknown只生成推荐，不自动下单）。能显著降低震荡扫损，但也会减少出单。',
  AUTO_TRADE_ALLOW_RANGING: '当不强制 only_trending 时，是否允许在震荡市自动交易。默认关闭，避免横盘来回扫损。',
  AUTO_TRADE_ALLOW_UNKNOWN: '当 marketRegime=unknown（K线不足或未判定）时，是否允许自动交易。默认关闭更稳健。',
  AUTO_TRADE_ALLOW_4H_NEUTRAL: '是否允许4H趋势=neutral时自动交易。关闭可减少震荡扫损；开启会显著增加出单（需配合更严格的信号门槛）。',
  RANGING_MARKET_SIGNAL_BOOST: '震荡市额外提高的最小信号门槛。比如 2 表示在基础 MIN_SIGNAL_STRENGTH 上再 +2，只保留更强的震荡市信号。',
  LIMIT_ORDER_OFFSET_PCT: '限价入场偏移（%）。BUY 挂在当前价下方，SELL 挂在当前价上方。值越小越贴近当前价，更容易成交；值越大更保守。建议：0.05%-0.20%。',
  ENTRY_CONFIRM_TIMEOUT_SEC: '下单后等待成交的确认超时（秒）。在纯限价模式下，超时未成交会撤单并跳过。建议：60-180秒。',
  ENTRY_CHASE_MAX_STEPS: '智能入场最大追价步数。步数越多越不容易错过，但也更接近追价；建议 2-4 步。',
  ENTRY_STEP_WAIT_SEC: '智能入场每一步等待成交时间（秒）。建议 15-25 秒。',
  ENTRY_MARKET_FALLBACK_AFTER_SEC: '趋势强时市价兜底延迟（秒）。建议 45-90 秒。',
  ENTRY_MAX_DRIFT_PCT_TRENDING: '趋势强时最大追价偏离（%）。例如 0.3 表示 0.3%。越小越保守；建议 0.2%-0.6%。',
  ENTRY_MAX_DRIFT_PCT_RANGING: '震荡/弱趋势最大追价偏离（%）。建议 0.1%-0.3%。',
  TRAILING_STOP_ACTIVATION: '移动止损激活阈值（如0.01表示1%）。当盈利达到此百分比时，移动止损开始跟踪价格。建议：1-2%。',
  TRAILING_STOP_PROTECT: '移动止损保护利润（如0.01表示1%）。当价格从最高点回撤达到此百分比时触发止损。建议：1-2%。',
  BETA_FILTER_ENABLED: '大盘共振过滤：当BTC/ETH短周期下跌时屏蔽多单，避免逆势开多。建议在大盘明显下跌时保持开启。',
  BETA_FILTER_THRESHOLD: '大盘共振阈值（比例，如-0.005表示-0.5%）。当BTC/ETH 15m或1h涨跌幅低于此值时触发过滤，屏蔽多单。',
  MARKET_SCHEME: '市场方案：bull(多头市)/bear(熊市)/normal(正常)/conservative(保守)。用于根据当前市场环境调整策略参数。',
  USE_ATR_STOP_LOSS: '是否使用ATR动态止损（优先于固定百分比）。ATR根据波动率计算止损距离，更适应不同市场。',
  ATR_STOP_LOSS_MULTIPLIER: 'ATR止损倍数。建议 2.0-3.0。放宽止损可以给波动留出空间，提高胜率。',
  ATR_TAKE_PROFIT_MULTIPLIER: 'ATR止盈倍数。建议 1.0-2.0。对应盈亏比 1:1 到 2:1。',
  USE_FIXED_RISK_SIZING: '使用固定风险百分比计算仓位（凯利公式）。启用后，每笔单子承受的风险固定为 FIXED_RISK_PERCENT，避免大额亏损。',
  FIXED_RISK_PERCENT: '每笔单子承受的风险百分比（相对于总资金）。例如 0.02 表示 2%。',
  AUTO_TRADE_ENABLED: '自动交易总开关。关闭后仅生成推荐，不会自动下单，适合先观察/体验。',
  MAX_OPEN_POSITIONS: '同时持仓数量上限（防止仓位过多/难管理）。建议 1-5。',
  MAX_DAILY_ENTRIES: '每日最多开仓次数（防止高频下单/过度交易）。建议 3-15。',
  SUNDAY_MAX_OPENS: '周日最多开仓次数。0=不限制（与每日一致）；2-3 可降低周日亏损又不完全停单。',
  SUNDAY_MIN_SIGNAL_STRENGTH: '周日最低信号强度(0-10)。仅当>=此值才开仓，0=不提高。建议 8-9。',
  NIGHT_HOURS_NO_OPEN_ENABLED: '晚间/凌晨禁止开新仓。开启后 21:00～06:00（北京）不开新仓。',
  NIGHT_HOURS_START: '禁止开仓开始小时（北京），含',
  NIGHT_HOURS_END: '禁止开仓结束小时（北京），不含',
  NIGHT_HOURS_ONLY_SUNDAY: 'True=仅周六21:00～周日06:00禁止；False=每天21:00～06:00禁止',
  NO_OPEN_HOURS_BJ: '禁止开仓小时（北京），逗号分隔如 17,19,22 表示该整点不开新仓；空则不限制。按历史亏损时段配置。',
  MANUAL_REDUCED_SYMBOLS: '手动减仓交易对列表，支持逗号、空格或换行分隔。命中后仅缩小仓位并提高自动交易门槛，不会完全停做。',
  MANUAL_BLOCKED_SYMBOLS: '手动停做交易对列表，支持逗号、空格或换行分隔。命中后直接禁止自动开仓，仅保留平仓与手动干预。',
  MANUAL_REDUCED_SYMBOL_POSITION_FACTOR: '手动减仓交易对的仓位系数。比如 0.5 表示此类币只按正常仓位的 50% 下单。',
  MANUAL_REDUCED_SYMBOL_SIGNAL_BOOST: '手动减仓交易对额外提高的最小信号门槛。比如 1 表示在原门槛基础上再 +1，用于减少边缘信号开仓。',
  LOCK_PROFIT_STAGE1_TRIGGER_PCT: '分层锁盈第一层触发阈值（相对保证金，如 0.08=8%）。达到后会把止损从保本抬到小幅锁利。',
  LOCK_PROFIT_STAGE1_PCT: '分层锁盈第一层锁利比例（相对保证金，如 0.02=2%）。',
  LOCK_PROFIT_STAGE2_TRIGGER_PCT: '分层锁盈第二层触发阈值（相对保证金，如 0.15=15%）。达到后进一步抬高止损，减少利润回吐。',
  LOCK_PROFIT_STAGE2_PCT: '分层锁盈第二层锁利比例（相对保证金，如 0.05=5%）。',
  BINANCE_API_KEY: '币安API密钥。用于访问币安账户的凭证，需要启用"合约交易"权限。请妥善保管，不要泄露。',
  BINANCE_API_SECRET: '币安API密钥Secret。与API Key配对使用，用于签名验证。请妥善保管，不要泄露。',
  USE_TESTNET: '是否使用币安测试网。true表示模拟交易（无真实资金），false表示真实交易。',
  MIN_MARGIN_USDT: '最小单笔保证金（USDT）。低于此值不下单，用于满足币安合约最小名义价值要求。',
  MIN_VOLUME_24H_STRICT: '严格成交量过滤（USD）。24H Volume 低于此值的交易对直接剔除，比 MIN_VOLUME_24H 更严格。',
  SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT: '智能补单额外候选数。当 TOP_N 中部分因冷却等被跳过时，仍会尝试这批额外候选，避免无单可下。',
  EXCLUDE_MAJOR_COINS: '是否排除主流币（BTC、ETH、BNB等）。开启后专注于山寨币策略。',
  TAKE_PROFIT_1_PERCENT: '分步止盈第一目标（保证金百分比，如0.15=15%）。第一目标触发后了结50%仓位，剩余追求第二目标。',
  TRADING_PROFILE: '交易预设：conservative(稳健)/fast(快速验证)。conservative 偏向低频+高门槛；fast 偏向高频+宽松过滤。',
  ATR_PERIOD: 'ATR 计算周期（K线数量）。默认 14，影响 ATR 动态止损的敏感度。',
  SYMBOL_LOSS_COOLDOWN_ENABLED: '连续亏损冷却开关。开启后，同一交易对连续亏损达到阈值则禁止交易一段时间，避免连续亏损。',
  SYMBOL_MAX_CONSECUTIVE_LOSSES: '最大允许连续亏损次数。超过后触发该交易对的冷却。',
  SYMBOL_LOSS_COOLDOWN_SEC: '连续亏损后的冷却时间（秒），默认 3600（1小时）。',
  SCAN_STAGGER_BY_ACCOUNT: '多账号错峰扫描总开关。开启后，每个账号在首次启动扫描前会延迟一段时间，避免多个账号同时对币安扫行情/下单，减少并发与限频风险；单账号可以关闭。',
  SCAN_STAGGER_RANDOM: '错峰模式选择：True=在 [SCAN_STAGGER_MIN_SEC, SCAN_STAGGER_MAX_SEC] 区间内随机延迟（更分散且不规则）；False=按账号ID固定间隔延迟 (account_id-1)*SCAN_STAGGER_SEC（可预测）。',
  SCAN_STAGGER_MIN_SEC: '随机错峰模式下的最小延迟秒数。例如 10 表示至少延迟 10 秒。建议：多账号时可设 10-30。',
  SCAN_STAGGER_MAX_SEC: '随机错峰模式下的最大延迟秒数。例如 120 表示至多延迟 120 秒。建议：多账号时可设 60-300，根据你能接受的最大启动等待时间调整。',
  SCAN_STAGGER_SEC: '固定错峰模式下每个账号之间的步长（秒）。实际延迟 = (account_id-1)*SCAN_STAGGER_SEC。例如 account_id=3 且步长 60，则延迟 120 秒后开始扫描。',
  STAGNATION_EARLY_EXIT_ENABLED: '滞涨早止盈：当持仓曾达到一定浮盈后，长时间未再创新高（动能衰减）时，系统会主动执行“先部分平仓锁定利润 + 对剩余仓位抬高止损”的组合动作。建议只在回测/实盘验证效果良好后再开启。',
  STAGNATION_MIN_RUNUP_PCT: '触发滞涨早止盈前，历史最高浮盈至少要达到的百分比（基于保证金）。例如 10 表示曾浮盈≥10% 才会进入滞涨监控，防止对小级别波动过度敏感。',
  STAGNATION_NO_NEW_HIGH_HOURS: '从最近一次创历史新高开始计时，如果连续这么多小时都没有再创新高（价格高点停滞），则视为“滞涨”，会触发滞涨早止盈逻辑。建议 2-4 小时区间，根据周期调整。',
  STAGNATION_PARTIAL_CLOSE_PCT: '滞涨触发后，首先立即平掉的仓位比例，用于锁定一部分利润、降低回撤风险。例如 0.5 表示先平掉 50% 仓位，剩余 50% 继续观察。',
  STAGNATION_LOCK_PCT: '滞涨触发后，对剩余仓位抬升止损时，至少要锁住的利润百分比（基于保证金）。实际使用时会与“历史最高浮盈的一半”取较大值，用于在强势趋势中多保留利润空间，在一般情况下则锁定一个稳定的收益区间。',
  ENTRY_PULLBACK_FILTER_ENABLED: '开启后：开仓前用近 N 根 K 线的最低价/最高价构成区间，若做多时现价已贴在区间上沿（相对位置过高）则跳过，避免追高后一回撤就损；做空对称。与「趋势信号价追价上限」(USE_TREND_ENTRY_FILTER) 互补。',
  ENTRY_PULLBACK_INTERVAL: '计算区间所用的 K 线周期。留空则跟随 ENTRY_INTERVAL（一般为 1h）。想更敏感可填 15m。',
  ENTRY_PULLBACK_LOOKBACK_BARS: '参与统计的 K 线根数，默认 24。越大区间越宽、过滤越钝；越小越敏感。',
  ENTRY_PULLBACK_MIN_BARS: '至少几根 K 才启用本过滤，不足则跳过（避免数据过少误判）。',
  ENTRY_PULLBACK_MAX_LONG_IN_RANGE: '做多允许的最高相对位置 pos（0=区间底，1=区间顶）。默认 0.62 表示超过则视为贴近前高/上沿，不开多。想更严可改为 0.55～0.58。',
  ENTRY_PULLBACK_MIN_SHORT_IN_RANGE: '做空允许的最低相对位置。默认 0.38，低于则视为贴近前低/下沿，不开空。想更严可改为 0.42～0.45。',
}

const ConfigItem = ({ label, config, onUpdate, disabled }) => {
  const isPercentKey = label.includes('PERCENT') || label.includes('PCT')
  const PCT_LIKE_KEYS = new Set([
    'LIMIT_ORDER_OFFSET_PCT',
    'ENTRY_MAX_DRIFT_PCT_TRENDING',
    'ENTRY_MAX_DRIFT_PCT_RANGING',
    // FIXED_RISK_PERCENT 已移除，直接显示小数
  ])
  const isPctLike = PCT_LIKE_KEYS.has(label)
  const isNumberAsIs = NUMBER_AS_IS_KEYS.has(label)
  const baseLabel = KEY_LABELS[label]
  const displayLabel = baseLabel ? `${baseLabel} (${label})` : label

  const formatPercent = (n) => {
    if (typeof n !== 'number' || isNaN(n)) return ''
    return n.toFixed(4).replace(/\.?0+$/, '')
  }

  const getInitialDisplayValue = (val) => {
    if (config.type === 'number' && isNumberAsIs) {
      if (val === null || val === undefined || val === '') return ''
      const numVal = typeof val === 'string' ? parseFloat(val) : val
      return isNaN(numVal) ? '' : String(numVal)
    }
    if (config.type === 'number' && isPercentKey) {
      if (val === null || val === undefined || val === '') {
        return ''
      }
      const numVal = typeof val === 'string' ? parseFloat(val) : val
      if (isNaN(numVal)) {
        return ''
      }
      if (isPctLike) {
        return formatPercent(numVal)
      }
      return formatPercent(numVal)
    }
    return val === null || val === undefined ? '' : val
  }

  const [localValue, setLocalValue] = useState(getInitialDisplayValue(config.value))
  const [isEditing, setIsEditing] = useState(false)

  useEffect(() => {
    setIsEditing(false)
    setLocalValue(getInitialDisplayValue(config.value))
  }, [config.value])

  const handleChange = (newValue) => {
    setLocalValue(newValue)
    setIsEditing(true)
  }

  const handleBlur = () => {
    if (!isEditing) return
    let finalValue = localValue
    if (config.type === 'number') {
      if (isNumberAsIs) {
        const numVal = parseFloat(localValue)
        if (isNaN(numVal)) {
          setLocalValue(getInitialDisplayValue(config.value))
          setIsEditing(false)
          return
        }
        finalValue = numVal
      } else if (isPercentKey) {
        const numVal = parseFloat(localValue)
        if (isNaN(numVal)) {
          setLocalValue(getInitialDisplayValue(config.value))
          setIsEditing(false)
          return
        }
        
        if (numVal < 0) {
           setLocalValue(getInitialDisplayValue(config.value))
           setIsEditing(false)
           return
        }
        
        finalValue = numVal
      } else {
        finalValue = parseFloat(localValue)
        if (isNaN(finalValue)) {
          setLocalValue(getInitialDisplayValue(config.value))
          setIsEditing(false)
          return
        }
      }
    } else if (config.type === 'boolean') {
      finalValue = localValue === 'true' || localValue === true
    } else {
      finalValue = localValue
    }
    onUpdate(finalValue)
    setIsEditing(false)
  }

  const displayValue = isEditing ? localValue : getInitialDisplayValue(config.value)

  return (
    <div className="config-item">
      <div className="config-item-header">
        <label>{displayLabel}</label>
      </div>
      <div className="config-input-wrapper">
        {config.type === 'boolean' ? (
          <select
            value={String(config.value || false)}
            onChange={(e) => {
              const boolValue = e.target.value === 'true'
              onUpdate(boolValue)
            }}
            disabled={disabled}
            style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }}
          >
            <option value="true">是</option>
            <option value="false">否</option>
          </select>
        ) : (
          <input
            type="text"
            inputMode={config.type === 'number' ? 'decimal' : 'text'}
            value={displayValue === '' ? '' : String(displayValue)}
            onChange={(e) => {
              let newValue = e.target.value
              if (config.type === 'number' && !isNumberAsIs) {
                if (isPercentKey) {
                  const numValue = parseFloat(newValue)
                  // 移除 > 1 的限制，允许输入百分比数值（如 30）
                  if (newValue !== '' && !isNaN(numValue) && numValue < 0) {
                    return
                  }
                }
              }
              handleChange(newValue)
            }}
            onBlur={handleBlur}
            onKeyDown={(e) => {
            if (e.key === 'Enter') {
              handleBlur()
            }
          }}
            disabled={disabled}
            style={{ width: '100%', padding: '8px', border: '1px solid #ddd', borderRadius: '4px' }}
          />
        )}
      </div>
      {config.description && (
        <div style={{ fontSize: '12px', color: '#666', marginTop: '4px' }}>{config.description}</div>
      )}
    </div>
  )
}

const GlobalConfig = () => {
  const isAdmin = useSelector(selectIsAdmin)
  
  const [loading, setLoading] = useState(true)
  const [message, setMessage] = useState('')
  const [busy, setBusy] = useState(false)
  // 系统控制相关
  const [systemStatus, setSystemStatus] = useState(null)
  const [backendStatus, setBackendStatus] = useState(null)
  const [recommendationsStatus, setRecommendationsStatus] = useState(null)
  const [servicesSummary, setServicesSummary] = useState(null)
  const [marketOverview, setMarketOverview] = useState(null)
  const [showMarketJson, setShowMarketJson] = useState(false)
  const [strategyOverviewCollapsed, setStrategyOverviewCollapsed] = useState(true)
  const [systemBusy, setSystemBusy] = useState(false)
  
  // 预设方案相关
  const [configs, setConfigs] = useState({})
  const [saving, setSaving] = useState(false)
  
  // 配置快照相关
  const [showSnapshot, setShowSnapshot] = useState(false)
  const [snapshotText, setSnapshotText] = useState('')
  const [snapshotIncludeSecrets, setSnapshotIncludeSecrets] = useState(false)
  const [snapshotBusy, setSnapshotBusy] = useState(false)
  
  // 新增：搜索和 Tab 状态
  const [searchTerm, setSearchTerm] = useState('')
  const [activeTab, setActiveTab] = useState('all')

  const PCT_LIKE_KEYS = new Set([
    'LIMIT_ORDER_OFFSET_PCT',
    'ENTRY_MAX_DRIFT_PCT_TRENDING',
    'ENTRY_MAX_DRIFT_PCT_RANGING',
  ])

  // isAdmin 已从 Redux 获取，无需重复定义

  // 预设方案配置（必须在函数定义之前，常量定义）
  const presets = {
    altcoin: {
      name: '山寨币策略（推荐）',
      desc: '【安全推荐】与当前稳定版本一致：宽ATR止损 + 4x基础杠杆，小众币最高4x、总杠杆不超12x；1%风险/单，10%激活移动止损、2%保护，支持智能入场与趋势/大盘过滤。',
      // 只比这几项与数据库一致即视为选中，避免因缺项或格式差异永远不中
      signatureKeys: ['AUTO_TRADE_ONLY_TRENDING', 'RSI_EXTREME_REVERSE_ENABLED', 'USE_MARGIN_CAP_FOR_TP', 'USE_MARGIN_CAP_FOR_SL', 'MAX_POSITION_PERCENT', 'MAX_DAILY_ENTRIES', 'TOP_N_SYMBOLS', 'FIXED_RISK_PERCENT'],
      configs: {
        // 风险与止盈止损（与 DEFAULT_TRADING_CONFIG 的核心风险参数对齐）
        ATR_STOP_LOSS_MULTIPLIER: 2.0,
        STOP_LOSS_PERCENT: 0.05,        // 5% 基础止损（配合ATR动态止损）
        MIN_STOP_LOSS_PRICE_PCT: 0.03,
        MIN_TAKE_PROFIT_PRICE_PCT: 0.02,
        RISK_REWARD_RATIO: 1.5,
        TAKE_PROFIT_1_PERCENT: 0.15,
        TAKE_PROFIT_PERCENT: 0.25,
        MIN_RR_FOR_TP1: 1.2,
        MIN_HOLD_TIME_SEC: 0,
        USE_FIXED_RISK_SIZING: true,
        FIXED_RISK_PERCENT: 0.01,       // 1% 风险/单（回归盈利期风格）
        USE_DYNAMIC_ATR_MULTIPLIER: false,

        USE_TRAILING_STOP: true,
        TRAILING_STOP_ACTIVATION: 0.10, // 10% 激活移动止损（更早锁盈）
        TRAILING_STOP_PROTECT: 0.02,    // 2% 保护利润

        MAX_POSITION_PERCENT: 0.20,
        MAX_TOTAL_POSITION_PERCENT: 0.65,
        MIN_POSITION_PERCENT: 0.02,
        MAX_DAILY_ENTRIES: 15,
        SUNDAY_MAX_OPENS: 3,
        SUNDAY_MIN_SIGNAL_STRENGTH: 8,
        NIGHT_HOURS_NO_OPEN_ENABLED: true,
        NIGHT_HOURS_START: 21,
        NIGHT_HOURS_END: 6,
        NIGHT_HOURS_ONLY_SUNDAY: true,
        MAX_OPEN_POSITIONS: 4,
        LEVERAGE: 4,
        MAX_LEVERAGE: 12,
        MIN_LEVERAGE: 4,
        MAX_LEVERAGE_SMALL_CAP: 4,
        USE_DYNAMIC_LEVERAGE: true,

        MIN_VOLUME_24H: 30000000,
        MIN_VOLATILITY: 0.03,
        MIN_CHANGE_PERCENT: 2.0,
        TOP_N_SYMBOLS: 30,
        MAX_SCAN_SYMBOLS: 500,
        MIN_SIGNAL_STRENGTH: 8,
        EXCLUDE_MAJOR_COINS: true,
        SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT: 20,
        SMART_ENTRY_ENABLED: true,

        SCAN_INTERVAL: 900,
        PRIMARY_INTERVAL: '4h',
        ENTRY_INTERVAL: '1h',
        CONFIRM_INTERVAL: '1d',

        AUTO_TRADE_ONLY_TRENDING: true,
        AUTO_TRADE_ALLOW_4H_NEUTRAL: false,
        MAX_RSI_FOR_LONG: 65,
        MIN_RSI_FOR_SHORT: 30,
        MAX_CHANGE_PERCENT_FOR_LONG: 25,
        MAX_CHANGE_PERCENT_FOR_SHORT: 10,

        SYMBOL_LOSS_COOLDOWN_ENABLED: true,
        SYMBOL_MAX_CONSECUTIVE_LOSSES: 2,
        SYMBOL_LOSS_COOLDOWN_SEC: 3600,

        BETA_FILTER_ENABLED: true,
        BETA_FILTER_THRESHOLD: -0.005,   // 与默认配置保持一致
        ENTRY_SHORT_TREND_FILTER_ENABLED: true,
        MAX_TREND_MOVE_BEFORE_ENTRY: 0.04,
        // 与盈利期对齐：关闭 RSI 反向，避免趋势里逆势止损；止盈/止损封顶保持开启
        RSI_EXTREME_REVERSE_ENABLED: false,
        RSI_EXTREME_REVERSE_ONLY_NEUTRAL_4H: true,
        USE_MARGIN_CAP_FOR_TP: true,
        USE_MARGIN_CAP_FOR_SL: true,
      },
    },
    conservative: {
      name: '保守稳健（低风险）',
      desc: '【极低风险】适合新手或震荡市，0.5%风险/单，止损极严（5%强平保护）。避免大幅回撤。',
      configs: {
        SCAN_INTERVAL: 900,
        MIN_CHANGE_PERCENT: 0.02, // 2% 涨跌幅
        MIN_SIGNAL_STRENGTH: 5,
        TOP_N_SYMBOLS: 10,
        MAX_POSITION_PERCENT: 0.05, // 5% 单仓上限
        MAX_LEVERAGE: 5, // 5x 低杠杆
        MAX_SCAN_SYMBOLS: 150,
        MIN_VOLATILITY: 0.02,
        STOP_LOSS_PERCENT: 0.05, // 5% 强平保护
        TAKE_PROFIT_PERCENT: 0.15, // 15% 止盈
        TAKE_PROFIT_1_PERCENT: 0.10, // 10% 第一止盈
        MIN_STOP_LOSS_PRICE_PCT: 0.025,
        MIN_TAKE_PROFIT_PRICE_PCT: 0.02,
        USE_TRAILING_STOP: false,
        ATR_STOP_LOSS_MULTIPLIER: 2.0,
        ATR_TAKE_PROFIT_MULTIPLIER: 1.5,
        RISK_REWARD_RATIO: 1.5,
        MIN_HOLD_TIME_SEC: 1800,
        USE_DYNAMIC_ATR_MULTIPLIER: false,
        USE_FIXED_RISK_SIZING: true,
        FIXED_RISK_PERCENT: 0.005, // 0.5% 风险
      }
    },
    swing: {
      name: '波段趋势（中线）',
      desc: '【中线推荐】关闭智能入场，回归纯限价+30分钟扫描。1%风险/单，适合捕捉日内趋势。',
      configs: {
        SCAN_INTERVAL: 1800,
        TOP_N_SYMBOLS: 8,
        MAX_POSITION_PERCENT: 0.10,
        MAX_TOTAL_POSITION_PERCENT: 0.45,
        MAX_LEVERAGE: 10,
        MIN_POSITION_PERCENT: 0.0,
        MIN_SIGNAL_STRENGTH: 8,
        USE_TRAILING_STOP: false,
        ATR_STOP_LOSS_MULTIPLIER: 2.0,
        ATR_TAKE_PROFIT_MULTIPLIER: 1.5,
        RISK_REWARD_RATIO: 1.5,
        TAKE_PROFIT_PERCENT: 0.30, // 30% 止盈
        TAKE_PROFIT_1_PERCENT: 0.20, // 20% 第一止盈
        STOP_LOSS_PERCENT: 0.10, // 10% 止损
        MIN_STOP_LOSS_PRICE_PCT: 0.025,
        MIN_TAKE_PROFIT_PRICE_PCT: 0.02,
        MIN_HOLD_TIME_SEC: 1800,
        SMART_ENTRY_ENABLED: false,
        USE_DYNAMIC_ATR_MULTIPLIER: false,
        USE_FIXED_RISK_SIZING: true,
        FIXED_RISK_PERCENT: 0.01, // 1% 风险
      },
    },
    strict: {
      name: '严格精选（高胜率）',
      desc: '更偏"少单、质量优先"：仅趋势行情自动交易 + 4H中性不自动下单 + 更高信号门槛。',
      configs: {
        SCAN_INTERVAL: 1800,
        TOP_N_SYMBOLS: 6,
        MAX_POSITION_PERCENT: 0.12,
        MAX_LEVERAGE: 20,
        MIN_SIGNAL_STRENGTH: 8,
        AUTO_TRADE_ONLY_TRENDING: true,
        AUTO_TRADE_ALLOW_4H_NEUTRAL: false,
        SMART_ENTRY_ENABLED: false,
        LIMIT_ORDER_OFFSET_PCT: 0.001, // 0.1% 修正为 0.001 (假设 PCT key 需为小数)
        ENTRY_CONFIRM_TIMEOUT_SEC: 180,
        USE_TRAILING_STOP: false,
        ATR_STOP_LOSS_MULTIPLIER: 2.0,
        ATR_TAKE_PROFIT_MULTIPLIER: 1.5,
        RISK_REWARD_RATIO: 1.5,
        TAKE_PROFIT_PERCENT: 0.30,
        TAKE_PROFIT_1_PERCENT: 0.20,
        STOP_LOSS_PERCENT: 0.08, // 8% 止损
        MIN_STOP_LOSS_PRICE_PCT: 0.025,
        MIN_TAKE_PROFIT_PRICE_PCT: 0.02,
        MIN_HOLD_TIME_SEC: 1800,
        USE_DYNAMIC_ATR_MULTIPLIER: false,
        USE_FIXED_RISK_SIZING: true,
        FIXED_RISK_PERCENT: 0.01, // 1% 风险
      },
    },
    // 市场状态方案：便于在不同行情间一键切换（熊市/正常/牛市/保守）
    scheme_bear: {
      name: '熊市',
      desc: '【熊市专用】放宽止损5%、单仓≤8%、4H下跌禁止开多、4H上涨禁止开空、大盘过滤开启。避免逆势抄底/做空、减少回撤。',
      signatureKeys: ['MARKET_SCHEME'],
      configs: {
        MARKET_SCHEME: 'bear',
        MIN_STOP_LOSS_PRICE_PCT: 0.05,
        MAX_POSITION_PERCENT: 0.08,
        ATR_STOP_LOSS_MULTIPLIER: 2.5,
        BLOCK_LONG_WHEN_4H_DOWN: true,
        BLOCK_SHORT_WHEN_4H_UP: true,
        BETA_FILTER_ENABLED: true,
      },
    },
    scheme_normal: {
      name: '正常',
      desc: '【常规市场】止损3%、单仓12%、4H下跌可开多、4H上涨禁止开空。适合震荡或温和行情。',
      signatureKeys: ['MARKET_SCHEME'],
      configs: {
        MARKET_SCHEME: 'normal',
        MIN_STOP_LOSS_PRICE_PCT: 0.03,
        MAX_POSITION_PERCENT: 0.12,
        ATR_STOP_LOSS_MULTIPLIER: 2.5,
        BLOCK_LONG_WHEN_4H_DOWN: false,
        BLOCK_SHORT_WHEN_4H_UP: true,
      },
    },
    scheme_bull: {
      name: '牛市',
      desc: '【牛市专用】止损3%、单仓12%、4H下跌可开多、4H上涨禁止开空、ATR倍数略低。偏积极。',
      signatureKeys: ['MARKET_SCHEME'],
      configs: {
        MARKET_SCHEME: 'bull',
        MIN_STOP_LOSS_PRICE_PCT: 0.03,
        MAX_POSITION_PERCENT: 0.12,
        ATR_STOP_LOSS_MULTIPLIER: 2.0,
        BLOCK_LONG_WHEN_4H_DOWN: false,
        BLOCK_SHORT_WHEN_4H_UP: true,
      },
    },
    scheme_conservative: {
      name: '保守',
      desc: '【极保守】止损6%、单仓6%、4H下跌禁止开多、4H上涨禁止开空。最小仓位、最严趋势过滤。',
      signatureKeys: ['MARKET_SCHEME'],
      configs: {
        MARKET_SCHEME: 'conservative',
        MIN_STOP_LOSS_PRICE_PCT: 0.06,
        MAX_POSITION_PERCENT: 0.06,
        ATR_STOP_LOSS_MULTIPLIER: 2.5,
        BLOCK_LONG_WHEN_4H_DOWN: true,
        BLOCK_SHORT_WHEN_4H_UP: true,
        BETA_FILTER_ENABLED: true,
      },
    },
    aggressive: {
      name: '激进短线（高风险）',
      desc: '针对晚间高波动：2%风险/单，交易频率高，止损较紧但止盈合理。注意：回撤风险较高。',
      configs: {
        SCAN_INTERVAL: 300,
        MIN_CHANGE_PERCENT: 0.01, // 1% 涨跌幅
        MIN_SIGNAL_STRENGTH: 3,
        TOP_N_SYMBOLS: 18,
        MAX_SCAN_SYMBOLS: 350,
        MIN_VOLATILITY: 0.015,
        STOP_LOSS_PERCENT: 0.10, // 10% 止损
        TAKE_PROFIT_PERCENT: 0.40, // 40% 止盈
        TAKE_PROFIT_1_PERCENT: 0.25, // 25% 第一止盈
        MIN_STOP_LOSS_PRICE_PCT: 0.025,
        MIN_TAKE_PROFIT_PRICE_PCT: 0.02,
        USE_TRAILING_STOP: false,
        ATR_STOP_LOSS_MULTIPLIER: 2.5,
        ATR_TAKE_PROFIT_MULTIPLIER: 1.5,
        RISK_REWARD_RATIO: 1.5,
        MIN_HOLD_TIME_SEC: 1800,
        USE_DYNAMIC_ATR_MULTIPLIER: false,
        USE_FIXED_RISK_SIZING: true,
        FIXED_RISK_PERCENT: 0.02, // 2% 风险
      }
    }
  }

  // 已知全局配置项默认值（兜底：后端未返回时前端仍能显示，避免看不到新配置项）
  const KNOWN_GLOBAL_CONFIG_DEFAULTS = {
    MAX_RSI_FOR_LONG: { value: 65, type: 'number', category: 'strategy', description: '做多时 RSI 超过此值则不开多（2026-02-12：65 避免追高）。' },
    MAX_CHANGE_PERCENT_FOR_LONG: { value: 25, type: 'number', category: 'strategy', description: '做多时 24h 涨跌幅超过此值则不开多（避免追大涨）。单位：百分比数值，如 25 表示 25%。' },
    MIN_RSI_FOR_SHORT: { value: 30, type: 'number', category: 'strategy', description: '做空时 RSI 低于此值则不做空（避免深超卖反弹）。2026-01-31新增。' },
    MAX_CHANGE_PERCENT_FOR_SHORT: { value: 10, type: 'number', category: 'strategy', description: '做空时 24h 涨跌幅超过此值则不做空（24h 仍大涨时不做空）。单位：百分比数值。' },
    TAKE_PROFIT_1_PERCENT: { value: 0.2, type: 'number', category: 'strategy', description: '分步止盈第一目标（保证金百分比，如 0.2=20%）。2026-02-12 提高以改善盈亏比。' },
    SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT: { value: 20, type: 'number', category: 'scan', description: '智能补单：多返回的候选数量，冷却时仍可尝试后续交易对。' },
    SYMBOL_LOSS_COOLDOWN_ENABLED: { value: true, type: 'boolean', category: 'strategy', description: '是否启用同一交易对连续亏损后的冷却。' },
    SYMBOL_MAX_CONSECUTIVE_LOSSES: { value: 2, type: 'number', category: 'strategy', description: '最大允许连续亏损次数（超过则禁止交易该交易对一段时间）。' },
    SYMBOL_LOSS_COOLDOWN_SEC: { value: 3600, type: 'number', category: 'strategy', description: '连续亏损后的冷却时间（秒），默认1小时。' },
    MIN_RR_FOR_TP1: { value: 1.5, type: 'number', category: 'strategy', description: '第一目标止盈相对止损的最小盈亏比（如 1.5 表示 TP1 至少为止损距离的 1.5 倍）。2026-02-12 新增。' },
    POSITION_SCALE_FACTOR: { value: 1.0, type: 'number', category: 'risk', description: '仓位放大系数：1.0=正常，1.2=+20% 仓位，1.5=+50%，上限 2.0。盈利时适度调高可扩大收益，仍受单笔上限约束。' },
    AUTO_TRADE_ONLY_TRENDING: { value: true, type: 'boolean', category: 'strategy', description: '仅在趋势市自动交易。' },
    AUTO_TRADE_ALLOW_RANGING: { value: false, type: 'boolean', category: 'strategy', description: '当不强制 only_trending 时，是否允许在震荡市自动交易。' },
    AUTO_TRADE_ALLOW_UNKNOWN: { value: false, type: 'boolean', category: 'strategy', description: '当市场状态未判定时，是否允许自动交易。' },
    AUTO_TRADE_ALLOW_4H_NEUTRAL: { value: false, type: 'boolean', category: 'strategy', description: '是否允许 4H 趋势为中性时自动交易。关闭可减少震荡扫损、提升质量（建议关闭）。' },
    RANGING_MARKET_SIGNAL_BOOST: { value: 2, type: 'number', category: 'strategy', description: '震荡市额外提高的最小信号门槛。' },
    BETA_FILTER_ENABLED: { value: true, type: 'boolean', category: 'strategy', description: '大盘共振过滤：BTC/ETH 下跌时屏蔽多单。' },
    BETA_FILTER_THRESHOLD: { value: -0.005, type: 'number', category: 'strategy', description: '大盘共振阈值（比例，如 -0.005 表示 -0.5%）。' },
    RSI_EXTREME_REVERSE_ENABLED: { value: false, type: 'boolean', category: 'strategy', description: '开启后：原信号做多但 RSI 超买(≥做多上限)时改为做空；原信号做空但 RSI 超卖(≤做空下限)时改为做多。属均值回归思路，可填补超买超卖时不下单的空置；默认关闭。' },
    RSI_EXTREME_REVERSE_ONLY_NEUTRAL_4H: { value: true, type: 'boolean', category: 'strategy', description: '建议开启：仅在 4H 趋势为中性时允许 RSI 反向单，避免在强趋势里逆势抄底/摸顶，降低风险。关闭则反向可与 4H 同向（仍受“禁止逆4H趋势”约束）。' },
    TAKE_PROFIT_PERCENT: { value: 0.30, type: 'number', category: 'risk', description: '止盈百分比（保证金比例，如 25.0=25%）。' },
    STOP_LOSS_PERCENT: { value: 0.10, type: 'number', category: 'risk', description: '止损百分比（保证金比例，如 8.0=8%）。' },
    TRAILING_STOP_ACTIVATION: { value: 0.30, type: 'number', category: 'risk', description: '移动止损激活阈值（盈利达到此比例后激活，如 20.0=20%）。' },
    TRAILING_STOP_PROTECT: { value: 0.10, type: 'number', category: 'risk', description: '移动止损保护阈值（回撤至此比例时触发止损，如 10.0=10%）。' },
    LEVERAGE: { value: 8, type: 'number', category: 'risk', description: '基础杠杆倍数。' },
    MAX_POSITION_PERCENT: { value: 0.20, type: 'number', category: 'risk', description: '单笔最大保证金占可用资金比例（如 0.12=12%），加大可提高单笔盈利。' },
    MAX_LEVERAGE: { value: 20, type: 'number', category: 'risk', description: '动态杠杆上限（如 20 表示最高 20 倍），配合单笔仓位提高收益。' },
    MIN_LEVERAGE: { value: 8, type: 'number', category: 'risk', description: '动态杠杆下限（如 8 表示不低于 8 倍）。之前盈利阶段多为 8x，避免被压到 2–4x 导致单笔盈利过少。' },
    MAX_LEVERAGE_SMALL_CAP: { value: 8, type: 'number', category: 'risk', description: '高波动/小众币最大允许杠杆（与之前盈利阶段一致）。' },
    RISK_REWARD_RATIO: { value: 3, type: 'number', category: 'risk', description: '盈亏比目标（用于计算动态止盈止损，建议 3:1）。' },
    ATR_TAKE_PROFIT_MULTIPLIER: { value: 6.0, type: 'number', category: 'risk', description: 'ATR 止盈倍数。' },
    ATR_STOP_LOSS_MULTIPLIER: { value: 3.0, type: 'number', category: 'risk', description: 'ATR 止损倍数（2026-02-12：3 减少噪音止损）。' },
    USE_FIXED_RISK_SIZING: { value: true, type: 'boolean', category: 'risk', description: '是否使用固定风险仓位计算（基于止损距离和账户余额）。' },
    FIXED_RISK_PERCENT: { value: 0.03, type: 'number', category: 'risk', description: '每笔交易固定风险百分比（如 0.01=1%）。' },
    POSITION_DETAILED_LOG_ENABLED: { value: false, type: 'boolean', category: 'strategy', description: '是否启用持仓详细监控日志（记录每次检查的当前价/止损/止盈/ROE 等）。仅排查问题时临时打开，平时建议关闭。' },
    // 仓位/同步相关（保证全局配置页能看到）
    SYNC_RECOVER_MISSING_POSITIONS: { value: true, type: 'boolean', category: 'position', description: '同步时补建「币安有仓、DB 无记录」的交易记录。' },
    SYNC_RECOVER_ONLY_WHEN_HAS_SLTP: { value: true, type: 'boolean', category: 'position', description: '仅当该持仓存在止损/止盈单时才补建（未配置 SYSTEM_ORDER_ID_PREFIX 时生效）。' },
    SYSTEM_ORDER_ID_PREFIX: { value: 'SYS', type: 'string', category: 'position', description: '系统单标识：下单时写入 newClientOrderId 前缀，同步时仅对「开仓订单 clientOrderId 以此前缀开头」的持仓补建；设空则用「是否有止损止盈单」判断。' },
    MARKET_SCHEME: { value: 'normal', type: 'string', category: 'strategy', description: '市场方案：normal / bear / bull / conservative。切换后自动覆盖止损、仓位、4H趋势过滤等参数。' },
    BLOCK_LONG_WHEN_4H_DOWN: { value: false, type: 'boolean', category: 'strategy', description: '4H 趋势下跌时禁止开多。bear / conservative 方案下自动为 true。' },
    BLOCK_SHORT_WHEN_4H_UP: { value: true, type: 'boolean', category: 'strategy', description: '4H 趋势上涨时禁止开空。默认 true，避免逆势做空导致止损。' },
    AUTO_MARKET_SCHEME_ENABLED: { value: false, type: 'boolean', category: 'strategy', description: '开启后，crontab 定时运行 update_market_scheme.py --apply 时自动更新 MARKET_SCHEME（根据 BTC 行情识别牛/熊/正常）。' },
    // 基于7天统计自动生成的全局过滤结果（仅展示，自动更新）
    STATS_SYMBOL_FILTERS: { value: {}, type: 'json', category: 'stats', description: '基于最近 N 天交易统计自动生成的 symbol 白/黑名单（软降权），由脚本 aggregate_trade_stats.py 自动更新，请勿手动修改。' },
    STATS_HOUR_FILTERS: { value: {}, type: 'json', category: 'stats', description: '基于最近 N 天交易统计自动生成的按小时过滤建议，用于差时段减仓/提门槛，由脚本 aggregate_trade_stats.py 自动更新，请勿手动修改。' },
    // 多账号错峰扫描配置
    SCAN_STAGGER_BY_ACCOUNT: { value: false, type: 'boolean', category: 'scan', description: '多账号错峰扫描总开关。开启后，不同账号会在首次扫描前延迟一段时间，避免同时扫行情与下单；单账号可保持关闭。' },
    SCAN_STAGGER_RANDOM: { value: true, type: 'boolean', category: 'scan', description: '错峰方式：True=在区间 [SCAN_STAGGER_MIN_SEC, SCAN_STAGGER_MAX_SEC] 内随机延迟；False=按账号ID固定间隔延迟 (account_id-1)*SCAN_STAGGER_SEC。' },
    SCAN_STAGGER_MIN_SEC: { value: 10, type: 'number', category: 'scan', description: '随机错峰模式下的最小延迟秒数。例如 10 表示至少延迟 10 秒。' },
    SCAN_STAGGER_MAX_SEC: { value: 120, type: 'number', category: 'scan', description: '随机错峰模式下的最大延迟秒数。例如 120 表示至多延迟 120 秒。' },
    SCAN_STAGGER_SEC: { value: 60, type: 'number', category: 'scan', description: '固定错峰模式下每个账号之间的步长（秒）。延迟 = (account_id-1)*SCAN_STAGGER_SEC。' },
    // 周日/晚间控制（周六 21 点～周日 06 点禁止开仓等），全局配置中可见
    SUNDAY_MAX_OPENS: { value: 3, type: 'number', category: 'risk', description: '周日最多开仓次数，0=不限制；2-3 可降低周日亏损。' },
    SUNDAY_MIN_SIGNAL_STRENGTH: { value: 8, type: 'number', category: 'risk', description: '周日最低信号强度(0-10)，仅当>=此值才开仓，0=不提高。' },
    NIGHT_HOURS_NO_OPEN_ENABLED: { value: true, type: 'boolean', category: 'risk', description: '晚间/凌晨禁止开新仓。开启后 21:00～06:00（北京）不开新仓。' },
    NIGHT_HOURS_START: { value: 21, type: 'number', category: 'risk', description: '禁止开仓开始小时（北京），含。从周六晚 21 点开始控制。' },
    NIGHT_HOURS_END: { value: 6, type: 'number', category: 'risk', description: '禁止开仓结束小时（北京），不含。' },
    NIGHT_HOURS_ONLY_SUNDAY: { value: true, type: 'boolean', category: 'risk', description: 'True=仅周六21:00～周日06:00禁止；False=每天21:00～06:00禁止。' },
    NO_OPEN_HOURS_BJ: { value: '', type: 'string', category: 'risk', description: '禁止开仓小时（北京），逗号分隔如 17,19,22 表示该整点不开新仓；空则不限制。按历史亏损时段配置。' },
    MANUAL_REDUCED_SYMBOLS: { value: '', type: 'string', category: 'strategy', description: '手动减仓交易对列表，支持逗号、空格或换行分隔。' },
    MANUAL_BLOCKED_SYMBOLS: { value: '', type: 'string', category: 'strategy', description: '手动停做交易对列表，支持逗号、空格或换行分隔。' },
    MANUAL_REDUCED_SYMBOL_POSITION_FACTOR: { value: 0.5, type: 'number', category: 'strategy', description: '手动减仓交易对的仓位系数。0.5 表示只开半仓。' },
    MANUAL_REDUCED_SYMBOL_SIGNAL_BOOST: { value: 1, type: 'number', category: 'strategy', description: '手动减仓交易对额外提高的最小信号门槛。' },
    LOCK_PROFIT_STAGE1_TRIGGER_PCT: { value: 0.08, type: 'number', category: 'strategy', description: '分层锁盈第一层触发阈值（如 0.08=8%）。' },
    LOCK_PROFIT_STAGE1_PCT: { value: 0.02, type: 'number', category: 'strategy', description: '分层锁盈第一层锁利比例（如 0.02=2%）。' },
    LOCK_PROFIT_STAGE2_TRIGGER_PCT: { value: 0.15, type: 'number', category: 'strategy', description: '分层锁盈第二层触发阈值（如 0.15=15%）。' },
    LOCK_PROFIT_STAGE2_PCT: { value: 0.05, type: 'number', category: 'strategy', description: '分层锁盈第二层锁利比例（如 0.05=5%）。' },
    ENTRY_PULLBACK_FILTER_ENABLED: { value: false, type: 'boolean', category: 'strategy', description: '回撤区间入场过滤：避免买在K线区间上沿、卖在下沿。' },
    ENTRY_PULLBACK_INTERVAL: { value: '', type: 'string', category: 'strategy', description: '留空=使用 ENTRY_INTERVAL；可填 15m、1h、4h。' },
    ENTRY_PULLBACK_LOOKBACK_BARS: { value: 24, type: 'number', category: 'strategy', description: '统计区间用的 K 线根数。' },
    ENTRY_PULLBACK_MIN_BARS: { value: 5, type: 'number', category: 'strategy', description: '至少几根 K 才启用回撤过滤。' },
    ENTRY_PULLBACK_MAX_LONG_IN_RANGE: { value: 0.62, type: 'number', category: 'strategy', description: '做多：区间相对位置上限制（0~1），默认 0.62。' },
    ENTRY_PULLBACK_MIN_SHORT_IN_RANGE: { value: 0.38, type: 'number', category: 'strategy', description: '做空：区间相对位置下限制（0~1），默认 0.38。' },
    SHADOW_MODE_AUTO_APPLY: { value: false, type: 'boolean', category: 'strategy', description: '是否按 current_suggestions.json 自动跳过黑名单/差时段并调整杠杆。' },
    SHADOW_MODE_MIN_CONFIDENCE: { value: 0.7, type: 'number', category: 'strategy', description: 'shadow_mode_tracking.json 置信度低于此值则不应用。' },
    SHADOW_MODE_SUGGESTIONS_PATH: { value: 'config/current_suggestions.json', type: 'string', category: 'strategy', description: '优化建议 JSON（相对项目根）。' },
    SHADOW_MODE_TRACKING_PATH: { value: 'config/shadow_mode_tracking.json', type: 'string', category: 'strategy', description: '跟踪准确率 JSON。' },
    SHADOW_MODE_INCREASE_LEVERAGE_MULT: { value: 1.5, type: 'number', category: 'strategy', description: 'increase_position 名单杠杆乘数。' },
    SHADOW_MODE_DECREASE_LEVERAGE_MULT: { value: 0.5, type: 'number', category: 'strategy', description: 'decrease_position 名单杠杆乘数。' },
  }

  const loadConfigs = async () => {
    try {
      // 管理员全局配置：从独立的全局配置表读取，不依赖任何 account
      if (isAdmin) {
        const data = await api.getGlobalConfigs()
        // 兜底：若后端未返回新配置项（如未重启），用已知默认值合并，确保前端能看到
        const merged = { ...(data || {}) }
        Object.keys(KNOWN_GLOBAL_CONFIG_DEFAULTS).forEach(key => {
          if (!(key in merged)) {
            merged[key] = KNOWN_GLOBAL_CONFIG_DEFAULTS[key]
          }
        })
        console.log('Loaded global configs:', Object.keys(merged).length, 'keys')
        setConfigs(merged)
      } else {
        // 非管理员不应该访问这个页面
        setConfigs({})
      }
    } catch (error) {
      console.error('Failed to load global configs:', error)
      setConfigs({})
    }
  }

  const loadSystemStatus = async () => {
    try {
      const res = await api.getTradingSystemStatus()
      setSystemStatus(res)
      
      try {
        const servicesRes = await api.get('/system/trading/services')
        setServicesSummary(servicesRes.data.summary)
      } catch (e) {
        // Services summary failed
      }
    } catch (error) {
      // 静默失败
    }
  }

  const loadBackendStatus = async () => {
    try {
      const res = await api.getBackendStatus()
      setBackendStatus(res)
    } catch (error) {
      // 静默失败
    }
  }

  const loadRecommendationsStatus = async () => {
    try {
      const res = await api.getRecommendationsStatus()
      setRecommendationsStatus(res)
    } catch (error) {
      // 静默失败
    }
  }

  const loadMarketOverview = async () => {
    try {
      const res = await api.getMarketOverview()
      setMarketOverview(res)
    } catch (error) {
      setMarketOverview(null)
    }
  }

  useEffect(() => {
    const init = async () => {
      if (isAdmin) {
        // 加载全局配置（独立于账户）
        await Promise.allSettled([
          loadConfigs(),
          loadSystemStatus(),
          loadBackendStatus(),
          loadRecommendationsStatus(),
          loadMarketOverview()
        ])
      }
      setLoading(false)
    }
    init()

    // 只有管理员才轮询系统状态
    if (isAdmin) {
      const timer = setInterval(() => {
        loadSystemStatus().catch(() => {})
        loadBackendStatus().catch(() => {})
        loadRecommendationsStatus().catch(() => {})
      }, 3000)
      return () => clearInterval(timer)
    }
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [isAdmin])

  // 系统控制函数
  const handleClearCache = async () => {
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.clearSystemCache()
      setMessage(res.message || '缓存已清理')
      await loadConfigs()
      await loadSystemStatus()
    } catch (error) {
      setMessage('清理缓存失败: ' + (error.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }

  const handleStopBackend = async () => {
    if (!window.confirm('警告：确定要停止后端服务吗？\n\n停止后 Web 界面将立即无法访问！\n必须手动登录服务器执行 ./start.sh 才能恢复。')) return
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.stopBackend()
      setMessage(res.message || '后端服务已停止')
      alert('后端服务已停止，页面将不再响应。请手动去服务器启动。')
    } catch (error) {
      setMessage('停止后端失败: ' + (error.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }

  const handleRestartBackend = async () => {
    if (!window.confirm('确定要重启后端服务吗？重启期间页面接口会短暂不可用（约 3-10 秒）。')) return
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.restartBackend()
      setMessage(res.message || '已发起后端重启')
      setTimeout(() => {
        loadBackendStatus()
      }, 4000)
    } catch (error) {
      setMessage('重启后端失败: ' + (error.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }

  const handleRestartRecommendations = async () => {
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.restartRecommendations()
      setMessage(res.message || '已发起推荐服务重启')
      setTimeout(() => loadRecommendationsStatus(), 4000)
    } catch (error) {
      setMessage('重启推荐服务失败: ' + (error.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }

  const handleStopRecommendations = async () => {
    if (!window.confirm('确定要停止推荐服务吗？停止后需在此处点击「启动推荐服务」或手动在服务器启动。')) return
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.stopRecommendations()
      setMessage(res.message || '已发起推荐服务停止')
      setTimeout(() => loadRecommendationsStatus(), 3000)
    } catch (error) {
      setMessage('停止推荐服务失败: ' + (error.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }

  const handleStartRecommendations = async () => {
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.startRecommendations()
      setMessage(res.message || (res.skipped ? '推荐服务已在运行中' : '已发起推荐服务启动'))
      setTimeout(() => loadRecommendationsStatus(), 3000)
    } catch (error) {
      setMessage('启动推荐服务失败: ' + (error.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }

  const handleRestartAllTrading = async () => {
    if (!window.confirm('确定要重启【所有账号】的交易进程吗？这会让所有用户的交易服务短暂中断（约 3-10 秒），用于升级代码后统一生效。')) return
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.restartAllTradingSystems({ prefix: 'auto_sys_acc', do_update: true })
      setMessage(`已发起批量重启：共 ${res.count} 个，成功 ${res.ok}，失败 ${res.failed}`)
    } catch (e) {
      setMessage('批量重启失败: ' + (e?.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }

  const handleStopAllTrading = async () => {
    if (!window.confirm('确定要停止【所有账号】的交易进程吗？所有账号将停止自动交易！')) return
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.stopAllTradingSystems({ prefix: 'auto_sys_acc', include_default: true })
      setMessage(`已发起批量停止：共 ${res.count} 个，成功 ${res.ok}，失败 ${res.failed}`)
      await loadSystemStatus()
    } catch (e) {
      setMessage('批量停止失败: ' + (e?.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }

  const handleTriggerScan = async () => {
    setSystemBusy(true)
    setMessage('')
    try {
      const res = await api.triggerScan()
      setMessage(res.message || '已触发手动扫描')
    } catch (e) {
      setMessage('触发扫描失败: ' + (e?.message || '未知错误'))
    } finally {
      setSystemBusy(false)
    }
  }


  const applyPreset = async (presetKey) => {
    const preset = presets[presetKey]
    if (!preset) return

    setSaving(true)
    setMessage('')
    try {
      const configItems = Object.entries(preset.configs).map(([key, value]) => {
        const config = configs[key]
        if (!config) {
          let type = 'number'
          let category = 'risk'
          if (typeof value === 'boolean') {
            type = 'boolean'
            category = 'strategy'
          }
          if (key.startsWith('ENTRY_') || key.startsWith('SMART_ENTRY_') || key === 'SMART_ENTRY_ENABLED') {
            type = typeof value === 'boolean' ? 'boolean' : 'number'
            category = 'strategy'
          } else if (key.startsWith('AUTO_TRADE_')) {
            type = typeof value === 'boolean' ? 'boolean' : 'number'
            category = 'strategy'
          } else if (key === 'LIMIT_ORDER_OFFSET_PCT') {
            type = 'number'
            category = 'strategy'
          } else if (key.includes('PERCENT') || key.includes('PCT')) {
            type = 'number'
            if (key.includes('STOP_LOSS') || key.includes('TAKE_PROFIT')) {
              category = 'risk'
            } else if (key.includes('POSITION')) {
              category = 'position'
            } else {
              category = 'scan'
            }
          } else if (key === 'MIN_VOLATILITY') {
            type = 'number'
            category = 'scan'
          } else if (typeof value === 'number') {
            type = 'number'
            category = 'scan'
          }

          return {
            key,
            value,
            type,
            category,
            description: `预设方案配置项：${key}`
          }
        }
        return {
          key,
          value,
          type: config.type,
          category: config.category,
          description: config.description
        }
      }).filter(Boolean)

      // 管理员全局配置：使用独立的全局配置API
      let response
      if (isAdmin) {
        response = await api.updateGlobalConfigsBatch(configItems)
      } else {
        // 非管理员不应该访问这个页面，但为了安全还是处理一下
        throw new Error('只有管理员可以修改全局配置')
      }
      setMessage(response.message || `已应用${preset.name}`)
      if (response.note) {
        setTimeout(() => {
          setMessage(response.note)
        }, 2000)
      }
      await loadConfigs()
    } catch (error) {
      setMessage('应用预设失败: ' + error.message)
    } finally {
      setSaving(false)
    }
  }

  // 单个配置更新（带确认）
  const handleConfigUpdate = async (key, value, config) => {
    // 检查是否有实质变化
    if (config.value === value) return

    // 格式化显示值用于确认弹窗
    let displayOld = config.value
    let displayNew = value
    
    // 如果是百分比相关的，尝试转回百分数显示，更直观
    const isPercent = key.includes('PERCENT') || key.includes('PCT')
    if (isPercent && typeof value === 'number' && Math.abs(value) <= 1) {
       // 简单判断：如果原值是小数且 key 含 PERCENT，可能是小数存储。
       // 这里为了保险，直接显示原始值和新值，管理员自己判断。
       // 或者简单转一下
       displayOld = `${config.value} (${(config.value * 100).toFixed(2)}%)`
       displayNew = `${value} (${(value * 100).toFixed(2)}%)`
    }

    const confirmMsg = `确定修改配置项【${key}】吗？\n\n原值: ${displayOld}\n新值: ${displayNew}\n\n修改将立即生效。`
    if (!window.confirm(confirmMsg)) {
      // 如果用户取消，理论上 UI 应该回滚。
      // 但 ConfigItem 内部状态已经变了（onBlur 触发）。
      // 触发一次重载配置可以强制 UI 回滚
      await loadConfigs()
      return
    }

    try {
      setSaving(true)
      setMessage('')
      if (!isAdmin) {
        setMessage('只有管理员可以修改全局配置')
        return
      }
      await api.updateGlobalConfigsBatch([{
        key,
        value,
        type: config.type,
        category: config.category,
        description: config.description
      }])
      setMessage(`已更新 ${key}`)
      await loadConfigs()
    } catch (error) {
      setMessage('更新配置失败: ' + error.message)
    } finally {
      setSaving(false)
    }
  }

  // 配置快照函数
  const isSecretKey = (key) => {
    return key === 'BINANCE_API_KEY' || key === 'BINANCE_API_SECRET'
  }

  const maskSecret = (val) => {
    const s = val === null || val === undefined ? '' : String(val)
    if (!s) return ''
    if (s.length <= 8) return '****'
    return `${s.slice(0, 4)}...${s.slice(-4)}`
  }

  const toDisplayValueForSnapshot = (key, value) => {
    if (value === null || value === undefined) return value
    if (typeof value === 'number' && (key.includes('PERCENT') || key.includes('PCT'))) {
      if (PCT_LIKE_KEYS.has(key)) {
        return value <= 0.05 ? value * 100 : value
      }
      return value < 1 ? value * 100 : value
    }
    return value
  }

  const buildConfigSnapshot = async (includeSecrets) => {
    // 管理员全局配置：从独立的全局配置表读取
    let data
    if (isAdmin) {
      data = await api.getGlobalConfigs()
    } else {
      data = await api.getConfigs()
    }
    const now = new Date()

    const categoryMap = {
      scan: '市场扫描',
      position: '仓位控制',
      risk: '风险控制',
      strategy: '策略参数',
      api: 'API配置',
    }

    const entries = Object.entries(data || {}).map(([key, cfg]) => {
      const rawVal = cfg?.value
      const valMasked = isSecretKey(key) && !includeSecrets ? maskSecret(rawVal) : rawVal
      const displayVal = toDisplayValueForSnapshot(key, valMasked)
      return {
        key,
        category: cfg?.category || '',
        category_label: categoryMap[cfg?.category] || cfg?.category || '',
        type: cfg?.type || '',
        value: valMasked,
        display_value: displayVal,
        description: cfg?.description || '',
      }
    })

    entries.sort((a, b) => {
      const ca = a.category_label || a.category || ''
      const cb = b.category_label || b.category || ''
      if (ca !== cb) return ca.localeCompare(cb)
      return a.key.localeCompare(b.key)
    })

    // 临时获取当前配置以检测预设
    const tempConfigs = data || {}
    let detectedPreset = null
    for (const [presetKey, preset] of Object.entries(presets)) {
      let match = true
      for (const [key, expectedValue] of Object.entries(preset.configs)) {
        const currentConfig = tempConfigs[key]
        if (!currentConfig) {
          match = false
          break
        }
        let currentValue = currentConfig.value
        if (key.includes('PERCENT') || key.includes('PCT')) {
          if (PCT_LIKE_KEYS.has(key)) {
            currentValue = currentValue <= 0.05 ? currentValue * 100 : currentValue
          } else {
            currentValue = currentValue * 100
          }
        }
        if (typeof expectedValue === 'number' && typeof currentValue === 'number') {
          if (Math.abs(currentValue - expectedValue) > 0.01) {
            match = false
            break
          }
        } else if (currentValue !== expectedValue) {
          match = false
          break
        }
      }
      if (match) {
        detectedPreset = presetKey
        break
      }
    }

    // 构建配置说明（用于策略分析，优先用详细说明，无则用后端 description）
    const config_guide = {}
    entries.forEach((e) => {
      const detail = CONFIG_GUIDE_DETAILS[e.key] || e.description
      if (detail) config_guide[e.key] = detail
    })

    const snapshot = {
      fetched_at: now.toISOString(),
      export_purpose: '策略配置全量导出，用于离线分析与调参。来源：getGlobalConfigs（管理员）或 getConfigs（普通用户），包含数据库配置及后端默认值。',
      note: 'configs 为全部配置项；display_value 对 PERCENT/PCT 做了百分比换算；敏感字段（API Key/Secret）可选择脱敏或明文。config_guide 提供各配置的详细说明与建议，便于策略分析。',
      config_count: entries.length,
      preset_detected: detectedPreset,
      system_status: systemStatus ? {
        running: !!systemStatus.running,
        pid: systemStatus.pid || null,
        program: systemStatus.program || null,
        state: systemStatus.state || null,
      } : null,
      configs: entries,
      config_guide,
    }

    return JSON.stringify(snapshot, null, 2)
  }

  const openSnapshot = async (includeSecrets) => {
    setSnapshotBusy(true)
    setMessage('')
    try {
      const text = await buildConfigSnapshot(includeSecrets)
      setSnapshotText(text)
      setShowSnapshot(true)
    } catch (e) {
      setMessage('生成配置快照失败: ' + (e?.message || '未知错误'))
    } finally {
      setSnapshotBusy(false)
    }
  }

  const copySnapshot = async () => {
    try {
      if (navigator?.clipboard?.writeText) {
        await navigator.clipboard.writeText(snapshotText || '')
        setMessage('已复制配置快照到剪贴板')
      } else {
        setMessage('当前浏览器不支持剪贴板 API，可手动全选复制')
      }
    } catch (e) {
      setMessage('复制失败: ' + (e?.message || '未知错误'))
    }
  }

  const downloadSnapshot = () => {
    try {
      const blob = new Blob([snapshotText || ''], { type: 'application/json;charset=utf-8' })
      const url = URL.createObjectURL(blob)
      const a = document.createElement('a')
      a.href = url
      a.download = `config-snapshot-${new Date().toISOString().replace(/[:.]/g, '-')}.json`
      document.body.appendChild(a)
      a.click()
      a.remove()
      URL.revokeObjectURL(url)
    } catch (e) {
      setMessage('下载失败: ' + (e?.message || '未知错误'))
    }
  }


 

  if (loading) {
    return <div className="global-config">加载中...</div>
  }

  // 管理员全局配置页面：不依赖任何 account，直接管理全局配置表
  const isGlobalStrategyAccount = isAdmin
  
  // 预设检测：只比「特征键」且与数据库一致（宽松：数字容差、布尔兼容字符串、百分比支持 0.2 与 20 两种存法）
  const toNum = (v) => (typeof v === 'number' ? v : parseFloat(v))
  const toBool = (v) => (v === true || v === false ? v : (v === 'true' || String(v).toLowerCase() === 'true' || v === 1 || v === '1'))
  const toRatio = (v, key) => {
    const n = toNum(v)
    if (Number.isNaN(n)) return null
    if (key && (key.includes('PERCENT') || key.includes('PCT')) && n > 1) return n / 100
    return n
  }
  const valueMatches = (cur, exp, key) => {
    if (exp === true || exp === false) return toBool(cur) === exp
    if (typeof exp === 'number') {
      const r = key && (key.includes('PERCENT') || key.includes('PCT'))
      const a = r ? toRatio(cur, key) : toNum(cur)
      let b = exp
      if (r && exp > 1) b = exp / 100
      if (a == null || Number.isNaN(a)) return false
      return Math.abs(a - b) <= 0.002
    }
    return cur === exp
  }
  // 基础策略：山寨币/备选等（排除 scheme_ 开头的市场方案）
  let currentBasePreset = null
  // 市场方案：bear/normal/bull/conservative（从 MARKET_SCHEME 直接读取，便于展示自动更新结果）
  let currentMarketSchemeDisplay = null
  const schemeDisplayMap = { bear: '熊市', normal: '正常', bull: '牛市', conservative: '保守' }

  let scheme = ''
  if (configs && Object.keys(configs).length > 0 && presets) {
    try {
      // 1. 匹配基础策略（非 scheme 的 preset）
      for (const [presetKey, preset] of Object.entries(presets)) {
        if (presetKey.startsWith('scheme_')) continue
        const keysToCheck = preset.signatureKeys && preset.signatureKeys.length > 0
          ? preset.signatureKeys
          : Object.keys(preset.configs)
        let match = true
        for (const key of keysToCheck) {
          const currentConfig = configs[key]
          if (!currentConfig) { match = false; break }
          const exp = preset.configs[key]
          if (exp === undefined) continue
          const cur = currentConfig.value
          if (!valueMatches(cur, exp, key)) { match = false; break }
        }
        if (match && keysToCheck.length > 0) {
          currentBasePreset = presetKey
          break
        }
      }
      // 2. 市场方案：从 MARKET_SCHEME 读取
      const schemeVal = configs['MARKET_SCHEME']?.value ?? configs['MARKET_SCHEME']
      scheme = typeof schemeVal === 'string' ? schemeVal.toLowerCase().trim() : ''
      currentMarketSchemeDisplay = schemeDisplayMap[scheme] || (scheme ? scheme : null)
    } catch (e) {
      console.error('detectCurrentPreset error:', e)
    }
  }
  const currentSchemePresetKey = scheme && schemeDisplayMap[scheme] ? `scheme_${scheme}` : null

  const presetUiMeta = {
    altcoin: { group: 'altcoin', tag: '当前推荐' },
    more_opportunities: { group: 'altcoin', tag: '增加机会' },
    profit_scale: { group: 'altcoin', tag: '盈利放大' },
    classic_profit: { group: 'altcoin', tag: '之前盈利' },
    classic_profit_amplify: { group: 'altcoin', tag: '杠杆放大' },
    swing: { group: 'backup', tag: '纯限价' },
    strict: { group: 'backup', tag: '精选低频' },
    conservative: { group: 'backup', tag: '保守' },
    scheme_bear: { group: 'scheme', tag: '熊市' },
    scheme_normal: { group: 'scheme', tag: '正常' },
    scheme_bull: { group: 'scheme', tag: '牛市' },
    scheme_conservative: { group: 'scheme', tag: '保守' },
  }

  // 快速方案：当前推荐（含增加机会/盈利放大/之前盈利）+ 备选 + 市场状态方案
  const presetGroups = [
    {
      key: 'altcoin',
      title: '当前推荐 · 山寨币策略',
      desc: '与当前策略一致。想增加下单机会可点「增加机会」；盈利阶段可点「盈利放大」；想恢复 2 月初有盈利时的下单风格可点「之前盈利」；在之前盈利基础上再放大可点「杠杆放大」（10x）。',
      presetKeys: ['altcoin', 'more_opportunities', 'profit_scale', 'classic_profit', 'classic_profit_amplify'],
    },
    {
      key: 'backup',
      title: '备选方案（纯限价 / 保守）',
      desc: '需要更少出单或纯限价时可选：波段回归、精选低频、保守传统。',
      presetKeys: ['swing', 'strict', 'conservative'],
    },
    {
      key: 'scheme',
      title: '市场状态方案',
      desc: '按当前行情切换：熊市（放宽止损、降仓、4H 下跌不开多）→ 正常 → 牛市 → 保守（最小仓位、最严过滤）。',
      presetKeys: ['scheme_bear', 'scheme_normal', 'scheme_bull', 'scheme_conservative'],
    },
  ]

  // 统计过滤可视化：解析全局 STATS_* 配置
  const rawStatsSymbolFilters = configs?.STATS_SYMBOL_FILTERS
  const rawStatsHourFilters = configs?.STATS_HOUR_FILTERS

  const statsSymbolFiltersValue =
    rawStatsSymbolFilters && typeof rawStatsSymbolFilters === 'object'
      ? (rawStatsSymbolFilters.value ?? rawStatsSymbolFilters)
      : null

  const statsHourFiltersValue =
    rawStatsHourFilters && typeof rawStatsHourFilters === 'object'
      ? (rawStatsHourFilters.value ?? rawStatsHourFilters)
      : null

  const statsBlacklist = Array.isArray(statsSymbolFiltersValue?.blacklist)
    ? statsSymbolFiltersValue.blacklist
    : []
  const statsWhitelist = Array.isArray(statsSymbolFiltersValue?.whitelist)
    ? statsSymbolFiltersValue.whitelist
    : []
  const statsHours = Array.isArray(statsHourFiltersValue?.hours)
    ? statsHourFiltersValue.hours
    : []

  const statsGeneratedAt = statsSymbolFiltersValue?.generated_at || statsHourFiltersValue?.generated_at || null
  const statsDays = statsSymbolFiltersValue?.days || statsHourFiltersValue?.days || null

  return (
    <div className="global-config">
      <div className="global-config-header">
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
          <div>
            <h2>全局配置</h2>
            <p>管理用户、账号和全局策略配置</p>
          </div>
          <div style={{ display: 'flex', gap: '12px' }}>
            <button
              type="button"
              className="guide-link snapshot-btn"
              onClick={() => openSnapshot(snapshotIncludeSecrets)}
              disabled={snapshotBusy}
              title="导出当前全量配置（用于分析）"
            >
              {snapshotBusy ? '生成中...' : '查看整体配置'}
            </button>
            <Link to="/config/guide" className="guide-link">📖 配置说明</Link>
          </div>
        </div>
      </div>

      {message && (
        <div className={`message ${message.includes('失败') ? 'error' : 'success'}`}>
          {message}
        </div>
      )}

      {/* 市场行情概览（供确认策略方案是否匹配市场） */}
      {isAdmin && (
        <section className="global-section" style={{ marginBottom: '20px' }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '10px', flexWrap: 'wrap', gap: '8px' }}>
            <h3>市场行情概览</h3>
            <div style={{ display: 'flex', gap: '8px' }}>
              <button type="button" className="system-btn primary" onClick={loadMarketOverview}>
                刷新
              </button>
              {marketOverview && (
                <button type="button" className="system-btn" onClick={() => setShowMarketJson(v => !v)}>
                  {showMarketJson ? '收起 JSON' : '查看 JSON'}
                </button>
              )}
            </div>
          </div>
          {marketOverview ? (
            <>
              <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(200px, 1fr))', gap: '12px', fontSize: '14px' }}>
                <div style={{ padding: '10px', background: '#f8f9fa', borderRadius: '6px' }}>
                  <div style={{ color: '#666', fontSize: '12px' }}>BTC 24h</div>
                  <div style={{ fontWeight: 600, color: marketOverview.btc_24h_change_pct >= 0 ? '#4caf50' : '#f44336' }}>
                    {marketOverview.btc_24h_change_pct != null ? `${marketOverview.btc_24h_change_pct}%` : '—'}
                  </div>
                </div>
                <div style={{ padding: '10px', background: '#f8f9fa', borderRadius: '6px' }}>
                  <div style={{ color: '#666', fontSize: '12px' }}>ETH 24h</div>
                  <div style={{ fontWeight: 600, color: marketOverview.eth_24h_change_pct >= 0 ? '#4caf50' : '#f44336' }}>
                    {marketOverview.eth_24h_change_pct != null ? `${marketOverview.eth_24h_change_pct}%` : '—'}
                  </div>
                </div>
                <div style={{ padding: '10px', background: '#f8f9fa', borderRadius: '6px' }}>
                  <div style={{ color: '#666', fontSize: '12px' }}>BTC 15m / 1h</div>
                  <div style={{ fontWeight: 500 }}>
                    {marketOverview.btc_15m_change_pct != null ? marketOverview.btc_15m_change_pct : '—'}% / {marketOverview.btc_1h_change_pct != null ? marketOverview.btc_1h_change_pct : '—'}%
                  </div>
                </div>
                <div style={{ padding: '10px', background: '#f8f9fa', borderRadius: '6px' }}>
                  <div style={{ color: '#666', fontSize: '12px' }}>ETH 15m / 1h</div>
                  <div style={{ fontWeight: 500 }}>
                    {marketOverview.eth_15m_change_pct != null ? marketOverview.eth_15m_change_pct : '—'}% / {marketOverview.eth_1h_change_pct != null ? marketOverview.eth_1h_change_pct : '—'}%
                  </div>
                </div>
                <div style={{ padding: '10px', background: marketOverview.beta_filter_triggered ? '#ffebee' : '#e8f5e9', borderRadius: '6px' }}>
                  <div style={{ color: '#666', fontSize: '12px' }}>大盘共振过滤</div>
                  <div style={{ fontWeight: 600, color: marketOverview.beta_filter_triggered ? '#c62828' : '#2e7d32' }}>
                    {marketOverview.beta_filter_triggered ? '已触发（屏蔽多单）' : '未触发'}
                  </div>
                  {marketOverview.config && (
                    <div style={{ fontSize: '11px', color: '#888', marginTop: '4px' }}>
                      阈值 {marketOverview.config.BETA_FILTER_THRESHOLD_PCT}% · {marketOverview.config.BETA_FILTER_ENABLED ? '已开启' : '已关闭'}
                    </div>
                  )}
                </div>
                <div style={{ padding: '10px', background: '#f8f9fa', borderRadius: '6px' }}>
                  <div style={{ color: '#666', fontSize: '12px' }}>市场状态 / 4H</div>
                  <div style={{ fontWeight: 500 }}>
                    {marketOverview.market_regime || '—'} / {marketOverview.btc_trend_4h || '—'}
                  </div>
                </div>
                <div style={{ padding: '10px', background: '#f8f9fa', borderRadius: '6px' }}>
                  <div style={{ color: '#666', fontSize: '12px' }}>当前方案</div>
                  <div style={{ fontWeight: 600 }}>{marketOverview.config?.MARKET_SCHEME || '—'}</div>
                </div>
              </div>

              {/* 市场行情概览 - JSON 查看（放在策略执行概览上边） */}
              {showMarketJson && marketOverview && (
                <div style={{ marginTop: '16px' }}>
                  <div style={{ display: 'flex', justifyContent: 'flex-end', marginBottom: '6px' }}>
                    <button
                      type="button"
                      className="system-btn"
                      onClick={() => {
                        const json = JSON.stringify(marketOverview, null, 2)
                        navigator.clipboard.writeText(json).then(() => setMessage('已复制到剪贴板')).catch(() => setMessage('复制失败'))
                      }}
                    >
                      复制 JSON
                    </button>
                  </div>
                  <pre style={{ background: '#263238', color: '#aed581', padding: '12px', borderRadius: '6px', overflow: 'auto', maxHeight: '400px', fontSize: '13px', margin: 0 }}>
                    {JSON.stringify(marketOverview, null, 2)}
                  </pre>
                </div>
              )}

              {/* 策略执行概览：默认折叠约 2/3，点击展开/收起 */}
              <div className="strategy-execution-overview" style={{ marginTop: '20px', padding: '16px', background: '#fafafa', borderRadius: '8px', border: '1px solid #eee' }}>
                <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: strategyOverviewCollapsed ? 0 : '12px' }}>
                  <h4 style={{ margin: 0, fontSize: '15px', fontWeight: 600, color: '#333' }}>策略执行概览</h4>
                  <button
                    type="button"
                    className="system-btn"
                    onClick={() => setStrategyOverviewCollapsed(v => !v)}
                  >
                    {strategyOverviewCollapsed ? '展开' : '收起'}
                  </button>
                </div>
                {marketOverview.strategy_execution_overview?.sections?.length > 0 ? (
                  <>
                    {!strategyOverviewCollapsed && (
                      <p style={{ margin: '0 0 14px 0', fontSize: '13px', color: '#666', lineHeight: 1.5 }}>
                        以下为当前生效的整体策略执行标准与机制说明（由数据库/Redis 配置生成，刷新市场行情时一并更新）。
                      </p>
                    )}
                    <div style={{ maxHeight: strategyOverviewCollapsed ? '120px' : 'none', overflow: 'hidden' }}>
                      {marketOverview.strategy_execution_overview.sections.map((sec, idx) => (
                        <div key={idx} style={{ marginBottom: idx < marketOverview.strategy_execution_overview.sections.length - 1 ? '14px' : 0 }}>
                          <div style={{ fontSize: '13px', fontWeight: 600, color: '#1976d2', marginBottom: '4px' }}>{sec.title}</div>
                          <div style={{ fontSize: '13px', color: '#444', lineHeight: 1.6, whiteSpace: 'pre-wrap' }}>{sec.content}</div>
                        </div>
                      ))}
                    </div>
                  </>
                ) : (
                  <p style={{ margin: 0, fontSize: '13px', color: '#888' }}>
                    暂无数据。请点击上方「刷新」重试；若仍无内容，请确认后端已重启并已部署包含策略执行概览的最新代码。
                  </p>
                )}
              </div>
            </>
          ) : (
            <div style={{ color: '#888', fontSize: '14px' }}>加载中或拉取失败，请点击刷新</div>
          )}
        </section>
      )}

      {/* 系统控制 */}
      {isAdmin && (
        <section className="global-section system-section">
          <div className="system-header">
            <h3>系统控制</h3>
            <div className="system-status-indicators" style={{ display: 'flex', gap: '15px', fontSize: '14px', alignItems: 'center', marginLeft: '20px' }}>
               <div className="status-item" style={{ display: 'flex', alignItems: 'center', gap: '6px' }}>
                  <span style={{ width: '10px', height: '10px', borderRadius: '50%', backgroundColor: backendStatus?.running ? '#4caf50' : '#f44336', display: 'inline-block' }}></span>
                  <span style={{ fontWeight: 500 }}>后端: {backendStatus?.running ? '运行中' : '停止'}</span>
               </div>
               <div className="status-item" style={{ display: 'flex', alignItems: 'center', gap: '6px', borderLeft: '1px solid #ddd', paddingLeft: '15px' }}>
                  <span style={{ width: '10px', height: '10px', borderRadius: '50%', backgroundColor: recommendationsStatus?.running ? '#4caf50' : '#f44336', display: 'inline-block' }}></span>
                  <span style={{ fontWeight: 500 }}>推荐: {recommendationsStatus?.running ? '运行中' : '停止'}</span>
               </div>
               {servicesSummary && (
                 <div className="status-item" style={{ display: 'flex', alignItems: 'center', gap: '6px', borderLeft: '1px solid #ddd', paddingLeft: '15px' }}>
                    <span style={{ width: '10px', height: '10px', borderRadius: '50%', backgroundColor: '#2196f3', display: 'inline-block' }}></span>
                    <span style={{ fontWeight: 500 }}>交易服务: </span>
                    <span style={{ fontWeight: 'bold' }}>{servicesSummary.total}</span>
                    <span style={{ color: '#666' }}>个</span>
                    <span style={{ color: '#4caf50', marginLeft: '8px', fontSize: '0.9em' }}>● 运行 {servicesSummary.running}</span>
                    <span style={{ color: '#f44336', marginLeft: '8px', fontSize: '0.9em' }}>● 停止 {servicesSummary.stopped}</span>
                    {servicesSummary.unknown > 0 && <span style={{ color: '#999', marginLeft: '8px', fontSize: '0.9em' }}>● 未知 {servicesSummary.unknown}</span>}
                 </div>
               )}
            </div>
          </div>
          
          <div className="system-control-group">
            <h4 className="control-group-title" style={{ margin: '10px 0 8px', fontSize: '14px', color: '#666' }}>后端服务管理</h4>
            <div className="system-actions">
              <button
                type="button"
                className="system-btn primary"
                onClick={handleRestartBackend}
                disabled={systemBusy}
                title="通过 backend/restart.sh 重启后端（uvicorn）。重启期间接口会短暂不可用。"
              >
                重启后端服务
              </button>
              <button
                type="button"
                className="system-btn danger"
                onClick={handleStopBackend}
                disabled={systemBusy}
                title="停止后端服务（需要手动去服务器启动）"
              >
                停止后端服务
              </button>
            </div>
          </div>

          <div className="system-control-group" style={{ marginTop: '15px' }}>
            <h4 className="control-group-title" style={{ margin: '10px 0 8px', fontSize: '14px', color: '#666' }}>推荐服务管理</h4>
            <div className="system-actions">
              <button
                type="button"
                className="system-btn primary"
                onClick={handleRestartRecommendations}
                disabled={systemBusy}
                title="通过 backend/restart_recommendations.sh 重启推荐服务（recommendations_main）"
              >
                重启推荐服务
              </button>
              <button
                type="button"
                className="system-btn danger"
                onClick={handleStopRecommendations}
                disabled={systemBusy}
                title="停止推荐服务"
              >
                停止推荐服务
              </button>
              <button
                type="button"
                className="system-btn primary"
                onClick={handleStartRecommendations}
                disabled={systemBusy}
                title="启动推荐服务（若已运行则跳过）"
              >
                启动推荐服务
              </button>
            </div>
          </div>

          <div className="system-control-group" style={{ marginTop: '15px' }}>
            <h4 className="control-group-title" style={{ margin: '10px 0 8px', fontSize: '14px', color: '#666' }}>系统数据维护</h4>
            <div className="system-actions">
              <button
                type="button"
                className="system-btn warning"
                onClick={handleClearCache}
                disabled={systemBusy}
                title="清理Redis配置缓存并从数据库回灌。这将影响后端服务和所有交易进程。切换API Key或修改配置后建议执行。"
              >
                清除全局缓存
              </button>
              <span style={{ fontSize: '12px', color: '#888', alignSelf: 'center', marginLeft: '10px' }}>
                * 影响后端服务及所有交易账号，修改配置后请点击。
              </span>
            </div>
          </div>

          <div className="system-control-group" style={{ marginTop: '15px' }}>
            <h4 className="control-group-title" style={{ margin: '10px 0 8px', fontSize: '14px', color: '#666' }}>交易服务管理</h4>
            <div className="system-actions">
              <button
                type="button"
                className="system-btn primary"
                onClick={handleTriggerScan}
                disabled={systemBusy}
                title="通知所有交易进程立即执行一次市场扫描（无需重启）"
              >
                立即触发扫描
              </button>
              <button
                type="button"
                className="system-btn primary"
                onClick={handleRestartAllTrading}
                disabled={systemBusy}
                title="批量重启所有账号交易进程（auto_sys_acc*），用于代码升级后统一生效"
              >
                重启所有账号交易
              </button>
              <button
                type="button"
                className="system-btn danger"
                onClick={handleStopAllTrading}
                disabled={systemBusy}
                title="批量停止所有账号交易进程（auto_sys_acc*）"
              >
                停止所有账号交易
              </button>
            </div>
          </div>

          <div className="system-hint">
            建议流程：先更新配置里的 Key → 点击"清除缓存" → 点击"重启所有账号交易"，确保不再使用旧账号下单。
          </div>
        </section>
      )}







      {/* 预设方案快速切换（仅管理员 + 全局策略账号） */}
      {isAdmin && isGlobalStrategyAccount && (
        <section className="global-section preset-section">
          <div className="preset-header">
            <h3>快速切换方案</h3>
            <div className="current-preset-status" style={{ display: 'flex', flexWrap: 'wrap', gap: '12px 24px', alignItems: 'center' }}>
              <div>
                <span className="status-label">基础策略：</span>
                <span className={`status-badge ${currentBasePreset ? 'preset' : 'custom'}`}>
                  {currentBasePreset && presets && presets[currentBasePreset] ? presets[currentBasePreset].name : '自定义'}
                </span>
              </div>
              <div>
                <span className="status-label">市场方案：</span>
                <span className={`status-badge ${currentMarketSchemeDisplay ? 'preset' : 'custom'}`}>
                  {currentMarketSchemeDisplay || '未设置'}
                </span>
              </div>
            </div>
          </div>
          <div className="preset-guide">
            <div className="preset-guide-title">使用说明</div>
            <ul className="preset-guide-list">
              <li>
                <strong>基础策略 + 市场方案</strong>：基础策略（山寨币/备选）决定整体风控风格，市场方案（熊市/正常/牛市/保守）在其上叠加止损、仓位、4H 过滤等调整。二者配合使用。
              </li>
              <li>
                <strong>日常使用</strong>：选「山寨币策略（当前推荐）」+ 按行情选市场方案即可。
              </li>
              <li>
                <strong>若几乎不出单</strong>：可临时把 <code>AUTO_TRADE_ALLOW_4H_NEUTRAL</code> 打开（会增加 4H 震荡单，信号质量下降）。
              </li>
              <li>
                <strong>要更少出单</strong>：选备选里的「波段回归」或「精选低频」。
              </li>
            </ul>
          </div>

          <div className="preset-groups">
            {presetGroups.map((g) => (
              <div key={g.key} className="preset-group">
                <div className="preset-group-header">
                  <div className="preset-group-title">{g.title}</div>
                  <div className="preset-group-desc">{g.desc}</div>
                </div>
                <div className="preset-buttons">
                  {g.presetKeys
                    .filter((k) => presets && presets[k])
                    .map((k) => {
                      const preset = presets && presets[k] ? presets[k] : null
                      if (!preset) return null
                      const meta = presetUiMeta && presetUiMeta[k] ? presetUiMeta[k] : { group: g.key, tag: '' }
                      return (
                        <button
                          key={k}
                          className={`preset-btn ${(k.startsWith('scheme_') ? currentSchemePresetKey === k : currentBasePreset === k) ? 'active' : ''}`}
                          onClick={() => {
                            if (typeof applyPreset === 'function') {
                              applyPreset(k)
                            }
                          }}
                          disabled={saving}
                          title={preset.desc}
                        >
                          <div className="preset-name">
                            {preset.name}
                            {meta.tag ? (
                              <span className={`preset-tag preset-tag--${meta.group}`}>{meta.tag}</span>
                            ) : null}
                            {(k.startsWith('scheme_') ? currentSchemePresetKey === k : currentBasePreset === k) ? <span className="active-indicator">✓</span> : null}
                          </div>
                          <div className="preset-desc">{preset.desc}</div>
                        </button>
                      )
                    })
                    .filter(Boolean)}
                </div>
              </div>
            ))}
          </div>
        </section>
      )}

      {/* 全局策略配置项编辑（仅管理员） */}
      {isAdmin && (
        <section className="global-section config-section">
          <div className="section-header">
            <h3>全局策略配置</h3>
            <p style={{ fontSize: '14px', color: '#666', marginTop: '8px' }}>
              修改全局策略配置，所有普通用户账号将使用这些配置（风险旋钮除外）
            </p>
          </div>

          {/* 搜索和筛选栏 */}
          <div className="config-toolbar" style={{ marginBottom: '20px', display: 'flex', flexDirection: 'column', gap: '15px' }}>
            {/* 搜索框 */}
            <div className="search-bar" style={{ display: 'flex', alignItems: 'center', position: 'relative' }}>
              <input
                type="text"
                placeholder="🔍 搜索配置项 Key、描述或中文名..."
                value={searchTerm}
                onChange={(e) => setSearchTerm(e.target.value)}
                style={{
                  width: '100%',
                  padding: '10px 12px',
                  borderRadius: '6px',
                  border: '1px solid #ddd',
                  fontSize: '14px'
                }}
              />
              {searchTerm && (
                <button
                  onClick={() => setSearchTerm('')}
                  style={{ 
                    position: 'absolute',
                    right: '10px',
                    border: 'none', 
                    background: 'none', 
                    cursor: 'pointer', 
                    color: '#999',
                    fontSize: '16px'
                  }}
                >
                  ✕
                </button>
              )}
            </div>

          {/* Tabs */}
          <div className="config-tabs" style={{ display: 'flex', gap: '10px', overflowX: 'auto', paddingBottom: '5px' }}>
            {[
              { key: 'all', label: '全部' },
              { key: 'risk', label: '风险控制' },
              { key: 'strategy', label: '策略参数' },
              { key: 'scan', label: '市场扫描' },
              { key: 'position', label: '仓位控制' },
              { key: 'stats', label: '统计过滤' },
            ].map(tab => (
                <button
                  key={tab.key}
                  onClick={() => setActiveTab(tab.key)}
                  className={`tab-btn ${activeTab === tab.key ? 'active' : ''}`}
                  style={{
                    padding: '8px 16px',
                    borderRadius: '20px',
                    border: activeTab === tab.key ? '1px solid #007bff' : '1px solid #eee',
                    background: activeTab === tab.key ? '#e7f1ff' : '#f9f9f9',
                    color: activeTab === tab.key ? '#007bff' : '#666',
                    cursor: 'pointer',
                    fontWeight: activeTab === tab.key ? '600' : 'normal',
                    whiteSpace: 'nowrap',
                    transition: 'all 0.2s'
                  }}
                >
                  {tab.label}
                </button>
              ))}
            </div>
          </div>

          {/* 统计过滤可视化（仅在“统计过滤” Tab 展示） */}
          {activeTab === 'stats' && (
            <div style={{ marginBottom: '24px', padding: '16px', borderRadius: '8px', background: '#f8f9fb', border: '1px solid #e1e5ee' }}>
              <div style={{ marginBottom: '8px', display: 'flex', justifyContent: 'space-between', alignItems: 'baseline', gap: '12px', flexWrap: 'wrap' }}>
                <div>
                  <h4 style={{ margin: 0, fontSize: '15px' }}>统计过滤总览</h4>
                  <div style={{ fontSize: '12px', color: '#6c757d', marginTop: '4px' }}>
                    基于最近 {statsDays || 7} 天的交易统计自动生成：差标的与差时段会被自动降权并提高信号门槛，仅影响自动交易，不会一刀切禁止。
                  </div>
                </div>
                {statsGeneratedAt && (
                  <div style={{ fontSize: '12px', color: '#6c757d' }}>生成时间（UTC）：{String(statsGeneratedAt).replace('T', ' ').slice(0, 19)}</div>
                )}
              </div>

              <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 2fr) minmax(0, 1.6fr)', gap: '16px' }}>
                {/* Symbol 白/黑名单 */}
                <div style={{ background: '#ffffff', borderRadius: '6px', padding: '12px', border: '1px solid #e5e9f0' }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
                    <strong style={{ fontSize: '13px' }}>交易对白名单 / 黑名单</strong>
                    <span style={{ fontSize: '11px', color: '#6c757d' }}>
                      白名单 {statsWhitelist.length} · 黑名单 {statsBlacklist.length}
                    </span>
                  </div>
                  {(statsWhitelist.length === 0 && statsBlacklist.length === 0) ? (
                    <div style={{ fontSize: '12px', color: '#999' }}>暂无统计结果（需先定时跑 7 天聚合脚本）。</div>
                  ) : (
                    <div style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1fr) minmax(0, 1fr)', gap: '10px' }}>
                      <div>
                        <div style={{ fontSize: '12px', fontWeight: 600, marginBottom: '4px', color: '#155724' }}>白名单（近期表现较好）</div>
                        <div style={{ maxHeight: '220px', overflowY: 'auto', border: '1px solid #e9ecef', borderRadius: '4px' }}>
                          <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '12px' }}>
                            <thead>
                              <tr style={{ background: '#f1f3f5' }}>
                                <th style={{ padding: '4px 6px', textAlign: 'left' }}>Symbol</th>
                                <th style={{ padding: '4px 6px', textAlign: 'right' }}>笔数</th>
                                <th style={{ padding: '4px 6px', textAlign: 'right' }}>净盈亏</th>
                                <th style={{ padding: '4px 6px', textAlign: 'right' }}>胜率%</th>
                              </tr>
                            </thead>
                            <tbody>
                              {statsWhitelist.slice(0, 20).map((item) => (
                                <tr key={item.symbol}>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', fontWeight: 600 }}>{item.symbol}</td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', textAlign: 'right' }}>{item.trade_count}</td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', textAlign: 'right', color: Number(item.net_pnl) >= 0 ? '#28a745' : '#dc3545' }}>
                                    {Number(item.net_pnl) >= 0 ? '+' : ''}{Number(item.net_pnl).toFixed(2)}
                                  </td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', textAlign: 'right' }}>{Number(item.win_rate_pct).toFixed(1)}</td>
                                </tr>
                              ))}
                            </tbody>
                          </table>
                        </div>
                      </div>
                      <div>
                        <div style={{ fontSize: '12px', fontWeight: 600, marginBottom: '4px', color: '#721c24' }}>黑名单（软/硬，考察期动态调整）</div>
                        <div style={{ maxHeight: '220px', overflowY: 'auto', border: '1px solid #f5c6cb', borderRadius: '4px' }}>
                          <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '12px' }}>
                            <thead>
                              <tr style={{ background: '#f8d7da' }}>
                                <th style={{ padding: '4px 6px', textAlign: 'left' }}>Symbol</th>
                                <th style={{ padding: '4px 6px', textAlign: 'right' }}>笔数</th>
                                <th style={{ padding: '4px 6px', textAlign: 'right' }}>净盈亏</th>
                                <th style={{ padding: '4px 6px', textAlign: 'right' }}>胜率%</th>
                                <th style={{ padding: '4px 6px', textAlign: 'center' }}>模式</th>
                              </tr>
                            </thead>
                            <tbody>
                              {statsBlacklist.slice(0, 20).map((item) => (
                                <tr key={item.symbol}>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f5c6cb', fontWeight: 600 }}>{item.symbol}</td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f5c6cb', textAlign: 'right' }}>{item.trade_count}</td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f5c6cb', textAlign: 'right', color: '#dc3545' }}>
                                    {Number(item.net_pnl).toFixed(2)}
                                  </td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f5c6cb', textAlign: 'right' }}>{Number(item.win_rate_pct).toFixed(1)}</td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f5c6cb', textAlign: 'center' }}>
                                    {String(item.mode || 'reduced') === 'hard' ? (
                                      <span style={{ display: 'inline-block', padding: '1px 6px', borderRadius: '10px', fontSize: '11px', background: '#dc3545', color: '#fff' }}>
                                        硬
                                      </span>
                                    ) : (
                                      <span style={{ display: 'inline-block', padding: '1px 6px', borderRadius: '10px', fontSize: '11px', background: '#fff3cd', color: '#856404', border: '1px solid #ffeeba' }}>
                                        软
                                      </span>
                                    )}
                                  </td>
                                </tr>
                              ))}
                            </tbody>
                          </table>
                        </div>
                      </div>
                    </div>
                  )}
                </div>

                {/* 按小时时段过滤 */}
                <div style={{ background: '#ffffff', borderRadius: '6px', padding: '12px', border: '1px solid #e5e9f0' }}>
                  <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
                    <strong style={{ fontSize: '13px' }}>按小时过滤（北京时间）</strong>
                    <span style={{ fontSize: '11px', color: '#6c757d' }}>bucket=bad → 降仓 & 提高信号门槛</span>
                  </div>
                  {statsHours.length === 0 ? (
                    <div style={{ fontSize: '12px', color: '#999' }}>暂无时段统计结果。</div>
                  ) : (
                    <div style={{ maxHeight: '260px', overflowY: 'auto', border: '1px solid #e9ecef', borderRadius: '4px' }}>
                      <table style={{ width: '100%', borderCollapse: 'collapse', fontSize: '12px' }}>
                        <thead>
                          <tr style={{ background: '#f1f3f5' }}>
                            <th style={{ padding: '4px 6px', textAlign: 'left' }}>小时</th>
                            <th style={{ padding: '4px 6px', textAlign: 'right' }}>笔数</th>
                            <th style={{ padding: '4px 6px', textAlign: 'right' }}>净盈亏</th>
                            <th style={{ padding: '4px 6px', textAlign: 'center' }}>分组</th>
                            <th style={{ padding: '4px 6px', textAlign: 'right' }}>仓位系数</th>
                            <th style={{ padding: '4px 6px', textAlign: 'right' }}>信号+档数</th>
                          </tr>
                        </thead>
                        <tbody>
                          {statsHours
                            .slice()
                            .sort((a, b) => Number(a.hour) - Number(b.hour))
                            .map((item) => {
                              const bucket = String(item.bucket || '').toLowerCase()
                              const rowBg =
                                bucket === 'bad'
                                  ? '#fff5f5'
                                  : bucket === 'good'
                                  ? '#f0fff4'
                                  : '#ffffff'
                              const badgeColor =
                                bucket === 'bad'
                                  ? '#dc3545'
                                  : bucket === 'good'
                                  ? '#28a745'
                                  : '#6c757d'
                              return (
                                <tr key={item.hour} style={{ background: rowBg }}>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5' }}>{item.hour}:00</td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', textAlign: 'right' }}>{item.trade_count}</td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', textAlign: 'right', color: Number(item.net_pnl) >= 0 ? '#28a745' : '#dc3545' }}>
                                    {Number(item.net_pnl) >= 0 ? '+' : ''}{Number(item.net_pnl).toFixed(2)}
                                  </td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', textAlign: 'center' }}>
                                    <span style={{ display: 'inline-block', padding: '1px 6px', borderRadius: '10px', fontSize: '11px', color: badgeColor, border: `1px solid ${badgeColor}33` }}>
                                      {bucket || 'unknown'}
                                    </span>
                                  </td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', textAlign: 'right' }}>
                                    {item.position_factor !== undefined ? Number(item.position_factor).toFixed(2) : '1.00'}
                                  </td>
                                  <td style={{ padding: '3px 6px', borderTop: '1px solid #f1f3f5', textAlign: 'right' }}>
                                    {item.signal_boost || 0}
                                  </td>
                                </tr>
                              )
                            })}
                        </tbody>
                      </table>
                    </div>
                  )}
                </div>
              </div>
            </div>
          )}

          {Object.keys(configs).length > 0 ? (
            (() => {
              const configCategories = {
                risk: '风险控制',
                strategy: '策略参数',
                scan: '市场扫描',
                position: '仓位控制',
                stats: '统计过滤',
              }

              // 过滤逻辑
              const filteredConfigs = Object.entries(configs).filter(([key, config]) => {
                // 1. 基础过滤（排除非对象、仅排除部分风险旋钮与 API Key；周日/晚间控制在全局配置中展示）
                if (!config || typeof config !== 'object') return false
                const HIDDEN_RISK_KNOBS = ['MIN_MARGIN_USDT', 'MIN_POSITION_PERCENT', 'MAX_POSITION_PERCENT',
                  'MAX_TOTAL_POSITION_PERCENT', 'AUTO_TRADE_ENABLED', 'MAX_OPEN_POSITIONS', 'MAX_DAILY_ENTRIES']
                if (HIDDEN_RISK_KNOBS.includes(key)) return false
                if (key === 'BINANCE_API_KEY' || key === 'BINANCE_API_SECRET' || key === 'USE_TESTNET') return false

                // 2. Tab 过滤
                if (activeTab !== 'all' && config.category !== activeTab) return false

                // 3. 搜索过滤
                if (searchTerm) {
                  const lowerTerm = searchTerm.toLowerCase()
                  const matchKey = key.toLowerCase().includes(lowerTerm)
                  const matchDesc = (config.description || '').toLowerCase().includes(lowerTerm)
                  const matchLabel = (KEY_LABELS[key] || '').toLowerCase().includes(lowerTerm)
                  if (!matchKey && !matchDesc && !matchLabel) return false
                }

                return true
              })

              if (filteredConfigs.length === 0) {
                 return <div style={{ textAlign: 'center', color: '#999', padding: '30px', background: '#f5f5f5', borderRadius: '8px' }}>未找到匹配的配置项</div>
              }

              // 分组渲染
              // 如果是 'all' Tab，按 Category 分组
              // 如果是特定 Tab，直接渲染（或者也分组，只有一个组）
              const groupsToRender = activeTab === 'all' 
                ? Object.keys(configCategories) 
                : [activeTab]

              // 将 filteredConfigs 转为 Map 或方便查找的结构，或者每次 filter
              
              return groupsToRender.map(category => {
                const categoryLabel = configCategories[category] || category
                const groupConfigs = filteredConfigs.filter(([_, cfg]) => cfg.category === category)
                
                if (groupConfigs.length === 0) return null

                return (
                  <div key={category} style={{ marginBottom: '24px' }}>
                    <h4 style={{ marginBottom: '12px', fontSize: '16px', fontWeight: '600', borderLeft: '4px solid #007bff', paddingLeft: '10px', color: '#333' }}>
                      {categoryLabel}
                    </h4>
                    <div className="config-grid" style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(300px, 1fr))', gap: '16px' }}>
                      {groupConfigs.map(([key, config]) => (
                        <ConfigItem
                          key={key}
                          label={key}
                          config={config}
                          onUpdate={(val) => handleConfigUpdate(key, val, config)}
                          disabled={saving}
                        />
                      ))}
                    </div>
                  </div>
                )
              })
            })()
          ) : (
            <div style={{ padding: '20px', textAlign: 'center', color: '#666' }}>
              {loading ? '加载配置中...' : '暂无配置项'}
            </div>
          )}
        </section>
      )}





      {/* 配置快照 Modal */}
      {showSnapshot && (
        <div className="snapshot-modal-overlay" onClick={() => setShowSnapshot(false)} role="presentation">
          <div className="snapshot-modal" onClick={(e) => e.stopPropagation()}>
            <div className="snapshot-modal-header">
              <div>
                <h3>当前整体配置快照</h3>
                <div className="snapshot-hint">
                  导出全量策略配置（与配置页数据源一致）。config_guide 含各配置项说明与建议，便于策略分析。默认脱敏 API Key/Secret，可勾选明文后重新生成。
                </div>
              </div>
              <button type="button" className="snapshot-close" onClick={() => setShowSnapshot(false)}>
                关闭
              </button>
            </div>

            <div className="snapshot-toolbar">
              <label className="snapshot-checkbox">
                <input
                  type="checkbox"
                  checked={snapshotIncludeSecrets}
                  onChange={async (e) => {
                    const checked = e.target.checked
                    setSnapshotIncludeSecrets(checked)
                    await openSnapshot(checked)
                  }}
                />
                显示敏感信息（明文）
              </label>

              <div className="snapshot-actions">
                <button type="button" className="system-btn" onClick={copySnapshot}>
                  复制
                </button>
                <button type="button" className="system-btn primary" onClick={downloadSnapshot}>
                  下载 JSON
                </button>
              </div>
            </div>

            <pre className="snapshot-pre">{snapshotText}</pre>
          </div>
        </div>
      )}
    </div>
  )
}

export default GlobalConfig
