﻿// 交易房间页面 v3 - 重构消息UI、可复用组件体系
var _a = React; var useState = _a.useState, useEffect = _a.useEffect, useRef = _a.useRef, useCallback = _a.useCallback;
var h = React.createElement;
function svgArrL(c){return h('svg',{width:20,height:20,viewBox:'0 0 24 24',fill:'none',stroke:c||'#334155',strokeWidth:1.875,strokeLinecap:'round',strokeLinejoin:'round'},h('path',{d:'M15 18l-6-6 6-6'}));}


// ==================== 常量 ====================
var STATUS_MAP = {
  10:'待支付',20:'已支付待验号',30:'验号中',40:'待换绑',
  50:'换绑中',60:'审核期',70:'待买家确认',80:'已完成',
  90:'纠纷中',100:'仲裁中',110:'已退款',120:'已取消'
};

var STEP_LABELS = {
  awaiting_confirmation: '确认交易',
  awaiting_credentials: '提交账号资料',
  credentials_submitted: '验号阶段',
  buyer_verifying: '验号阶段',
  buyer_verified: '待换绑',
  awaiting_rebind: '待换绑',
  awaiting_verification: '验号阶段',
  completed: '交易成功',
  inspection_dispute: '纠纷处理中'
};

var DISPUTE_TYPES = [
  {v:'content_mismatch',l:'账号内容与描述不符'},
  {v:'cannot_login',l:'无法登录'},
  {v:'other',l:'其他'}
];

// SVG icon paths for message icons
var ICON_PATHS = {
  confirm: 'M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z',
  info: 'M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z',
  key: 'M15 7a2 2 0 012 2m4 0a6 6 0 01-7.743 5.743L11 17H9v2H7v2H4a1 1 0 01-1-1v-2.586a1 1 0 01.293-.707l5.964-5.964A6 6 0 1121 9z',
  check: 'M5 13l4 4L19 7',
  done: 'M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z',
  video: 'M15 10l4.553-2.276A1 1 0 0121 8.618v6.764a1 1 0 01-1.447.894L15 14M5 18h8a2 2 0 002-2V8a2 2 0 00-2-2H5a2 2 0 00-2 2v8a2 2 0 002 2z',
  rebind: 'M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15',
  alert: 'M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-2.5L13.732 4c-.77-.833-1.964-.833-2.732 0L4.082 16.5c-.77.833.192 2.5 1.732 2.5z',
  payment: 'M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z',
  document: 'M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z',
  shield: 'M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z',
  clock: 'M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z',
  lock: 'M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z',
  copy: 'M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2 M15 2H9a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V3a1 1 0 0 0-1-1z',
};

// 可复用: SVG图标渲染
function svgIcon(pathData, size) {
  var s = size || 18;
  var attrs = { width: s, height: s, viewBox: '0 0 24 24', fill: 'none', stroke: 'currentColor', strokeWidth: 2, strokeLinecap: 'round', strokeLinejoin: 'round' };
  return h('svg', attrs, h('path', { d: pathData }));
}

// 可复用: 系统消息卡片
function SystemMessageCard(props) {
  var iconPath = (ICON_PATHS[props.iconType] || ICON_PATHS.info);
  return h('div', {
    className: 'sys-msg-card' + (props.className ? ' ' + props.className : '')
  },
    h('div', { className: 'sys-msg-card-inner' },
      props.title ? h('div', { style: { fontWeight: 600, fontSize: 14, marginBottom: 6 } }, props.title) : null,
      h('div', { style: { display: 'flex', alignItems: 'flex-start', gap: 6 } },
        h('span', { className: 'sys-msg-card-icon', style: { flexShrink: 0, marginTop: 1 } }, svgIcon(iconPath, 14)),
        h('span', { className: 'sys-msg-card-text', style: { flex: 1, whiteSpace: 'pre-wrap', lineHeight: '1.7', fontSize: 13, color: '#444' } }, props.text)
      ),
      props.time && h('span', { className: 'sys-msg-card-time' },
        new Date(props.time).toLocaleTimeString('zh-CN', {hour:'2-digit',minute:'2-digit'})
      )
    )
  );
}

// 可复用: 交易操作卡片
function TradeActionCard(props) {
  var iconPath = (ICON_PATHS[props.iconType] || ICON_PATHS.document);
  var variantClass = '';
  if (props.variant === 'danger') variantClass = ' danger-zone';
  else if (props.variant === 'warn') variantClass = ' warn-zone';

  return h('div', {
    className: 'trade-action-card' + variantClass,
    key: props.keyName
  },
    h('div', { className: 'tac-header' },
      h('span', { className: 'tac-icon' }, svgIcon(iconPath, 16)),
      h('span', { className: 'tac-title' }, props.title)
    ),
    props.children && h('div', { className: 'tac-body' }, props.children),
    props.hint && h('div', { className: 'tac-hint' }, props.hint),
    props.buttons && props.buttons.length > 0 && h('div', { className: 'tac-actions' },
      props.buttons.map(function(b, i) {
        return h('button', {
          key: i,
          className: 'tac-btn' + (b.variant ? ' ' + b.variant : ''),
          disabled: !!b.disabled,
          onClick: b.onClick
        }, b.label);
      })
    )
  );
}

// 可复用: 交易进度条组件
// currentStep: 0=全部待开始, 1=确认交易, 2=买家验号, 3=手动换绑, 4=交易完成
var TRADE_STEPS = ["确认交易", "买家验号", "手动换绑", "交易完成"];

function TradeProgressSteps(props) {
  var step = props.currentStep || 0;
  return h('div', {className:'tr3-progress'},
    TRADE_STEPS.map(function(label, i) {
      var idx = i + 1;
      var state = '';
      if (idx < step) state = 'done';
      else if (idx === step) state = 'active';
      else state = 'pending';
      return h('div', {key:i, className:'tr3-progress-item ' + state},
        h('div', {className:'tr3-progress-line-wrap'},
          i > 0 && h('span', {className:'tr3-progress-line ' + (idx <= step ? 'done' : '')})
        ),
        h('div', {className:'tr3-progress-dot-wrap'},
          h('span', {className:'tr3-progress-dot ' + state},
            state === 'done'
              ? h('svg', {width:10,height:10,viewBox:'0 0 24 24',fill:'none',stroke:'currentColor',strokeWidth:3,strokeLinecap:'round',strokeLinejoin:'round'}, h('path',{d:'M5 13l4 4L19 7'}))
              : h('span', {className:'tr3-progress-num'}, idx)
          )
        ),
        h('div', {className:'tr3-progress-label ' + state}, label)
      );
    })
  );
}

// 状态到进度步骤的映射
function getProgressStep(status) {
  if (!status || status < 20) return 0;      // 待支付: 全部待开始
  if (status === 20) return 1;                // 已支付: 确认交易阶段
  if (status >= 30 && status < 40) return 2;  // 验号中: 买家验号阶段
  if (status >= 40 && status < 80) return 3;  // 换绑中: 手动换绑阶段
  if (status >= 80) return 4;                  // 已完成: 全部完成
  return 0;
}

// ==================== 主组件 ====================
function TradeRoomPage() {
  // --- 基础状态 ---
  var _session = useState(null); var session = _session[0], setSession = _session[1];
  var _messages = useState([]); var messages = _messages[0], setMessages = _messages[1];
  var _loading = useState(true); var loading = _loading[0], setLoading = _loading[1];
  var _error = useState(''); var error = _error[0], setError = _error[1];
  var _actionLoading = useState(false); var actionLoading = _actionLoading[0], setActionLoading = _actionLoading[1];
  var _actionError = useState(''); var actionError = _actionError[0], setActionError = _actionError[1];
  var _cooldownRemaining = useState(0); var cooldownRemaining = _cooldownRemaining[0], setCooldownRemaining = _cooldownRemaining[1];

  // --- 凭证展示 ---
  var _creds = useState(null); var creds = _creds[0], setCreds = _creds[1];
  var _showPass = useState(false); var showPass = _showPass[0], setShowPass = _showPass[1];
  var _verifyCode = useState(null); var verifyCode = _verifyCode[0], setVerifyCode = _verifyCode[1];
  var _spData = useState(null); var spData = _spData[0], setSpData = _spData[1];
  var _rebindCode = useState(null); var rebindCode = _rebindCode[0], setRebindCode = _rebindCode[1];

  // --- 弹层 ---
  var _showVerifyConfirm = useState(false); var showVerifyConfirm = _showVerifyConfirm[0], setShowVerifyConfirm = _showVerifyConfirm[1];
  var _showTradeSuccess = useState(false); var showTradeSuccess = _showTradeSuccess[0], setShowTradeSuccess = _showTradeSuccess[1];
  var _showDispute = useState(false); var showDispute = _showDispute[0], setShowDispute = _showDispute[1];
  var _showCredReject = useState(false); var showCredReject = _showCredReject[0], setShowCredReject = _showCredReject[1];
  var _disputeForm = useState({disputeType:'',description:'',images:[]});
  var _showCredConfirm = useState(false); var showCredConfirm = _showCredConfirm[0], setShowCredConfirm = _showCredConfirm[1];
  var disputeForm = _disputeForm[0], setDisputeForm = _disputeForm[1];
  var _disputeImages = useState([]); var disputeImages = _disputeImages[0], setDisputeImages = _disputeImages[1];
  var _disputeUploading = useState(false); var disputeUploading = _disputeUploading[0], setDisputeUploading = _disputeUploading[1];

  // --- 输入 ---
  var _credInput = useState({gameAccount:'',gamePassword:''}); var credInput = _credInput[0], setCredInput = _credInput[1];
  var _vcInput = useState(''); var vcInput = _vcInput[0], setVcInput = _vcInput[1];
  var _rbForm = useState({secondaryPassword:'',noSp:false}); var rbForm = _rbForm[0], setRbForm = _rbForm[1];
  var _vcCooldown = useState(0); var vcCooldown = _vcCooldown[0], setVcCooldown = _vcCooldown[1];
  var _loginCodeCooldown = useState(0); var loginCodeCooldown = _loginCodeCooldown[0], setLoginCodeCooldown = _loginCodeCooldown[1];
    var _payout = useState(null); var payout = _payout[0], setPayout = _payout[1];
  var _payoutLoading = useState(false); var payoutLoading = _payoutLoading[0], setPayoutLoading = _payoutLoading[1];
  var _recordingCooldown = useState(5); var recordingCooldown = _recordingCooldown[0], setRecordingCooldown = _recordingCooldown[1];
  var _recordingTimer = useState(null); var recordingTimer = _recordingTimer[0], setRecordingTimer = _recordingTimer[1];
  var _codeSent = useState(false); var codeSent = _codeSent[0], setCodeSent = _codeSent[1];
  var _vcCooldown = useState(0); var vcCooldown = _vcCooldown[0], setVcCooldown = _vcCooldown[1];

  var pageParams = NK.Store.get('pageParams');
  var orderId = pageParams && pageParams.orderId;
  var source = pageParams && pageParams.source;
  var user = NK.Store.get('user');
  var pollingRef = useRef(null);

  var fetchPayoutStatus = function(oid) {
    NK.Api.Payouts.get(oid).then(function(r) {
      if (r && r.code === 0) setPayout(r.data);
    }).catch(function(){});
  };

  // ==================== 数据加载 ====================
  var order = session ? session.order : null;
  var tradeSession = session ? session.session : null;
  var role = session ? session.role : '';
  var status = order ? order.status : 0;
  var step = tradeSession ? tradeSession.current_step : '';
  var rebindState = session ? session.rebindState : null;
  var hasPendingVerifyRequest = session ? session.hasPendingVerifyRequest : false;
  var verifyRequestStatus = session ? session.verifyRequestStatus : null;
  var buyerConfirmed = tradeSession ? tradeSession.buyer_confirmed : false;
  var sellerConfirmed = tradeSession ? tradeSession.seller_confirmed : false;

  var fetchAll = useCallback(async function() {
    if (!orderId) return;
    try {
      var _r = await Promise.all([
        NK.Api.Trades.session(orderId),
        NK.Api.Trades.messages(orderId)
      ]);
      var sRes = _r[0], mRes = _r[1];
      if (sRes.code === 0) setSession(sRes.data);
      else if (sRes.code === -1) setError(sRes.message);
      if (mRes.code === 0) setMessages(mRes.data || []);
    } catch (e) { /* ignore poll errors */ }
  }, [orderId]);

  useEffect(function() {
    if (!orderId) { setLoading(false); setError('缺少订单ID'); return; }
    setLoading(true);
    fetchAll().finally(function() { setLoading(false); });
  }, [orderId]);

  useEffect(function() {
    if (!orderId || error) return;
    pollingRef.current = setInterval(fetchAll, 5000);
    return function() {
      if (pollingRef.current) clearInterval(pollingRef.current);
    };
  }, [orderId, error, fetchAll]);

  // Buyer cooldown timer (updates every second)
  useEffect(function() {
    if (!order || order.status !== 20 || role !== 'buyer') return;
    var orderTime = order.create_time ? new Date(order.create_time).getTime() : 0;
    if (!orderTime) return;
    var tick = function() {
      var elapsed = Date.now() - orderTime;
      var cooldownMs = 2 * 60 * 60 * 1000;
      var remaining = Math.max(0, cooldownMs - elapsed);
      setCooldownRemaining(remaining);
      if (remaining <= 0) return; // stop updating when expired
    };
    tick();
    var timer = setInterval(tick, 1000);
    return function() { clearInterval(timer); };
  }, [order, role]);

  // Recording ack cooldown timer (5 seconds)
  useEffect(function() {
    return function() { if (recordingTimer) clearTimeout(recordingTimer); };
  }, [recordingTimer]);

  // Auto-fetch credentials when buyer enters verification stage
  useEffect(function() {
    if (status !== 30 || role !== 'buyer') return;
    if (creds) return;
    // Compute hasCreds inline (same logic as buyer block)
    var hc = step === 'credentials_submitted' || step === 'buyer_verifying' || step === 'buyer_verified' || step === 'awaiting_rebind' || step === 'credential_rejected';
    if (!hc) return;
    // Compute hasAck inline
    var ha = (tradeSession && (tradeSession.buyer_recording_ack == 1 || tradeSession.buyer_recording_ack === true)) || step === 'buyer_verifying' || step === 'buyer_verified' || step === 'awaiting_rebind' || step === 'credential_rejected';
    if (!ha) return;
    handleGetCreds();
  }, [status, role, step, tradeSession, creds, orderId]);


  // Auto-fetch verify code when seller sends it
  useEffect(function() {
    if (status !== 30 || role !== 'buyer') return;
    if (verifyCode) return; // already have code
    if (!hasPendingVerifyRequest && step !== 'buyer_verifying' && verifyRequestStatus !== 'submitted') return;
    // Poll for code every 5 seconds
    var pollVC = async function() {
      try {
        var res = await NK.Api.Trades.verifyCode(orderId);
        if (res.code === 0) { setVerifyCode(res.data); }
      } catch(e) {}
    };
    pollVC();
    var timer = setInterval(pollVC, 5000);
    return function() { clearInterval(timer); };
  }, [status, role, step, hasPendingVerifyRequest, verifyCode, orderId]);

  // Start recording cooldown when entering buyer verification
  useEffect(function() {
    if (status === 30 && role === 'buyer' && tradeSession && tradeSession.buyer_recording_ack !== 1 && step !== 'buyer_verifying' && step !== 'buyer_verified' && step !== 'awaiting_rebind') {
      setRecordingCooldown(5);
      var cd = 5;
      var tick = function() {
        cd--;
        setRecordingCooldown(cd);
        if (cd > 0) {
          var t = setTimeout(tick, 1000);
          setRecordingTimer(t);
        }
      };
      var t = setTimeout(tick, 1000);
      setRecordingTimer(t);
    }
  }, [status, role, tradeSession && tradeSession.buyer_recording_ack, step]);

  // Load rebindState from session (initial + refreshes)
  useEffect(function() {
    if (rebindState && rebindState.spData) {
      if (!spData || spData.secondaryPassword !== rebindState.spData.secondaryPassword || spData.noSecondaryPassword !== rebindState.spData.noSecondaryPassword) {
        setSpData(rebindState.spData);
      }
    }
    if (rebindState && rebindState.rebindCode) {
      if (!rebindCode || rebindCode.code !== rebindState.rebindCode.code) {
        setRebindCode(rebindState.rebindCode);
      }
    }
  }, [rebindState]);

  // Poll for rebind data updates when buyer is in rebind stage
  useEffect(function() {
    if ((status !== 40 && status !== 50) || role !== 'buyer') return;
    var poll = function() {
      // Always fetch latest secondary password
      NK.Api.Trades.secondaryPassword(orderId).then(function(r){
        if (r.code === 0 && r.data) {
          var newSp = r.data;
          if (!spData || spData.secondaryPassword !== newSp.secondaryPassword || spData.noSecondaryPassword !== newSp.noSecondaryPassword) {
            setSpData(newSp);
          }
        }
      }).catch(function(){});
      // Always fetch latest rebind code
      NK.Api.Trades.rebindCode(orderId).then(function(r){
        if (r.code === 0 && r.data && r.data.code) {
          var newCode = r.data.code;
          if (!rebindCode || rebindCode.code !== newCode) {
            setRebindCode(r.data);
          }
        }
      }).catch(function(){});
    };
    poll();
    var timer = setInterval(poll, 5000);
    return function() { clearInterval(timer); };
  }, [status, role, orderId, spData, rebindCode]);

  // Poll for verify code when buyer is waiting in status 30
  useEffect(function() {
    if (status !== 30 || role !== 'buyer') return;
    if (!hasPendingVerifyRequest && verifyRequestStatus !== 'pending') return;
    var poll = function() {
      NK.Api.Trades.verifyCode(orderId).then(function(r){
        if (r.code === 0 && r.data && r.data.code) {
          setVerifyCode(r.data);
        }
      }).catch(function(){});
    };
    poll();
    var timer = setInterval(poll, 5000);
    return function() { clearInterval(timer); };
  }, [status, role, orderId, hasPendingVerifyRequest, verifyRequestStatus]);

  // Rebind verification code 60s cooldown
  useEffect(function() {
    if (vcCooldown <= 0) return;
    var timer = setTimeout(function() { setVcCooldown(function(c){ return c - 1; }); }, 1000);
    return function() { clearTimeout(timer); };
  }, [vcCooldown]);

  // Login code cooldown timer (status 30 only)
  useEffect(function() {
    if (loginCodeCooldown <= 0) return;
    var timer = setTimeout(function() { setLoginCodeCooldown(function(c){ return c - 1; }); }, 1000);
    return function() { clearTimeout(timer); };
  }, [loginCodeCooldown]);

  // Reset vcCooldown when entering rebind phase (status 40/50)
  useEffect(function() {
    if (status === 40 || status === 50) { setVcCooldown(0); }
  }, [status]);

  // Reset codeSent when new pending verify request arrives (so seller sees new input)
  useEffect(function() {
    if (verifyRequestStatus === 'pending') { setCodeSent(false); setVcInput(''); }
  }, [verifyRequestStatus]);

  // Reset vcInput when new rebind code request arrives
  useEffect(function() {
    if (step === 'awaiting_rebind_code' || step === 'buyer_verifying') { setVcInput(''); }
  }, [step]);

  // ==================== 操作 ====================
  var doAction = async function(fn) {
    setActionLoading(true); setActionError('');
    try {
      var res = await fn();
      if (res.code === 0) {
        setCreds(null); setVerifyCode(null); setRebindCode(null); setSpData(null); setVcCooldown(0);
        setShowVerifyConfirm(false); setShowTradeSuccess(false);
        setShowDispute(false);
        await fetchAll();
      } else {
        setActionError(res.message || '操作失败');
      }
    } catch (e) {
      setActionError('网络错误');
    } finally {
      setActionLoading(false);
    }
  };

  var handleConfirm = function() { doAction(function() { return NK.Api.Trades.confirm(orderId); }); };
  var _cancelLoading = useState(false); var cancelLoading = _cancelLoading[0], setCancelLoading = _cancelLoading[1];
  var _showCancelConfirm = useState(false); var showCancelConfirm = _showCancelConfirm[0], setShowCancelConfirm = _showCancelConfirm[1];

  var handleCancel = function() {
    setShowCancelConfirm(true);
  };

  var doCancel = async function() {
    setCancelLoading(true); setActionError('');
    try {
      var res = await NK.Api.Trades.cancel(orderId);
      if (res.code === 0) {
        setShowCancelConfirm(false);
        await fetchAll();
      } else {
        setActionError(res.message || '取消失败');
      }
    } catch (e) {
      setActionError('网络错误');
    } finally {
      setCancelLoading(false);
    }
  };

  var handleSubmitCreds = function() {
    if (!credInput.gameAccount.trim()) { setActionError('请输入游戏账号'); return; }
    if (!credInput.gamePassword.trim()) { setActionError('请输入游戏密码'); return; }
    setShowCredConfirm(true);
  };
  var handleCredReject = function() { doAction(function() { return NK.Api.Trades.credentialReject(orderId); }); };
  var doSubmitCreds = function() {
    setShowCredConfirm(false);
    doAction(function() { return NK.Api.Trades.submitCredentials(orderId, credInput); });
  };
  var handleRecordingAck = function() { doAction(function() { return NK.Api.Trades.recordingAck(orderId); }); };
  var handleGetCreds = async function() {
    setActionLoading(true); setActionError('');
    var res = await NK.Api.Trades.credentials(orderId);
    setActionLoading(false);
    if (res.code === 0) setCreds(res.data);
    else setActionError(res.message || '获取失败');
  };
  // 获取验证码（合并请求+显示）
  var handleRequestVC = async function() {
    if (loginCodeCooldown > 0) return;
    setActionLoading(true); setActionError(''); setVerifyCode(null);
    try {
      // Step 1: Request verify code from seller
      var reqRes = await NK.Api.Trades.requestVerifyCode(orderId);
      if (reqRes.code !== 0) {
        var remain = (reqRes.data && reqRes.data.remainingSeconds) || 0;
        if (remain > 0) { setLoginCodeCooldown(remain); setActionLoading(false); return; }
        setActionError(reqRes.message || '请求失败');
        setActionLoading(false);
        return;
      }
      // Start 60s cooldown
      setLoginCodeCooldown(60);
      await fetchAll();
      setActionLoading(false);
      // Step 3: Try to fetch the code if seller already sent it
      try {
        var codeRes = await NK.Api.Trades.verifyCode(orderId);
        if (codeRes.code === 0) { setVerifyCode(codeRes.data); }
      } catch(e) {}
    } catch (e) {
      setActionError('网络错误');
      setActionLoading(false);
    }
  };
  // 获取验证码（仅拉取，不重新请求）
  var handleFetchVC = async function() {
    setActionLoading(true); setActionError('');
    try {
      var res = await NK.Api.Trades.verifyCode(orderId);
      if (res.code === 0) { setVerifyCode(res.data); await fetchAll(); }
      else { setActionError(res.message || '验证码尚未就绪'); }
    } catch (e) { setActionError('网络错误'); }
    finally { setActionLoading(false); }
  };

  var handleSubmitVC = async function() {
    if (!vcInput.trim()) { setActionError('请输入验证码'); return; }
    setActionLoading(true); setActionError('');
    try {
      var res = await NK.Api.Trades.submitVerifyCode(orderId, {code: vcInput.trim()});
      if (res.code === 0) {
        setCodeSent(true);
        setVcInput('');
        await fetchAll();
      } else {
        setActionError(res.message || '发送失败');
      }
    } catch (e) {
      setActionError('网络错误');
    } finally {
      setActionLoading(false);
    }
  };
  var handleVerifyConfirm = function() { doAction(function() { return NK.Api.Trades.verifyConfirm(orderId); }); };
  var handleGetRebindCode = async function() {
    var res = await NK.Api.Trades.rebindCode(orderId);
    if (res.code === 0) setRebindCode(res.data);
  };
  var handleRequestRebindCode = function() {
    if (vcCooldown > 0) return;
    doAction(async function() {
      var res = await NK.Api.Trades.requestRebindCode(orderId);
      if (res.code === 0) setVcCooldown(60);
      return res;
    });
  };
  var handleRebindConfirm = function() { doAction(function() { return NK.Api.Trades.rebindConfirm(orderId); }); };
  var handleDispute = function() {
    if (!disputeForm.disputeType) { setActionError('请选择纠纷类型'); return; }
    if (!disputeForm.description || !disputeForm.description.trim()) { setActionError('请填写问题描述'); return; }
    if (disputeForm.description.length > 500) { setActionError('问题描述不能超过500字'); return; }
    doAction(async function() {
      // Upload images first if any
      var imageUrls = [];
      if (disputeImages.length > 0) {
        setDisputeUploading(true);
        for (var i = 0; i < disputeImages.length; i++) {
          var formData = new FormData();
          formData.append('image', disputeImages[i]);
          var uploadRes = await NK.Api.Upload.image(formData);
          if (uploadRes.code === 0 && uploadRes.data && uploadRes.data.url) {
            imageUrls.push(uploadRes.data.url);
          }
        }
        setDisputeUploading(false);
      }
      return NK.Api.Trades.inspectionDispute(orderId, {
        disputeType: disputeForm.disputeType,
        description: disputeForm.description.trim(),
        images: imageUrls
      });
    });
  };

  var copyText = function(text) {
    if (!text) return;
    if (navigator.clipboard) {
      navigator.clipboard.writeText(text).catch(function(){});
    }
    var ta = document.createElement('textarea');
    ta.value = text; ta.style.position = 'fixed'; ta.style.opacity = '0';
    document.body.appendChild(ta); ta.select();
    document.execCommand('copy'); document.body.removeChild(ta);
  };

  // ==================== 消息解析 (修复 [object Object] bug) ====================
  var parseMsgContent = function(msg) {
    var c = msg.content;
    if (c === null || c === undefined) return { text: '' };
    // 已经是对象 (MySQL json column 直接返回对象)
    if (typeof c === 'object' && !Array.isArray(c)) {
      if (c.text !== undefined) return c;
      return { text: JSON.stringify(c) };
    }
    // 字符串: 尝试 JSON 解析
    if (typeof c === 'string') {
      try {
        var parsed = JSON.parse(c);
        if (typeof parsed === 'object' && parsed !== null) return parsed;
        return { text: String(parsed) };
      } catch(e) {
        return { text: c };
      }
    }
    return { text: String(c) };
  };

  var isSystemMsg = function(msg) {
    return msg.msg_type === 'system_notice' || msg.msg_type === 'system';
  };

  var getMsgIconType = function(text) {
    if (!text) return 'info';
    var t = text;
    if (/确认|confirm/.test(t)) return 'confirm';
    if (/提交|资料|密码|账号/.test(t)) return 'key';
    if (/录屏|录制/.test(t)) return 'video';
    if (/验证|验号|verify/.test(t)) return 'check';
    if (/换绑|rebind|绑定/.test(t)) return 'rebind';
    if (/完成|成功|到账/.test(t)) return 'done';
    if (/纠纷|争议|问题|售后/.test(t)) return 'alert';
    return 'info';
  };
  // ==================== 渲染操作卡片 ====================
  var renderActionCards = function() {
    if (!session || !role) return null;

    // 状态10: 待支付
    if (status === 10) {
      return h(TradeActionCard, {iconType:'payment',title:'订单待支付',keyName:'act-pay',hint:'订单尚未完成支付，请先完成支付后再进入交易流程。',
        buttons:[{label:'返回订单页',variant:'primary',onClick:function(){NK.Store.navigate('orders');}}]
      });
    }

    // 状态20: 确认交易
    if (status === 20) {
      var mySideConfirmed = (role === 'buyer' && buyerConfirmed) || (role === 'seller' && sellerConfirmed);
      var bothConfirmed = buyerConfirmed && sellerConfirmed;
      if (bothConfirmed) {
        return h(TradeActionCard, {iconType:'done',title:'双方已确认',keyName:'act-done',hint:'订单即将进入验号阶段，请稍候...'});
      }
      // Build cancel button
      var cancelBtn = null;
      if (role === "seller") {
        cancelBtn = {label:"取消交易",variant:"danger",onClick:handleCancel};
      } else if (role === "buyer") {
        if (cooldownRemaining <= 0) {
          cancelBtn = {label:"取消交易",variant:"danger",onClick:handleCancel};
        } else {
          var hrs = Math.floor(cooldownRemaining / 3600000);
          var mins = Math.floor((cooldownRemaining % 3600000) / 60000);
          var secs = Math.floor((cooldownRemaining % 60000) / 1000);
          var timeStr = String(hrs).padStart(2,"0") + ":" + String(mins).padStart(2,"0") + ":" + String(secs).padStart(2,"0");
          cancelBtn = {label:"还需 " + timeStr + " 可取消",variant:"danger",disabled:true};
        }
      }
      if (mySideConfirmed) {
        return h(TradeActionCard, {iconType:"confirm",title:"已确认，等待对方",keyName:"act-wait",hint:"您已确认交易，等待对方确认...",
          buttons: cancelBtn ? [cancelBtn] : []
        });
      }
      var noticeAmount = (order && (order.amount || order.total_amount)) || 0;
      var noticeAmountStr = '¥' + (parseFloat(noticeAmount) || 0).toFixed(2);
      var noticeType = (order && order.type_name) || '成品号';
      var noticeOrderId = (order && (order.id || order.order_no)) || '';
      var noticeText = '交易已创建，当前进入「确认交易」阶段。' + "\n\n" +
        '买家已对商品发起交易申请，卖家需在规定时间内确认是否继续交易。' + "\n\n" +
        '请双方认真核对以下交易信息：' + "\n\n" +
        '交易金额：' + noticeAmountStr + "\n" +
        '商品类型：' + noticeType + "\n" +
        '订单编号：' + (noticeOrderId || '暂无') + "\n\n" +
        '当前阶段：确认交易' + "\n\n" +
        '1、请卖家确认商品信息与描述一致。' + "\n" +
        '2、请卖家设置二级密码保护，并将贵重可交易物品锁定。' + "\n" +
        '3、请买家确认购买意向是否明确，仔细阅读商品描述及交易规则。' + "\n" +
        '4、交易过程中请保持联系畅通。' + "\n\n" +
        '温馨提示：' + "\n\n" +
        '交易过程中请勿私下转账、私下换绑、私下发送敏感信息。' + "\n" +
        '所有交易沟通请在本交易群内完成，方便平台保留记录以及售后。' + "\n" +
        '如交易信息与商品描述不一致，请立即停止交易并联系平台客服。' + "\n" +
        '未经双方确认前，交易不会进入下一阶段。' + "\n\n" +
        '双方点击「确认交易」后，系统将进入买家验号阶段。';
      return h(TradeActionCard, {iconType:"confirm",title:"交易须知",keyName:"act-confirm",
        children: h("div", {style:{whiteSpace:"pre-wrap",lineHeight:"1.7",fontSize:13,color:"#444"}}, noticeText),
        buttons:[
          {label:"确认交易",variant:"primary",onClick:handleConfirm}
        ].concat(cancelBtn ? [cancelBtn] : [])
      });
    }

    // 状态30: 验号中
    if (status === 30) {
            // 买家验号阶段交易须知
      var verifyNotice = h(TradeActionCard, {iconType:"info",title:"交易须知",keyName:"verify-notice",
        children: h("div", {style:{whiteSpace:"pre-wrap",lineHeight:"1.7",fontSize:13,color:"#444"}},
          '当前进入「买家验号」阶段。' + "\n\n" +
          '该阶段交易流程为：' + "\n" +
          '① 卖家提供账号密码资料' + "\n" +
          '② 买家开启录屏并登录账号' + "\n" +
          '③ 买家发送登录验证码' + "\n" +
          '④ 卖家提供登录验证码' + "\n" +
          '⑤ 买家输入验证码完成登录并验证账号' + "\n\n" +
          '验号期间请买家重点确认以下内容：' + "\n\n" +
          '为避免不必要纠纷，买家请在验号阶段全程保持录屏。' + "\n" +
          '账号是否能正常登录。' + "\n" +
          '登录方式是否与商品描述一致。' + "\n" +
          '账号内装备、坐骑、道具、资源、货币等是否与描述一致。' + "\n" +
          '是否存在明显货不对板、虚假描述、遗漏重要信息等情况。' + "\n\n" +
          '请注意：' + "\n\n" +
          '买家验号完成后，如确认无误，应点击「验号通过」。' + "\n" +
          '验号通过后，订单将进入「手动换绑」阶段。' + "\n\n" +
          '如发现问题，请不要点击验号通过，请点击「申请售后」并详细说明问题。' + "\n" +
          '若双方无法协商解决，可申请客服介入。'
        )
      });
      if (role === 'seller') {
                if (step === "credential_rejected") {
          return [verifyNotice, h(TradeActionCard, {iconType:"alert",title:"买家请求重新提供账号资料",keyName:"re-cred",
            hint:"买家反馈当前账号或密码无法正常登录，请卖家核对后重新提交账号密码资料。",
            children: h("div", null,
              h("input", {className:"card-input",type:"text",placeholder:"游戏账号",value:credInput.gameAccount,onChange:function(e){setCredInput(function(p){return Object.assign({},p,{gameAccount:e.target.value});});}}),
              h("div", {style:{position:"relative"}},
                h("input", {className:"card-input",type:showPass?"text":"password",placeholder:"游戏密码",value:credInput.gamePassword,onChange:function(e){setCredInput(function(p){return Object.assign({},p,{gamePassword:e.target.value});});}}),
                h("span", {className:"input-eye",onClick:function(){setShowPass(!showPass);}}, showPass?"🙈":"👁")
              ),
              actionError && h("div",{style:{color:"#ff4d4f",fontSize:13,marginTop:6}}, actionError),
              actionLoading && h("div",{style:{color:"#666",fontSize:13,marginTop:6}},"提交中..."),
            ),
            buttons:[{label:"重新提交账号资料",variant:"primary",onClick:handleSubmitCreds}]
          })];
        }
        var hasCreds = step === 'credentials_submitted' || step === 'buyer_verifying' || step === 'buyer_verified' || step === 'awaiting_rebind' || step === 'credential_rejected';
        if (!hasCreds) {
          return [verifyNotice, h(TradeActionCard, {iconType:'key',title:'提交账号资料',keyName:'act-creds',
            children: h('div', null,
              h('input', {className:'card-input',type:'text',placeholder:'游戏账号',value:credInput.gameAccount,onChange:function(e){setCredInput(function(p){return Object.assign({},p,{gameAccount:e.target.value});});}}),
              h('div', {style:{position:'relative'}},
                h('input', {className:'card-input',type:showPass?'text':'password',placeholder:'游戏密码',value:credInput.gamePassword,onChange:function(e){setCredInput(function(p){return Object.assign({},p,{gamePassword:e.target.value});});}}),
                h('span', {className:'input-eye',onClick:function(){setShowPass(!showPass);}}, showPass?'🙈':'👁')
              ),
              actionError && h('div', {style:{color:'#ff4d4f',fontSize:13,marginTop:6}}, actionError),
              actionLoading && h('div', {style:{color:'#666',fontSize:13,marginTop:6}}, '提交中...'),
            ),
            buttons:[{label:'确认提交',variant:'primary',onClick:handleSubmitCreds}]
          })];
        }
        // Check verification code request status
        if (step === 'buyer_verifying' || (tradeSession && tradeSession.current_step === 'buyer_verifying') || hasPendingVerifyRequest) {
          if (codeSent || verifyRequestStatus === 'submitted' || verifyRequestStatus === 'received') {
            return [verifyNotice, h(TradeActionCard, {iconType:'lock',title:'验证码已发送',keyName:'code-sent',hint:'登录验证码已发送，等待买家验证...'})];
          }
          return [verifyNotice, h(TradeActionCard, {iconType:'lock',title:'买家请求登录验证码',keyName:'send-code',
            hint:'买家正在登录账号，请卖家及时提供本次登录验证码。',
            children: h('input', {className:'card-input',type:'text',placeholder:'请输入登录验证码',value:vcInput,onChange:function(e){setVcInput(e.target.value);}}),
            buttons:[{label:'发送验证码',variant:'primary',onClick:handleSubmitVC}]
          })];
        }
          return [verifyNotice, h(TradeActionCard, {iconType:'key',title:'修改账号资料',keyName:'act-creds-update',hint:'买家验号阶段支持卖家修改并重新提交账号密码资料。',
            children: h('div', null,
              actionError && h('div', {style:{color:'#ff4d4f',fontSize:13,marginBottom:8}}, actionError),
              actionLoading && h('div', {style:{color:'#666',fontSize:13,marginBottom:8}}, '提交中...'),
              h('input', {className:'card-input',type:'text',placeholder:'游戏账号',value:credInput.gameAccount,onChange:function(e){setCredInput(function(p){return Object.assign({},p,{gameAccount:e.target.value});});}}),
              h('div', {style:{position:'relative'}},
                h('input', {className:'card-input',type:showPass?'text':'password',placeholder:'游戏密码',value:credInput.gamePassword,onChange:function(e){setCredInput(function(p){return Object.assign({},p,{gamePassword:e.target.value});});}}),
                h('span', {className:'input-eye',onClick:function(){setShowPass(!showPass);}}, showPass?'🙈':'👁')
              )
            ),
            buttons:[{label:'修改并重新提交',variant:'primary',onClick:handleSubmitCreds}]
          })];
      }
      if (role === 'buyer') {
                var hasCreds = step === 'credentials_submitted' || step === 'buyer_verifying' || step === 'buyer_verified' || step === 'awaiting_rebind' || step === 'credential_rejected';
        var hasAck = (tradeSession && (tradeSession.buyer_recording_ack == 1 || tradeSession.buyer_recording_ack === true)) || step === 'buyer_verifying' || step === 'buyer_verified' || step === 'awaiting_rebind' || step === 'credential_rejected';
        var hasVerified = step === 'buyer_verified' || step === 'awaiting_rebind';
        if (!hasCreds) {
          return [verifyNotice, h(TradeActionCard, {iconType:'clock',title:'等待卖家提交',keyName:'act-wait',hint:'卖家尚未提交账号资料，请耐心等待...'})];
        }
        if (!hasAck) {
          return [verifyNotice, h(TradeActionCard, {iconType:'video',title:'录屏提醒',variant:'warn',keyName:'act-video',
            hint:'请在登录验号前开启全程录屏。若未录屏，后续因装备、物资、账号信息不符产生纠纷时，可能无法作为有效凭证。',
            buttons:[{label: recordingCooldown > 0 ? '请先阅读提示 ' + recordingCooldown + 's' : '我已开启录屏，继续验号', variant:'warning', onClick: recordingCooldown > 0 ? null : handleRecordingAck, disabled: recordingCooldown > 0}]
          })];
        }
        var cards = [verifyNotice];
        // 账号资料卡片（含验证码）
        if (!creds) {
          // Show loading state while auto-fetching credentials
          var vcPh2 = step === "credential_rejected" ? "等待卖家重新提交资料后获取验证码" : "加载账号资料中...";
          cards.push(h("div", {className:"trade-action-card",key:"creds-loading"},
            h("div", {className:"tac-header"},
              h("span", {className:"tac-icon"}, svgIcon(ICON_PATHS.key, 16)),
              h("span", {className:"tac-title"}, "账号资料")
            ),
            h("div", {className:"tr-credential"},
              h("div", {className:"cred-row"},
                h("span",{className:"cred-label"},"账号"),
                h("div", {className:"cred-field-group"},
                  h("input", {className:"cred-readonly-input",readOnly:true,value:"",placeholder: step === "credential_rejected" ? "等待卖家重新输入" : "加载中..."})
                )
              ),
              h("div", {className:"cred-row"},
                h("span",{className:"cred-label"},"密码"),
                h("div", {className:"cred-field-group"},
                  h("input", {className:"cred-readonly-input",readOnly:true,type:"text",value:"",placeholder: step === "credential_rejected" ? "等待卖家重新输入" : "加载中..."})
                )
              ),
              h("div", {className:"cred-row"},
                h("span",{className:"cred-label"},"验证码"),
                h("div", {className:"cred-field-group"},
                  h("input", {className:"cred-readonly-input",readOnly:true,value:"",placeholder:vcPh2})
                )
              )
            ),
            h("div", {className:"cred-actions"},
              h("button",{className:"cred-btn cred-btn-primary cred-btn-block",disabled:true}, "获取验证码")
            ),
            step === "credential_rejected"
              ? h("div", {className:"cred-reject-link disabled"}, "已通知卖家重新提交，请耐心等待")
              : null
          ));
        } else {
          var vcPending = step === "buyer_verifying" || hasPendingVerifyRequest;
          var vcDisplay = verifyCode ? (verifyCode.code || "") : "";
          var vcPh = loginCodeCooldown > 0 ? "等待卖家发送验证码..." : (vcPending ? "等待卖家发送验证码" : "请点击获取验证码"); if (step === "credential_rejected") { vcPh = "等待卖家重新提交资料后获取验证码"; vcBtnLabel = "获取验证码"; }
          var vcBtnLabel = loginCodeCooldown > 0 ? "重新获取 " + loginCodeCooldown + "s" : "获取验证码";
          var vcBtnAction = loginCodeCooldown > 0 ? null : handleRequestVC;
          cards.push(h("div", {className:"trade-action-card",key:"creds-shown"},
            h("div", {className:"tac-header"},
              h("span", {className:"tac-icon"}, svgIcon(ICON_PATHS.key, 16)),
              h("span", {className:"tac-title"}, "账号资料")
            ),
            h("div", {className:"tr-credential"},
              h("div", {className:"cred-row"},
                h("span",{className:"cred-label"},"账号"),
                h("div", {className:"cred-field-group"},
                  h("input", {className:"cred-readonly-input",readOnly:true,value: step === "credential_rejected" ? "等待卖家重新输入" : creds.gameAccount}),
                  h("span", {className:"cred-copy-icon",onClick: step === "credential_rejected" ? null : function(){copyText(creds.gameAccount);},title: step === "credential_rejected" ? "等待卖家重新提交" : "复制账号"},
                    svgIcon(ICON_PATHS.copy, 14))
                )
              ),
              h("div", {className:"cred-row"},
                h("span",{className:"cred-label"},"密码"),
                h("div", {className:"cred-field-group"},
                  h("input", {className:"cred-readonly-input",readOnly:true,type:"text",value: step === "credential_rejected" ? "等待卖家重新输入" : creds.gamePassword}),
                  h("span", {className:"cred-copy-icon",onClick: step === "credential_rejected" ? null : function(){copyText(creds.gamePassword);},title: step === "credential_rejected" ? "等待卖家重新提交" : "复制密码"},
                    svgIcon(ICON_PATHS.copy, 14))
                )
              ),
              h("div", {className:"cred-row"},
                h("span",{className:"cred-label"},"验证码"),
                h("div", {className:"cred-field-group"},
                  h("input", {className:"cred-readonly-input",readOnly:true,value:vcDisplay,placeholder:vcPh}),
                  h("span", {className:"cred-copy-icon" + (verifyCode && verifyCode.code ? "" : " disabled"),
                    onClick: verifyCode && verifyCode.code ? function(){copyText(verifyCode.code);} : null,
                    title: verifyCode && verifyCode.code ? "复制验证码" : "暂无可复制"},
                    svgIcon(ICON_PATHS.copy, 14))
                )
              )
            ),
            h("div", {className:"cred-actions"},
              h("button",{className:"cred-btn cred-btn-primary cred-btn-block",onClick:vcBtnAction,disabled: loginCodeCooldown > 0 || actionLoading || step === "credential_rejected"}, vcBtnLabel)
            ),
            step === "credential_rejected"
              ? h("div", {className:"cred-reject-link disabled"}, "已通知卖家重新提交，请耐心等待")
              : h("div", {className:"cred-reject-link",onClick:function(){setShowCredReject(true);}}, "账号密码有误，重新申请")
          ));
        }
        // 验号通过
        if (!hasVerified && step !== 'inspection_dispute' && step !== 'cs_pending') {
          cards.push(h(TradeActionCard, {iconType:'check',title:'验号通过',keyName:'verify-ok',
            buttons:[{label:'验号通过',variant:'success',onClick:function(){setShowVerifyConfirm(true);}}]
          }));
        }
        // 申请售后
        if (!hasVerified && step !== 'inspection_dispute' && step !== 'cs_pending') {
          cards.push(h(TradeActionCard, {iconType:'alert',title:'发现问题？',variant:'danger',keyName:'dispute-btn',
            hint:'如果发现装备、物资、账号信息与描述不符，可在此阶段申请售后。验号通过后将不能再因装备/物资不符申请售后。',
            buttons:[{label:'申请售后',variant:'danger',onClick:function(){setDisputeForm({disputeType:'',description:'',images:[]});setDisputeImages([]);setActionError('');setShowDispute(true);},disabled:step==='buyer_verified'}]
          }));
        }
        return cards;
      }
      return null;
    }

    // 状态90: 售后/纠纷处理中
    if (status === 90) {
      if (step === 'cs_pending') {
        if (role === 'seller') {
          return h(TradeActionCard, {iconType:'alert',title:'已申请客服介入',variant:'warn',keyName:'cs-wait',
            hint:'已申请客服介入，平台客服将根据交易记录、录屏证据及双方说明进行处理。'
          });
        }
        return h(TradeActionCard, {iconType:'alert',title:'客服介入中',variant:'warn',keyName:'cs-pending-buyer',
          hint:'卖家已申请客服介入，平台客服将根据交易记录、录屏证据及双方说明进行处理。'
        });
      }
      if (role === 'seller') {
        return h(TradeActionCard, {iconType:'alert',title:'买家提交售后申请',keyName:'seller-dispute',
          children: h('div', {style:{whiteSpace:'pre-wrap',lineHeight:'1.7',fontSize:13,color:'#555'}},
            '买家反馈验号异常，订单已暂停推进。' + '\n\n' +
            '请卖家立即录屏登录账号，检查账号装备、资源、货币、贵重物品等是否存在损失。' + '\n' +
            '若确认无损失，可同意取消订单。' + '\n' +
            '若发现损失，请申请客服介入处理。'
          ),
          buttons:[
            {label:'同意取消订单',variant:'danger',onClick:function(){
              if (confirm('确认同意取消订单？\n\n请确认已录屏登录账号并核对资产无损失。确认后订单将关闭。')) {
                doAction(function() { return NK.Api.Trades.agreeCancel(orderId); });
              }
            }},
            {label:'申请客服介入',variant:'default',onClick:function(){
              if (confirm('确认申请客服介入？\n\n请确认账号可能存在资产损失或双方无法协商处理。申请后平台客服将介入处理。')) {
                doAction(function() { return NK.Api.Trades.requestService(orderId); });
              }
            }}
          ]
        });
      }
      if (role === 'buyer') {
        return h(TradeActionCard, {iconType:'alert',title:'售后已提交',variant:'warn',keyName:'buyer-dispute-done',
          hint:'已提交售后申请，请等待卖家处理。'
        });
      }
      return null;
    }

    // 状态40: 手动换绑阶段
    if (status === 40) {
      if (role === 'seller') {
        var hasPendingRebindRequest = (step === 'awaiting_rebind_code' || step === 'rebind_code_submitted' || (rebindState && rebindState.hasPendingRebindRequest));
        var spRejected = step === 'sp_rejected';
        return [
          // 二级密码提交卡片
          h(TradeActionCard, {iconType:'key',title: spRejected ? '买家请求重新发送二级密码' : '提交二级密码',keyName:'sp-card',
            hint: spRejected ? '买家反馈二级密码错误，请核对后重新提交。' : '请提交账号二级密码。如果该账号没有二级密码，请勾选下方选项。',
            children: h('div', null,
              h('input', {className:'card-input',type:'text',placeholder:'二级密码（如无请勾选下方）',value:rbForm.secondaryPassword,
                disabled: rbForm.noSp,
                onChange:function(e){setRbForm(function(p){return Object.assign({},p,{secondaryPassword:e.target.value});});}}),
              h('label', {style:{display:'flex',alignItems:'center',marginTop:8,fontSize:13,color:'#666',cursor:'pointer'}},
                h('input', {type:'checkbox',checked:rbForm.noSp,
                  onChange:function(e){setRbForm(function(p){return Object.assign({},p,{noSp:e.target.checked,secondaryPassword: e.target.checked ? '' : p.secondaryPassword});});},
                  style:{marginRight:6}}),
                '该账号无二级密码'
              )
            ),
            buttons:[
              {label: spRejected ? '重新提交二级密码' : '提交二级密码', variant:'primary', onClick:function(){
                if (!rbForm.noSp && !rbForm.secondaryPassword.trim()) { setActionError('请输入二级密码或勾选"该账号无二级密码"'); return; }
                if (confirm('确认提交二级密码？\n\n请确认二级密码准确无误。\n如果该账号没有二级密码，请确认已选择"该账号无二级密码"。')) {
                  doAction(function() { return NK.Api.Trades.submitSecondaryPassword(orderId, {secondaryPassword: rbForm.secondaryPassword, noSecondaryPassword: rbForm.noSp}); });
                }
              }}
            ]
          }),
          // 换绑验证码提交卡片（仅在有pending请求时显示）
          // 换绑验证码：已提交则显示状态卡片，否则显示输入框
          hasPendingRebindRequest ? (
            step === 'rebind_code_submitted'
              ? h(TradeActionCard, {iconType:'lock',title:'换绑验证码已发送',keyName:'rb-code-sent',
                  hint:'换绑验证码已发送，等待买家完成换绑...'})
              : h(TradeActionCard, {iconType:'lock',title:'提交换绑验证码',keyName:'rb-code-card',
                  hint:'买家已申请获取换绑验证码，请及时查看账号绑定渠道并提交。',
                  children: h('input', {className:'card-input',type:'text',placeholder:'请输入换绑验证码',value:vcInput,
                    onChange:function(e){setVcInput(e.target.value);}}),
                  buttons:[{label:'提交换绑验证码',variant:'primary',onClick:function(){
                    if (!vcInput.trim()) { setActionError('请输入换绑验证码'); return; }
                    doAction(function() { return NK.Api.Trades.submitRebindCode(orderId, {code: vcInput.trim()}); });
                  }}]
                })
          ) : null
        ];
      }
      if (role === 'buyer') {
        var spRejected = step === 'sp_rejected';
        return h('div', null,
          // 二级密码卡片
          h('div', {className:'trade-action-card',key:'sp-buyer'},
            h('div', {className:'tac-header'},
              h('span', {className:'tac-icon'}, svgIcon(ICON_PATHS.key, 16)),
              h('span', {className:'tac-title'}, '二级密码')
            ),
            h('div', {className:'tr-credential'},
              h('div', {className:'cred-row'},
                h('div', {className:'cred-field-group'},
                  spRejected
                    ? h('input', {className:'cred-readonly-input',readOnly:true,value:'',placeholder:'已申请卖家重新发送二级密码，请等待卖家处理。'})
                    : spData
                      ? (spData.noSecondaryPassword
                        ? h('div', {style:{padding:'8px 0',fontSize:14,color:'#555'}}, '卖家确认该账号无二级密码')
                        : h('div', {style:{display:'flex',alignItems:'center'}},
                            h('input', {className:'cred-readonly-input',readOnly:true,type:'text',value:spData.secondaryPassword||'',style:{flex:1}}),
                            h('span', {className:'cred-copy-icon',onClick:function(){copyText(spData.secondaryPassword);},title:'复制二级密码'}, svgIcon(ICON_PATHS.copy, 14))
                          )
                      )
                      : h('input', {className:'cred-readonly-input',readOnly:true,value:'',placeholder:'等待卖家发送二级密码'})
                )
              )
            ),
            h('div', {className:'cred-actions'},
              spRejected
                ? h('div', {className:'cred-reject-link disabled'}, '已申请卖家重新发送二级密码，请等待卖家处理。')
                : spData
                  ? h('div', {className:'cred-reject-link',onClick:function(){
                      if (confirm('确认申请重新发送二级密码？\n\n提交后将通知卖家重新核对并提交二级密码。')) {
                        doAction(function() { return NK.Api.Trades.requestSecondaryPassword(orderId); });
                      }
                    }}, '二级密码错误，重新申请')
                  : null
            )
          ),
          // 换绑验证码卡片
          h('div', {className:'trade-action-card',key:'rb-code-buyer'},
            h('div', {className:'tac-header'},
              h('span', {className:'tac-icon'}, svgIcon(ICON_PATHS.lock, 16)),
              h('span', {className:'tac-title'}, '换绑验证码')
            ),
            h('div', {className:'tr-credential'},
              h('div', {className:'cred-row'},
                h('div', {className:'cred-field-group'},
                  rebindCode
                    ? h('div', {style:{display:'flex',alignItems:'center'}},
                        h('input', {className:'cred-readonly-input',readOnly:true,value:rebindCode.code||'',style:{flex:1}}),
                        h('span', {className:'cred-copy-icon',onClick:function(){copyText(rebindCode.code);},title:'复制验证码'}, svgIcon(ICON_PATHS.copy, 14))
                      )
                    : h('input', {className:'cred-readonly-input',readOnly:true,value:'',placeholder: vcCooldown > 0 ? '等待卖家发送验证码...' : '请点击获取换绑验证码'})
                )
              )
            ),
            h('div', {className:'cred-actions'},
              actionError && h('div', {style:{color:'#ff4d4f',fontSize:12,marginBottom:8}}, actionError),
              h('button',{className:'cred-btn cred-btn-primary cred-btn-block',
                onClick: vcCooldown > 0 ? null : function(){ setActionLoading(true); setActionError(''); NK.Api.Trades.requestRebindCode(orderId).then(function(r){ setActionLoading(false); if(r.code===0){ var cd = (r.data && r.data.cooldownSeconds) || 60; setVcCooldown(cd); fetchAll(); } else { var remain = (r.data && r.data.remainingSeconds) || 0; if (remain > 0) { setVcCooldown(remain); } else { setActionError(r.message||'请求失败'); } } }).catch(function(){ setActionLoading(false); setActionError('网络错误'); }); },
                disabled: vcCooldown > 0 || actionLoading},
                vcCooldown > 0 ? '重新获取 ' + vcCooldown + 's' : '获取换绑验证码'
              )
            )
          ),
          // 确认交易成功
          h(TradeActionCard, {iconType:'done',title:'确认交易成功',keyName:'trade-ok',
            hint:'请确认账号绑定信息已完成更换、二级密码正确、账号可正常登录后，点击确认。',
            buttons:[{label:'确认交易成功',variant:'success',onClick:function(){setShowTradeSuccess(true);}}]
          }),
          // 反馈问题
          h(TradeActionCard, {iconType:'alert',title:'换绑遇到问题？',variant:'danger',keyName:'rb-issue',
            hint:'如果在换绑过程中遇到问题，可以在此反馈。',
            buttons:[{label:'反馈问题',variant:'danger',onClick:function(){}}]
          })
        );
      }
      return null;
    }

    // 状态50: 换绑中（兼容旧流程）
    if (status === 50) {
      if (role === 'seller') {
        return h(TradeActionCard, {iconType:'lock',title:'提交换绑验证码',keyName:'rb-submit',
          children: h('input', {className:'card-input',type:'text',placeholder:'请输入换绑验证码',value:vcInput,onChange:function(e){setVcInput(e.target.value);}}),
          buttons:[{label:'提交验证码',variant:'primary',onClick:function(){
            if (!vcInput.trim()) { setActionError('请输入换绑验证码'); return; }
            doAction(function() { return NK.Api.Trades.submitRebindCode(orderId, {code: vcInput.trim()}); });
          }}]
        });
      }
      if (role === 'buyer') {
        if (!rebindCode) {
          return h(TradeActionCard, {iconType:'clock',title:'等待卖家提交换绑验证码',keyName:'rb-wait',
            hint:'卖家正在提交换绑验证码，请稍候...',
            buttons:[{label:'获取换绑验证码',variant:'primary',onClick:function(){ doAction(function() { return NK.Api.Trades.rebindCode(orderId); }); }}]
          });
        }
        return h('div', null,
          h('div', {className:'trade-action-card',key:'rb-shown'},
            h('div', {className:'tac-header'},
              h('span', {className:'tac-icon'}, svgIcon(ICON_PATHS.rebind, 16)),
              h('span', {className:'tac-title'}, '换绑验证码')
            ),
            h('div', {className:'tr-credential'},
              h('div', {className:'cred-row'},
                h('span',{className:'cred-label'},'验证码'),
                h('div', {className:'cred-field-group'},
                  h('input', {className:'cred-readonly-input',readOnly:true,value:rebindCode.code||''}),
                  h('span', {className:'cred-copy-icon',onClick:function(){copyText(rebindCode.code);},title:'复制'}, svgIcon(ICON_PATHS.copy, 14))
                )
              )
            )
          ),
          h(TradeActionCard, {iconType:'done',title:'确认交易成功',keyName:'trade-ok',
            buttons:[{label:'确认交易成功',variant:'success',onClick:function(){setShowTradeSuccess(true);}}]
          })
        );
      }
      return null;
    }
    // 状态80: 已完成
    if (status === 80) {
      return h(TradeActionCard, {iconType:'done',title:'交易已完成',keyName:'done',
        hint:'交易已成功完成。如有任何问题请联系客服。'
      });
    }

    // 状态110/120
    if (status === 110 || status === 120) {
      return h(TradeActionCard, {iconType:'info',title:'交易已关闭',keyName:'closed',hint:'该订单已关闭。'});
    }

    return null;
  };
  // ==================== 页面渲染 ====================
  if (loading) {
    return h('div', {className:'detail-page'},
      h('div', {className:'back-header'},
        h('div', {className:'bbtn',onClick:function(){NK.Store.back('messages');}}, svgArrL()),
        h('div', {className:'btitle'}, '交易房间')
      ),
      h('div', {className:'page-loading'},
        h('div', {className:'spinner'}),
        h('div', {style:{color:'#999',fontSize:14}},'加载中...')
      )
    );
  }

  if (error) {
    return h('div', {className:'detail-page'},
      h('div', {className:'back-header'},
        h('div', {className:'bbtn',onClick:function(){NK.Store.back('messages');}}, svgArrL()),
        h('div', {className:'btitle'}, '交易房间')
      ),
      h('div', {className:'empty-state'},
        h('div', {className:'eicon'}, '😥'),
        h('div', null, error),
        h('div', {style:{color:'#1677ff',marginTop:12,cursor:'pointer',fontSize:14},onClick:function(){NK.Store.back('messages');}}, '返回首页')
      )
    );
  }

  var statusText = STATUS_MAP[status] || ('状态'+status);
  var roleText = role==='buyer'?'买家':role==='seller'?'卖家':'';
  var sc = status;
  var stepLabel = STEP_LABELS[step] || '';


  // 取消交易确认弹窗
  if (showCancelConfirm) {
    return h('div', {className:'modal-overlay',onClick:function(e){if(e.target.className==='modal-overlay')setShowCancelConfirm(false);}},
      h('div', {className:'modal-card',style:{maxWidth:340}},
        h('div', {style:{fontSize:16,fontWeight:700,marginBottom:12}}, '确认取消本次交易？'),
        h('div', {style:{fontSize:13,color:'#666',marginBottom:16,lineHeight:'1.6'}}, '取消后订单将关闭，是否继续？'),
        h('div', {style:{display:'flex',gap:10}},
          h('button', {className:'btn-cancel',onClick:function(){setShowCancelConfirm(false);},style:{flex:1,padding:'10px 0',borderRadius:8,border:'1px solid #d9d9d9',background:'#fff',fontSize:14,cursor:'pointer'}}, '再想想'),
          h('button', {onClick:doCancel,disabled:cancelLoading,style:{flex:1,padding:'10px 0',borderRadius:8,border:'none',background:'#ff4d4f',color:'#fff',fontSize:14,cursor:cancelLoading?'not-allowed':'pointer'}}, cancelLoading ? '取消中...' : '确认取消')
        )
      )
    );
  }

  return h('div', {className:'detail-page trade-room-v3'},
    // ---- ?????? ----
    h('div', {className:'tr3-top-fixed'},
      // ---- ??? ----
      h('div', {className:'tr3-header'},
        h('div', {className:'tr3-header-top'},
          h('div', {className:'bbtn',onClick:function(){NK.Store.back('messages');}}, svgArrL()),
          h('div', {className:'tr3-title'}, '交易房间'),
          h('span', {className:'tr3-badge'+(sc>=80?' done':sc>=90?' alert':'')}, statusText)
        ),
        h('div', {className:'tr3-header-sub'},
          h('span', {className:'tr3-role'}, roleText),
          h('span', {className:'tr3-order-no'}, '订单 #'+(orderId||'')),
          h("span", {style:{fontSize:9,color:"#999",marginLeft:8}}, "v:2026-05-29-notice-v3"),
          stepLabel && h('span', {className:'tr3-step-tag'},
            h('span', {className:'tr3-step-dot'}),
            stepLabel
          )
        )
      ),

      // ---- 交易进度条 ----
      h(TradeProgressSteps, {currentStep:getProgressStep(status)}),
    ),

    // ---- ?????? ----
    h('div', {className:'tr3-scroll-area'},
    // ---- 消息列表 ----
    (function() {
      var filteredMsgs = messages.filter(function(msg) { return msg.msg_type !== 'trade_created_detail' && msg.msg_type !== 'trade_notice'; });
      if (filteredMsgs.length === 0) return null;
      return h('div', {className:'tr3-messages'},
        filteredMsgs.length > 0
          ? filteredMsgs.map(function(msg, i) {
              var content = parseMsgContent(msg);
              var text = content.text || '';
              // Filter placeholder English messages from garbled backend strings
              if (isSystemMsg(msg) && (text === "System message" || text === "OK" || text === "Error" || text === "Not found" || text === "Invalid" || text === "Permission denied" || text === "Notice placeholder" || text === "Trade created" || text === "Trade notice" || text === "Dispute filed" || (text && text.indexOf("Cooldown:") === 0))) {
                return null;
              }
              if (isSystemMsg(msg)) {
                var title = content.title || '';
                return h(SystemMessageCard, {
                  key: msg.id || i,
                  iconType: getMsgIconType(text),
                  title: title,
                  text: text,
                  time: msg.create_time
                });
              }
              return h(SystemMessageCard, {
                key: msg.id || i,
                text: text,
                time: msg.create_time
              });
            })
          : h('div', {className:'tr3-empty'},
              h('div', {style:{fontSize:36,marginBottom:8}}, '💬'),
              h('div', {style:{fontSize:13,color:'#bbb'}}, '暂无消息')
            )
      );
    })(),


    // ---- 操作区 ----
    h('div', {className:'tr3-action-area'}, renderActionCards()),

    // ---- 底部安全提示 ----
    h('div', {className:'tr3-safe-note'},
      svgIcon('M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z', 12),
      ' 交易全程由平台监控，敏感数据加密传输。请勿跳出平台私下交易。'
    ),
    ),



    // ========== 弹层 ==========
    showVerifyConfirm && h('div', {className:'modal-overlay',onClick:function(){setShowVerifyConfirm(false);}},
      h('div', {className:'modal-box',onClick:function(e){e.stopPropagation();}},
        h('div', {className:'modal-header'},
          h('div', {className:'modal-title'}, '确认验号通过')
        ),
        h('div', {className:'modal-body'},
          h('div', {className:'confirm-text'},
            '确认账号物资、装备、资源与描述一致？确认后将进入换绑阶段，后续原则上不能再因装备或物资不符申请售后。'
          ),
          actionError && h('div', {style:{color:'#ff4d4f',fontSize:13,marginTop:8,textAlign:'center'}}, actionError)
        ),
        h('div', {className:'modal-footer'},
          h('button',{className:'btn-cancel',onClick:function(){setShowVerifyConfirm(false);}},'取消'),
          h('button',{className:'btn-confirm',onClick:handleVerifyConfirm,disabled:actionLoading},
            actionLoading?'处理中...':'确认通过')
        )
      )
    ),

    showTradeSuccess && h('div', {className:'modal-overlay',onClick:function(){setShowTradeSuccess(false);}},
      h('div', {className:'modal-box',onClick:function(e){e.stopPropagation();},style:{maxWidth:420}},
        h('div', {className:'modal-header'},
          h('div', {className:'modal-title'}, '确认交易成功？')
        ),
        h('div', {className:'modal-body'},
          h('div', {className:'confirm-text',style:{fontSize:13,lineHeight:'1.8',color:'#555'}},
            '请确认你已完成以下检查：' + '\n\n' +
            '1. 已成功更换账号绑定信息。' + '\n' +
            '2. 二级密码正确。' + '\n' +
            '3. 账号可以正常再次登录。' + '\n' +
            '4. 卖家已无法通过原绑定信息找回账号。' + '\n' +
            '5. 账号内容与验号时保持一致。' + '\n\n' +
            '确认后订单将进入交易成功阶段。'
          ),
          actionError && h('div', {style:{color:'#ff4d4f',fontSize:13,marginTop:8,textAlign:'center'}}, actionError)
        ),
        h('div', {className:'modal-footer'},
          h('button',{className:'btn-cancel',onClick:function(){setShowTradeSuccess(false);}},'取消'),
          h('button',{className:'btn-confirm',onClick:handleRebindConfirm,disabled:actionLoading},
            actionLoading?'处理中...':'我已确认，交易成功')
        )
      )
    ),

    showDispute && h('div', {className:'modal-overlay',onClick:function(){setShowDispute(false);}},
      h('div', {className:'modal-box',onClick:function(e){e.stopPropagation();},style:{maxHeight:'85vh',overflowY:'auto'}},
        h('div', {className:'modal-header'},
          h('div', {className:'modal-title'}, '申请售后')
        ),
        h('div', {className:'modal-body dispute-form'},
          h('div', {className:'df-row'},
            h('div', {className:'df-label'}, '纠纷类型'),
            h('div', {className:'df-radio-group'},
              DISPUTE_TYPES.map(function(dt){
                return h('label', {key:dt.v,className:'df-radio-label',style:{display:'flex',alignItems:'center',marginBottom:10,cursor:'pointer'}},
                  h('input', {type:'radio',name:'disputeType',value:dt.v,checked:disputeForm.disputeType===dt.v,
                    onChange:function(e){setDisputeForm(function(p){return Object.assign({},p,{disputeType:e.target.value});});},
                    style:{marginRight:8}}),
                  dt.l
                );
              })
            )
          ),
          h('div', {className:'df-row'},
            h('div', {className:'df-label'}, '问题描述'),
            h('textarea', {className:'df-textarea',placeholder:'请详细说明账号存在的问题，例如无法登录、装备/资源不符、账号内容与描述不一致等。',rows:4,maxLength:500,
              value:disputeForm.description,
              onChange:function(e){var v=e.target.value;if(v.length<=500)setDisputeForm(function(p){return Object.assign({},p,{description:v});});}}),
            h('div', {style:{fontSize:11,color:'#999',textAlign:'right',marginTop:4}}, (disputeForm.description||'').length + '/500')
          ),
          h('div', {className:'df-row'},
            h('div', {className:'df-label'}, '截图补充'),
            h('div', {style:{fontSize:12,color:'#999',marginBottom:8}},'推荐上传（最多6张）'),
            disputeImages.length > 0 && h('div', {className:'df-image-previews'},
              disputeImages.map(function(img,idx){
                return h('div', {key:idx,className:'df-image-preview-item'},
                  h('img', {src:URL.createObjectURL(img),style:{width:80,height:80,objectFit:'cover',borderRadius:4}}),
                  h('span', {className:'df-image-remove',onClick:function(e){e.stopPropagation();setDisputeImages(function(p){var n=p.slice();n.splice(idx,1);return n;});},
                    style:{position:'absolute',top:-6,right:-6,width:20,height:20,borderRadius:'50%',background:'#ff4d4f',color:'#fff',fontSize:12,display:'flex',alignItems:'center',justifyContent:'center',cursor:'pointer'}}, '\u00D7')
                );
              })
            ),
            disputeImages.length < 6 && h('label', {className:'df-upload-btn',style:{display:'inline-flex',alignItems:'center',gap:6,padding:'8px 16px',border:'1px dashed #d9d9d9',borderRadius:6,cursor:'pointer',color:'#666',fontSize:13}},
              h('input', {type:'file',accept:'image/*',style:{display:'none'},
                onChange:function(e){var files=e.target.files;if(files.length>0){var remain=6-disputeImages.length;var add=Math.min(files.length,remain);var newImgs=[];for(var i=0;i<add;i++)newImgs.push(files[i]);setDisputeImages(function(p){return p.concat(newImgs);});}e.target.value='';}}),
              '\u{1F4F7} 选择图片'
            )
          ),
          actionError && h('div', {style:{color:'#ff4d4f',fontSize:13,marginTop:8,textAlign:'center'}}, actionError)
        ),
        h('div', {className:'modal-footer'},
          h('button',{className:'btn-cancel',onClick:function(){setShowDispute(false);}},'取消'),
          h('button',{className:'btn-danger',onClick:handleDispute,disabled:actionLoading || disputeUploading},
            (actionLoading || disputeUploading)?'处理中...':'提交申请')
        )
      )
    ),

    showCredConfirm && h('div', {className:'modal-overlay',onClick:function(){setShowCredConfirm(false);setActionError('');}},
      h('div', {className:'modal-box',onClick:function(e){e.stopPropagation();}},
        h('div', {className:'modal-header'},
          h('div', {className:'modal-title'}, '确认提交账号资料？')
        ),
        h('div', {className:'modal-body'},
          h('div', {className:'confirm-text'},
            '请确认账号和密码填写无误。提交后买家将进入验号流程。若账号资料有误，你仍可在当前阶段重新修改并提交。'
          ),
          h('div', {style:{background:'#f8f9fa',borderRadius:8,padding:12,fontSize:13,color:'#333',marginTop:12}},
            h('div', null, '账号：', credInput.gameAccount || '（未填写）'),
            h('div', {style:{marginTop:6}}, '密码：', credInput.gamePassword ? '******' : '（未填写）')
          ),
          actionError && h('div', {style:{color:'#ff4d4f',fontSize:13,marginTop:8,textAlign:'center'}}, actionError)
        ),
        h('div', {className:'modal-footer'},
          h('button',{className:'btn-cancel',onClick:function(){setShowCredConfirm(false);setActionError('');},disabled:actionLoading}, '取消'),
          h('button',{className:'btn-confirm',onClick:doSubmitCreds,disabled:actionLoading},
            actionLoading ? '提交中...' : '确认提交')
        )
      )
    ),

    showCredReject && h("div", {className:"modal-overlay",onClick:function(){setShowCredReject(false);}},
      h("div", {className:"modal-box",onClick:function(e){e.stopPropagation();}},
        h("div", {className:"modal-header"},
          h("div", {className:"modal-title"}, "确认重新申请账号资料？")
        ),
        h("div", {className:"modal-body"},
          h("div", {className:"confirm-text"},
            "提交后将通知卖家重新提供账号密码资料，请确认当前账号或密码确实无法正常登录。"
          ),
          actionError && h("div", {style:{color:"#ff4d4f",fontSize:13,marginTop:8,textAlign:"center"}}, actionError)
        ),
        h("div", {className:"modal-footer"},
          h("button",{className:"btn-cancel",onClick:function(){setShowCredReject(false);}},"取消"),
          h("button",{className:"btn-confirm",onClick:function(){setShowCredReject(false);handleCredReject();},disabled:actionLoading},
            actionLoading?"处理中...":"确认重新申请")
        )
      )
    )
  );
}

window.NK = window.NK || {};
NK.TradeRoomPage = TradeRoomPage;
