<template>
  <view class="container" :class="{ 'screen-shake': screenShaking }">
    <!-- 背景 -->
    <view class="bg-dark-grid"></view>

    <!-- 连接中状态 -->
    <view v-if="!isConnected" class="loading-screen">
      <view class="loading-content">
        <view class="loading-spinner">📡</view>
        <text class="loading-text">正在寻找基站...</text>
      </view>
    </view>

    <!-- 匹配大厅 -->
    <view v-else-if="!gameState" class="lobby-screen">
      <view class="bg-decoration"></view>
      
      <view class="lobby-content fadeInUp">
        <text class="game-title">动物扫雷大作战</text>
        
        <view v-if="isMatching" class="match-box glass-card">
          <view class="scanner-box pulse">
            <view class="scanner-line"></view>
            <text class="match-spinner">🦁</text>
          </view>
          <text class="match-status">正在匹配全球玩家...</text>
          
          <view class="match-info">
            <view class="timer-row">
              <text class="timer-label">等待时长</text>
              <text class="timer-value">{{ matchingTimer }}s</text>
            </view>
            
            <view class="status-tip">
              <text v-if="matchingTimer <= 30" class="tip-normal">⚡ 正在接入星际网道</text>
              <text v-else-if="matchingTimer <= 60" class="tip-warning">⏳ 搜索频率增强中...</text>
              <text v-else class="tip-error">⚠️ 信号微弱，请重试</text>
            </view>
            
            <view class="btn-secondary cancel-btn" @tap="cancelMatchmaking">
              取消匹配
            </view>
          </view>
        </view>

        <view v-else class="start-box">
          <view class="game-intro-card glass-card">
            <view class="intro-item">
              <text class="intro-icon">🎮</text>
              <text class="intro-text">{{ matchPlayerCount }}人经典竞技模式</text>
            </view>
            <view class="intro-item">
              <text class="intro-icon">🏆</text>
              <text class="intro-text">胜者赢取全额奖励</text>
            </view>
            <view class="intro-item online-count-row">
              <text class="intro-icon">👥</text>
              <text class="intro-text">当前在线: <text class="online-number">{{ onlineCount }}</text> 人</text>
            </view>
          </view>
          
          <view class="btn-primary start-btn" @tap="startMatchmaking">
            <text class="btn-text">🚀 开始匹配</text>
          </view>
        </view>

        <!-- 游戏日志 -->
        <view class="log-section glass-card">
          <view class="log-header">
            <text class="log-title">通讯终端 (LIVE)</text>
            <view class="online-indicator"></view>
          </view>
          <scroll-view scroll-y class="mini-logs" :scroll-top="logsScrollTop">
            <view v-for="log in logs" :key="log.id" class="log-item" :class="'log-' + log.type">
              <text class="log-time">[{{ log.time }}]</text>
              <text class="log-content">{{ log.content }}</text>
            </view>
            <view v-if="logs.length === 0" class="log-empty">等待操作指令...</view>
          </scroll-view>
        </view>
      </view>
    </view>

    <!-- 游戏主界面 -->
    <view v-else class="game-screen" :class="{ 'spectator-mode': isSpectator }">
      <!-- 顶部状态栏 -->
      <view class="game-header">
        <view class="round-badge">Round {{ gameState.globalTurnCount || gameState.round || 0 }}</view>
        <text class="header-title">动物扫雷</text>
        <view class="header-actions">
          <view class="btn-icon" @tap="showGuide = true">📜</view>
          <view class="btn-icon" :class="{ active: debugMode }" @tap="debugMode = !debugMode">🐛</view>
        </view>
      </view>

      <view class="game-layout">
        <!-- 对手列表 (横向滚动) -->
        <scroll-view scroll-x class="opponents-bar">
          <view class="opponents-list">
            <view 
              v-for="p in opponents" 
              :key="p.userId" 
              class="player-card opponent"
              :class="{ 
                'active-turn': gameState.gameStarted && gameState.turnOrder[gameState.currentTurnIndex] === p.userId,
                'damaged': damagedPlayers.includes(p.userId),
                'healed': healedPlayers.includes(p.userId)
              }"
            >
              <text class="avatar">{{ getPlayerAvatar(p) }}</text>
              <view class="player-info">
                <text class="username">{{ p.username || '对手' }}</text>
                <view class="hp-bar">
                  <text 
                    v-for="(n, i) in p.maxHp" 
                    :key="i" 
                    class="heart"
                    :class="{ filled: i < p.hp }"
                  >{{ i < p.hp ? '❤️' : '🤍' }}</text>
                </view>
                <view class="status-icons">
                  <text v-if="p.shield" class="icon">🛡️</text>
                  <text v-if="p.poisoned" class="icon">☠️</text>
                  <text v-if="p.curse" class="icon">👻</text>
                  <text v-if="p.revive" class="icon">💖</text>
                  <text v-if="p.timeBombTurns > 0" class="icon pulse">⏰{{p.timeBombTurns}}</text>
                  <text v-if="p.skipTurn" class="icon">⏭️</text>
                </view>
              </view>
              
              <!-- 玩家身上的飘字 -->
              <view v-for="l in getPlayerLabels(p.userId)" :key="l.id" class="float-label" :class="'text-' + l.type" style="top: 20%; left: 50%; transform: translateX(-50%);">
                {{ l.text }}
              </view>
            </view>
          </view>
        </scroll-view>

        <!-- 棋盘区 -->
        <view class="board-area">
          <view class="turn-indicator" v-if="gameState.gameStarted">
            <view class="turn-badge" :class="{ 'my-turn pulse': isMyTurn }">
              {{ isMyTurn ? '您的回合' : '等待对手...' }}
            </view>
            <view class="timer-badge" :class="{ urgent: turnTimer < 5 }">
              ⏱️ {{ turnTimer }}s
            </view>
            <view v-if="isSpectator" class="spectator-badge">围观中</view>
          </view>

          <view class="timer-progress-bg" v-if="gameState.gameStarted">
            <view 
              class="timer-progress-fill" 
              :style="{ width: (turnTimer / 15 * 100) + '%' }"
              :class="turnTimer < 5 ? 'bg-red' : (turnTimer < 10 ? 'bg-yellow' : 'bg-green')"
            ></view>
          </view>

          <view class="grid-board" :style="{ gridTemplateColumns: 'repeat(' + gameState.gridSize + ', 1fr)' }">
            <view
              v-for="(cell, i) in gameState.grid"
              :key="i"
              class="grid-cell"
              :class="[
                cell.revealed ? (cell.type === 'bomb' ? 'type-bomb explosion' : (cell.type === 'item' ? 'type-item item-' + (cell.itemId || 'generic') : 'type-empty')) : 'bg-slate-800',
                {
                  'revealed': cell.revealed,
                  'has-magnifier': myPlayer && myPlayer.revealedCells && myPlayer.revealedCells[i],
                  'has-mark': isSpectator && spectatorMarks[i] // 观察者画板标记
                }
              ]"
              @tap="handleCellClick(i)"
            >
              <view v-if="cell.revealed">
                <text v-if="cell.type === 'bomb'" class="cell-icon">💣</text>
                <text v-else-if="cell.type === 'empty'" class="cell-num" :style="{ color: getNumColor(cell.neighborBombs) }">
                  {{ cell.neighborBombs > 0 ? cell.neighborBombs : '' }}
                </text>
                <text v-else-if="cell.type === 'item'" class="cell-icon">{{ getItemIcon(cell.itemId) }}</text>
              </view>
              <view v-else-if="myPlayer && myPlayer.revealedCells && myPlayer.revealedCells[i]"
                class="magnifier-mark"
                :class="{
                  'safe-zone': myPlayer.revealedCells[i] === 'empty',
                  'bomb-zone': myPlayer.revealedCells[i] === 'bomb'
                }"
              >
                 <text class="cell-icon magnifier-content">{{ getContentIcon(myPlayer.revealedCells[i]) }}</text>
                 <text class="magnifier-badge">🔍</text>
              </view>

              <!-- 观察者画板标记 -->
              <view v-else-if="isSpectator && spectatorMarks[i]" class="spectator-mark">
                <text v-if="spectatorMarks[i] === 'flag'" class="mark-icon flag">🚩</text>
                <text v-else-if="spectatorMarks[i] === 'check'" class="mark-icon check">✅</text>
              </view>

              <!-- 格子上的飘字 -->
              <view v-for="l in getCellLabels(i)" :key="l.id" class="float-label" :class="'text-' + l.type">
                {{ l.text }}
              </view>
            </view>
          </view>
        </view>

        <!-- 底部面板 -->
        <view class="bottom-panel">
          <!-- 观察者工具栏 -->
          <view v-if="isSpectator" class="spectator-toolbar">
            <view
              v-for="tool in [
                { id: 'flag', icon: '🚩', name: '旗帜' },
                { id: 'check', icon: '✅', name: '勾选' },
                { id: 'eraser', icon: '🧹', name: '橡皮擦' }
              ]"
              :key="tool.id"
              class="tool-btn"
              :class="{ active: spectatorTool === tool.id }"
              @tap="selectTool(tool.id)"
            >
              <text class="tool-icon">{{ tool.icon }}</text>
              <text class="tool-name">{{ tool.name }}</text>
            </view>
          </view>

          <view v-if="myPlayer && !isSpectator" class="player-card me" 
            :class="{ 
              'active-turn': isMyTurn,
              'damaged': damagedPlayers.includes(myPlayer.userId),
              'healed': healedPlayers.includes(myPlayer.userId)
            }"
          >
            <text class="avatar lg">{{ getPlayerAvatar(myPlayer) }}</text>
            <view class="player-info">
              <text class="username font-bold">我</text>
              <view class="hp-bar">
                <text 
                  v-for="(n, i) in myPlayer.maxHp" 
                  :key="i" 
                  class="heart"
                  :class="{ filled: i < myPlayer.hp }"
                >{{ i < myPlayer.hp ? '❤️' : '🤍' }}</text>
              </view>
              <view class="status-icons">
                <text v-if="myPlayer.shield" class="icon">🛡️</text>
                <text v-if="myPlayer.poisoned" class="icon">☠️</text>
                <text v-if="myPlayer.curse" class="icon">👻</text>
                <text v-if="myPlayer.revive" class="icon">💖</text>
                <text v-if="myPlayer.timeBombTurns > 0" class="icon pulse">⏰{{myPlayer.timeBombTurns}}</text>
                <text v-if="myPlayer.skipTurn" class="icon">⏭️</text>
              </view>
            </view>
            
            <!-- 我身上的飘字 -->
            <view v-for="l in getPlayerLabels(myPlayer.userId)" :key="l.id" class="float-label" :class="'text-' + l.type" style="top: -20rpx; left: 50%; transform: translateX(-50%);">
              {{ l.text }}
            </view>
          </view>

          <!-- 迷你日志栏 -->
          <scroll-view scroll-y class="game-logs" :scroll-top="logsScrollTop">
            <view v-for="log in logs" :key="log.id" class="log-line" :class="'log-' + log.type">
              {{ log.content }}
            </view>
          </scroll-view>
        </view>
      </view>
    </view>

    <!-- 结算弹窗 - 移至根级，确保不被 game-screen 的销毁影响 -->
    <view v-if="showResultModal" class="modal-overlay" style="z-index: 9999;" @tap="closeResultModal">
      <view class="modal-content glass-card" @tap.stop>
        <view class="modal-close-btn" @tap="closeResultModal">✕</view>
        <text class="modal-emoji">{{ getGameResultEmoji() }}</text>
        <text class="modal-title">{{ getGameResultTitle() }}</text>
        <view class="result-details" v-if="gameState && gameState.players">
          <text class="detail-text">{{ getResultMessage() }}</text>
        </view>
        <view
          class="btn-primary"
          :class="{ disabled: isRefreshing }"
          @tap="refreshAndPlayAgain"
        >
          <text class="btn-text">🚀 再来一局</text>
        </view>
      </view>
    </view>



    <!-- 玩法说明弹窗 -->
    <view v-if="showGuide" class="modal-overlay" @tap="showGuide = false">
      <view class="guide-modal glass-card" @tap.stop>
        <view class="guide-header">
          <text class="guide-title">核心战书</text>
          <view class="close-btn" @tap="showGuide = false">✕</view>
        </view>
        <scroll-view scroll-y class="guide-body">
          <text class="section-title">🛡️ 道具百科</text>
          <view class="guide-grid">
            <view class="guide-item" v-for="(item, i) in guideItems" :key="'i'+i">
              <text class="icon">{{ item.i }}</text>
              <view class="desc">
                <text class="name">{{ item.n }}</text>
                <text class="detail">{{ item.d }}</text>
              </view>
            </view>
          </view>
          
          <text class="section-title" style="margin-top: 32rpx;">🐾 角色天赋</text>
          <view class="guide-grid">
            <view class="guide-item" v-for="(item, i) in characterGuides" :key="'c'+i">
              <text class="icon">{{ item.i }}</text>
              <view class="desc">
                <text class="name">{{ item.n }}</text>
                <text class="detail">{{ item.d }}</text>
              </view>
            </view>
          </view>
        </scroll-view>
      </view>
    </view>
    
    <!-- 全局飘字 -->
    <view v-for="l in globalLabels" :key="l.id" class="float-label global" :style="{ top: l.y + 'px', left: l.x + 'px' }" :class="'text-' + l.type">
      {{ l.text }}
    </view>

  </view>
</template>

<script>
import { nakamaManager } from '../../../utils/nakamaManager.js';

export default {
  data() {
    return {
      gameState: null,
      logs: [],
      isConnected: false,
      isMatching: false,
      matchId: null,
      gameToken: null,
      stableUserId: null,
      myUserId: null,
      floatingLabels: [],
      showGuide: false,
      debugMode: false,
      matchingTimer: 0,
      matchPlayerCount: 4,
      turnTimer: 15,
      screenShaking: false,
      damagedPlayers: [],
      healedPlayers: [],
      isRefreshing: false,
      logsScrollTop: 0,
      isSpectator: false,
      showSettlement: false,
      settlementWinnerId: '',
      showResultModal: false,
      onlineCount: 0,
      onlineCountInterval: null,
      spectatorFlags: {}, // 观察者模式的旗帜标记 { cellIndex: true }
      spectatorMarks: {}, // 观察者模式的画板标记 { cellIndex: 'flag' | 'check' }
      spectatorTool: 'flag', // 观察者当前选择的工具: 'flag' | 'check' | 'eraser'
      lastTapTime: 0, // 上次点击时间，用于检测双击
      lastTapIndex: -1, // 上次点击的格子索引
      // Timers
      matchInterval: null,
      turnInterval: null,
      
      guideItems: [
        { i: '💊', n: '医疗包', d: '回复1点HP并清除中毒' },
        { i: '🛡️', n: '护盾', d: '抵挡下次伤害' },
        { i: '🔍', n: '放大镜', d: '透视1个格子' },
        { i: '🔪', n: '匕首', d: '对随机对手造成1点伤害' },
        { i: '⚡', n: '闪电', d: '全员造成1点伤害' },
        { i: '⏭️', n: '好人卡', d: '获得护盾，跳过回合' },
        { i: '💖', n: '复活', d: 'HP归零时复活' },
        { i: '⏰', n: '定时炸弹', d: '3回合后爆炸' },
        { i: '☠️', n: '毒药', d: '对手中毒每步扣血' },
        { i: '👻', n: '诅咒', d: '特定操作触发扣血' },
        { i: '📦', n: '宝箱', d: '随机大奖' },
      ],
      characterGuides: [
        { i: '🐶', n: '小狗', d: '忠诚：每移动一定步数必触发放大镜效果' },
        { i: '🐘', n: '大象', d: '执拗：无法回复生命，但基础HP更高' },
        { i: '🐯', n: '虎哥', d: '猛攻：匕首进化为全屏范围伤害' },
        { i: '🐵', n: '猴子', d: '敏锐：每次点击都有概率发现香蕉(回血)' },
        { i: '🦥', n: '树懒', d: '迟缓：翻到炸弹时伤害减半(扣1点)' },
        { i: '🦛', n: '河马', d: '大胃：无法直接捡起道具卡' },
      ]
    }
  },
  computed: {
    myPlayer() {
      if (!this.gameState || !this.gameState.players || !this.myUserId) return null;
      return this.gameState.players[this.myUserId];
    },
    opponents() {
      if (!this.gameState || !this.gameState.players || !this.myUserId) return [];
      return Object.values(this.gameState.players).filter(p => p.userId !== this.myUserId);
    },
    isMyTurn() {
      if (!this.gameState) return false;
      return this.gameState.turnOrder[this.gameState.currentTurnIndex] === this.myUserId;
    },
    globalLabels() {
      return this.floatingLabels.filter(l => l.cellIndex === undefined && l.targetUserId === undefined);
    },
    // 判断是否应该显示结算弹窗
    shouldShowResultModal() {
      // 有游戏状态且游戏已结束且有获胜者
      if (!this.gameState) return false;

      // 检查 gameStarted 是否为 false（游戏已结束）
      const isGameOver = this.gameState.gameStarted === false;

      // 检查是否有获胜者
      const hasWinner = !!this.gameState.winnerId;

      console.log('[shouldShowResultModal] 检查弹窗显示条件:', {
        isGameOver,
        hasWinner,
        winnerId: this.gameState.winnerId,
        myUserId: this.myUserId,
        shouldShow: isGameOver && hasWinner
      });

      return isGameOver && hasWinner;
    }
  },
  watch: {
    shouldShowResultModal(newVal) {
      console.log('[WATCH] shouldShowResultModal changed:', newVal);
      if (newVal) {
        this.showResultModal = true;
      }
    },
    'gameState.currentTurnIndex'() {
       this.resetTurnTimer();
    },
    'gameState.gameStarted'(val) {
      if (val) this.resetTurnTimer();
    }
  },
  onLoad(options) {
    this.fetchGameConfig();
    const { game_token, nakama_server, nakama_key, match_id, is_spectator, uid } = options;
    if (is_spectator) {
      this.isSpectator = true;
      // 观察者模式：禁用页面缩放
      this.disablePageZoom();
    }
    if (match_id) this.matchId = match_id;
    if (uid) this.stableUserId = uid;

    // 微信小程序：动态禁用页面缩放
    // #ifdef MP-WEIXIN
    const pages = getCurrentPages();
    const currentPage = pages[pages.length - 1];
    if (currentPage && currentPage.$page) {
      currentPage.$page.meta = Object.assign({}, currentPage.$page.meta, {
        disableScroll: true,
        disableScale: true
      });
    }
    // #endif

    if (game_token) {
      this.initNakama(game_token, decodeURIComponent(nakama_server || ''), decodeURIComponent(nakama_key || ''), uid);
    } else {
      uni.showToast({ title: '参数错误', icon: 'none' });
    }
  },
  onUnload() {
    this.cleanup();
  },
  methods: {
    disablePageZoom() {
      // 观察者模式：禁用页面缩放和长按菜单
      // 注意：这些操作在 H5 环境下有效
      // #ifdef H5
      document.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        e.stopPropagation();
        return false;
      });

      // 禁用双指缩放
      document.addEventListener('gesturestart', (e) => {
        e.preventDefault();
      });

      document.addEventListener('gesturechange', (e) => {
        e.preventDefault();
      });

      document.addEventListener('gestureend', (e) => {
        e.preventDefault();
      });
      // #endif

      // 微信小程序环境下，通过页面配置禁用缩放
      // 这些需要在 pages.json 中配置
    },
    async fetchGameConfig() {
       try {
          const res = await new Promise((resolve, reject) => {
            uni.request({
              url: 'https://kdy.1024tool.vip/api/internal/game/minesweeper/config',
              header: { 'X-Internal-Key': 'bindbox-internal-secret-2024' },
              success: (res) => resolve(res),
              fail: (err) => reject(err)
            })
          });
          if (res.statusCode === 200 && res.data) {
             const config = res.data;
             if (config.match_player_count && config.match_player_count >= 2) {
                this.matchPlayerCount = config.match_player_count;
             }
          }
       } catch(e) {
          console.warn('Config fetch failed');
       }
    },
    getCellLabels(idx) {
      return this.floatingLabels.filter(l => l.cellIndex === idx);
    },
    getPlayerLabels(userId) {
      return this.floatingLabels.filter(l => l.targetUserId === userId);
    },
    addLog(type, content) {
      const now = new Date();
      const timeStr = now.getHours().toString().padStart(2, '0') + ':' + now.getMinutes().toString().padStart(2, '0') + ':' + now.getSeconds().toString().padStart(2, '0');
      const id = Date.now() + Math.random().toString();

      // 插入到数组头部，最新的在最上面
      this.logs.unshift({ id, type, content, time: timeStr });

      // 增加日志存储限制到 100 条，让用户能看到更多历史
      if (this.logs.length > 100) this.logs.pop();

      // 确保滚动到顶部显示最新日志
      this.$nextTick(() => {
        this.logsScrollTop = 0;
      });
    },
    spawnLabel(x, y, text, type, cellIndex, targetUserId) {
      const id = Date.now() + Math.random().toString();
      this.floatingLabels.push({ id, x, y, text, type, cellIndex, targetUserId });
      setTimeout(() => {
        this.floatingLabels = this.floatingLabels.filter(l => l.id !== id);
      }, 1000);
    },
    async initNakama(token, server, key, stableUid = null) {
      try {
        const serverUrl = server || 'wss://kdy.1024tool.vip';
        const serverKey = key || 'defaultkey';
        nakamaManager.initClient(serverUrl, serverKey);
        this.gameToken = token;

        // 只进行认证，不自动建立 WebSocket 连接
        // WebSocket 将在用户点击开始匹配时才连接（解决抖音小程序的 APIScopeError）
        const session = await nakamaManager.authenticateWithGameToken(token, stableUid);
        this.myUserId = session.user_id;

        // 注意：此时 isConnected = false，因为还未建立 WebSocket 连接
        // 连接将在 startMatchmaking() 或其他需要网络操作的方法中进行
        this.addLog('system', '✅ 认证成功，准备就绪');
        this.setupSocketListeners();

        // 跨设备对局发现逻辑：调用 RPC 询问服务器我当前是否有正在进行的战局
        if (!this.matchId) {
          try {
            // RPC 需要 WebSocket 连接，所以先连接
            await nakamaManager.connect();
            this.isConnected = true;

            const activeMatch = await nakamaManager.rpc('find_my_match', {});
            if (activeMatch && activeMatch.match_id) {
              console.log('[RPC发现] 发现服务器端活跃对局:', activeMatch.match_id);
              this.matchId = activeMatch.match_id;
            }
          } catch (rpcErr) {
            console.warn('[RPC发现] 检索活跃对局失败:', rpcErr);
          }
        }

        // 跨页面对局自愈逻辑：如果 RPC 没找到，尝试从本地缓存读取（作为兜底）
        if (!this.matchId) {
          const lastMatchId = uni.getStorageSync('minesweeper_last_match_id');
          if (lastMatchId) {
            console.log('[自愈] 发现未完成对局缓存:', lastMatchId);
            this.matchId = lastMatchId;
          }
        }

        // 如果有比赛 ID（来自 URL 或 缓存），尝试加入
        if (this.matchId) {
          // 确保已连接
          if (!this.isConnected) {
            await nakamaManager.connect();
            this.isConnected = true;
          }

          this.addLog('system', this.isSpectator ? '🔭 正在切入观察视角...' : '🚪 正在恢复战局...');
          try {
             await nakamaManager.joinMatch(this.matchId);
             uni.setStorageSync('minesweeper_last_match_id', this.matchId);
             this.addLog('system', '✅ 接入成功');
             setTimeout(() => {
                nakamaManager.sendMatchState(this.matchId, 100, JSON.stringify({ action: 'getState' }));
             }, 100);
          } catch(err) {
             console.error('[自愈] 重连失败:', err);
             this.addLog('system', '❌ 自动恢复失败 (对局可能已结束)');
             this.matchId = null;
             uni.removeStorageSync('minesweeper_last_match_id');
          }
        }

        // 加入大厅频道用于统计在线人数
        this.joinLobby();

        // 启动在线人数轮询
        this.startOnlineCountPolling();

      } catch (e) {
        console.error('[初始化错误]', e);
        this.addLog('system', '❌ 初始化失败: ' + e.message);
        uni.showToast({
          title: '连接失败，请重试',
          icon: 'none'
        });
      }
    },
    setupSocketListeners() {
      nakamaManager.setListeners({
        onmatchmakermatched: async (matched) => {
          // ========== 控制台日志 - 匹配成功 ==========
          console.log('=== 匹配成功事件 ===');
          console.log('[匹配时间]', new Date().toLocaleString());
          console.log('[Match ID]', matched.match_id);
          console.log('[Match Token]', matched.token ? '存在' : '不存在 (权威模式通常不需要)');
          console.log('[完整匹配对象]', JSON.stringify(matched, null, 2));
          console.log('==================');

          this.addLog('system', `📡 信号锁定！同步中...`);
          this.isMatching = false;
          clearInterval(this.matchInterval);

          // 保存匹配信息用于可能的重连
          this.pendingMatchId = matched.match_id;
          this.pendingMatchToken = matched.token;

          // 带重试的 joinMatch
          const maxRetries = 3;
          for (let i = 0; i < maxRetries; i++) {
            try {
              console.log(`[尝试 ${i + 1}/${maxRetries}] 开始加入游戏房间...`);
              const match = await nakamaManager.joinMatch(matched.match_id, matched.token);

              // ========== 控制台日志 - 加入成功 ==========
              console.log('=== 成功加入游戏房间 ===');
              console.log('[房间 ID]', match.match_id);
              console.log('[我的用户 ID]', this.myUserId);
              console.log('[房间对象]', JSON.stringify(match, null, 2));
              console.log('======================');

              this.matchId = match.match_id;
              uni.setStorageSync('minesweeper_last_match_id', match.match_id);
              this.addLog('system', `成功接入战局`);
              setTimeout(() => {
                console.log('[发送状态请求] 请求初始游戏状态...');
                nakamaManager.sendMatchState(match.match_id, 100, JSON.stringify({ action: 'getState' }));
              }, 100);
              return; // 成功，退出重试循环
            } catch (e) {
              // ========== 控制台日志 - 加入失败 ==========
              console.error(`[尝试 ${i + 1}/${maxRetries} 失败]`, e.message);
              console.error('[错误详情]', e);

              this.addLog('system', `⚠️ 接入尝试 ${i + 1}/${maxRetries} 失败: ${e.message}`);
              if (i < maxRetries - 1) {
                console.log(`等待 1 秒后重试...`);
                await new Promise(r => setTimeout(r, 1000)); // 等待1秒后重试
              }
            }
          }

          // ========== 控制台日志 - 最终失败 ==========
          console.error('=== 加入游戏房间最终失败 ===');
          console.error('[Match ID]', matched.match_id);
          console.error('[重试次数]', maxRetries);
          console.error('============================');

          this.addLog('system', `❌ 接入失败，请检查网络后重试`);
        },
        onmatchdata: (matchData) => {
          const opCode = matchData.op_code;
          // NakamaManager 已内部处理了 utf8 解码，直接 parse 即可
          // const data = JSON.parse(new TextDecoder().decode(matchData.data));
          const data = typeof matchData.data === 'string' ? JSON.parse(matchData.data) : matchData.data;

          // ========== 控制台日志 - 接收游戏数据 ==========
          console.log('=== 接收游戏数据 ===');
          console.log('[OpCode]', opCode, `(${this.getOpCodeName(opCode)})`);
          console.log('[数据]', data);
          console.log('[时间]', new Date().toLocaleString());
          console.log('====================');

          this.handleGameData(opCode, data);
        },
        ondisconnect: async () => {
          // ========== 控制台日志 - 断开连接 ==========
          console.warn('=== 连接断开 ===');
          console.warn('[断开时间]', new Date().toLocaleString());
          console.warn('[Match ID]', this.matchId);
          console.warn('===============');

          this.addLog('system', `⚠️ 连接断开，尝试重连中...`);
          this.isConnected = false;

          // 自动重连
          try {
            if (this._isReconnecting) return;
            this._isReconnecting = true;

            console.log('[重连] 开始重新连接...');

            // 先确保认证（如果 session 已失效）
            if (!nakamaManager.session || !nakamaManager.session.token) {
              console.log('[重连] 重新认证...');
              const session = await nakamaManager.authenticateWithGameToken(this.gameToken, this.stableUserId);
              this.myUserId = session.user_id;
            }

            // 建立WebSocket连接
            await nakamaManager.connect();
            this.isConnected = true;

            console.log('[重连] WebSocket 连接成功');
            this.addLog('system', `✅ 重连成功`);

            // 如果有进行中的比赛，尝试重新加入
            if (this.matchId) {
              console.log('[重连] 尝试重新加入游戏房间:', this.matchId);
              const match = await nakamaManager.joinMatch(this.matchId);
              console.log('[重连] 成功重新加入游戏房间');

              this.addLog('system', `✅ 已重新加入战局`);
              nakamaManager.sendMatchState(this.matchId, 100, JSON.stringify({ action: 'getState' }));
            }
          } catch (e) {
            console.error('[重连] 失败:', e);
            this.addLog('system', `❌ 重连失败: ${e.message}`);
          } finally {
            this._isReconnecting = false;
          }
        }
      });
    },
    handleGameData(opCode, data) {
      // 关键游戏状态的控制台日志
      if (opCode === 1) {
        // ========== 控制台日志 - 游戏开始 ==========
        console.log('🎮 ========== 游戏开始 ==========');
        console.log('[玩家数量]', Object.keys(data.players || {}).length);
        console.log('[我的位置]', data.turnOrder?.indexOf(this.myUserId));
        console.log('[网格大小]', data.gridSize);
        console.log('[游戏状态]', data);
        console.log('[DEBUG] myUserId:', this.myUserId);
        console.log('[DEBUG] turnOrder:', data.turnOrder);
        console.log('[DEBUG] currentTurnIndex:', data.currentTurnIndex);
        console.log('[DEBUG] 当前回合玩家:', data.turnOrder?.[data.currentTurnIndex]);
        console.log('[DEBUG] isMyTurn?', data.turnOrder?.[data.currentTurnIndex] === this.myUserId);
        console.log('================================');
      } else if (opCode === 6) {
        // ========== 控制台日志 - 游戏结束 ==========
        console.log('🏁 ========== 游戏结束 ==========');
        console.log('[获胜者]', data.winnerId);
        console.log('[是否胜利]', data.winnerId === this.myUserId);
        console.log('[最终状态]', data);
        console.log('================================');
      }

      if (opCode === 1) {
        this.gameState = data;
        const limit = data.turnDuration || 15;
        this.resetTurnTimer(limit, limit);
        this.addLog('system', '战局开始，准备翻格！');
      } else if (opCode === 2) {
        // 状态更新 - 检测 HP 变化触发特效
        const prevState = this.gameState;
        if (prevState && prevState.players) {
           Object.keys(data.players || {}).forEach(uid => {
             const prevP = prevState.players[uid];
             const newP = data.players[uid];
             if (!prevP || !newP) return;

             const hpDiff = newP.hp - prevP.hp;
             if (hpDiff < 0) {
               // 受伤
               const dmg = Math.abs(hpDiff);
               this.spawnLabel(0, 0, `-${dmg}`, 'damage', undefined, uid);
               this.triggerDamageEffect(uid, dmg);
             } else if (hpDiff > 0) {
               // 治疗
               const heal = Math.abs(hpDiff);
               this.spawnLabel(0, 0, `+${heal}`, 'heal', undefined, uid);
               this.healedPlayers.push(uid);
               setTimeout(() => this.healedPlayers = this.healedPlayers.filter(id => id !== uid), 600);
             }
           });
        }
        // 收到状态更新时重置计时器（使用服务器时间校准）
        if (data.gameStarted) {
            const serverTime = data.serverTime || Math.floor(Date.now() / 1000);
            const elapsed = serverTime - (data.lastMoveTimestamp || serverTime);
            const limit = data.turnDuration || 15;
            const remaining = Math.max(0, limit - elapsed);
            this.resetTurnTimer(remaining, limit);
        }
        
        // 关键修复：必须更新本地游戏状态
        this.gameState = data;
      } else if (opCode === 5) {
        this.handleEvent(data);
      } else if (opCode === 6) {
        // 游戏结束 - 确保正确设置游戏状态
        console.log('[游戏结束] 接收到游戏结束数据:', data);

        const winnerId = data.winnerId || (data.gameState && data.gameState.winnerId) || '';
        this.settlementWinnerId = winnerId;
        this.showSettlement = !!winnerId;
        uni.removeStorageSync('minesweeper_last_match_id');

        if (data.gameState) {
           // 更新游戏状态并标记为已结束
           this.gameState = {
             ...data.gameState,
             gameStarted: false,  // 明确标记游戏已结束
             winnerId: winnerId
           };
        } else if (this.gameState) {
           this.gameState.gameStarted = false;
           if (winnerId) this.gameState.winnerId = winnerId;
        } else {
           this.gameState = {
             ...data,
             gameStarted: false,
             winnerId: winnerId
           };
        }

        // 确保 winnerId 存在于全局状态中以便计算属性使用
        if (this.gameState && !this.gameState.winnerId && winnerId) {
          this.gameState.winnerId = winnerId;
        }

        console.log('[游戏结束] 最终游戏状态:', this.gameState);
        console.log('[游戏结束] winnerId:', winnerId);

        // 根据结果显示不同的日志消息
        let endMsg = '';
        if (winnerId === 'draw') {
          endMsg = '平局：无人幸存';
        } else if (winnerId === this.myUserId) {
          endMsg = '🎉 您获得了胜利！';
        } else {
          endMsg = '💀 很遗憾失败了';
        }
        this.addLog('system', endMsg);

        // 添加震动反馈
        // 微信小程序只支持 type: 'success' 或不传参数
        if (winnerId === this.myUserId) {
          uni.vibrateShort({ type: 'success' });
        } else if (winnerId === 'draw') {
          uni.vibrateShort();  // 平局使用普通震动
        } else {
          uni.vibrateShort();  // 失败使用普通震动
        }
      }
    },
    handleEvent(event) {
      if (event.type === 'damage' || event.type === 'heal' || event.type === 'item' || event.type === 'ability') {
        const isMe = event.playerID === this.myUserId || event.targetUserId === this.myUserId;
        const iAmTarget = event.targetUserId === this.myUserId;
        const attackerName = event.playerName || '系统';
        const itemDisplayName = this.guideItems.find(i => i.i === event.itemId)?.n || event.itemId;
        
        // 优先使用服务端传来的 message 字段
        let msg = event.message;
        if (!msg) {
          if (event.type === 'item') msg = `发现了 ${itemDisplayName}`;
          else if (event.type === 'damage') msg = `受到了 ${event.value} 点伤害`;
          else if (event.type === 'heal') msg = `回复了 ${event.value} 点生命制`;
        }

        if (isMe) this.addLog('effect', msg);
        else this.addLog('effect', `${attackerName.substring(0,8)}: ${msg}`);

        if (event.type === 'damage' || event.type === 'item') {
           if (iAmTarget && event.value) this.spawnLabel(0, 0, `-${event.value}`, 'damage', undefined, this.myUserId);
        }
        // 对于 opCode 5 的 damage 事件，也触发特效
        if (event.type === 'damage' && iAmTarget) this.triggerDamageEffect(this.myUserId, event.value);
      }
    },
    triggerDamageEffect(uid, amount) {
       this.damagedPlayers.push(uid);
       setTimeout(() => this.damagedPlayers = this.damagedPlayers.filter(id => id !== uid), 600);
       if (uid === this.myUserId) {
         this.screenShaking = true;
         setTimeout(() => this.screenShaking = false, 400);
       }
    },
    async startMatchmaking() {
      if (this.isMatching) return;
      try {
        console.log('--- UI Click: Start Matchmaking ---');
        this.isMatching = true;
        this.matchingTimer = 0;
        this.logs = [];
        this.addLog('system', '🚀 发射匹配脉冲...');

        // 抖音小程序：确保在用户交互后才建立 WebSocket 连接
        if (!this.isConnected) {
          this.addLog('system', '📡 正在建立连接...');
          try {
            await nakamaManager.connect();
            this.isConnected = true;
            this.addLog('system', '✅ 连接成功');
          } catch (connErr) {
            console.error('[连接失败]', connErr);
            this.addLog('system', '❌ 连接失败: ' + connErr.message);
            uni.showToast({
              title: '连接失败，请重试',
              icon: 'none'
            });
            this.isMatching = false;
            return;
          }
        }

        clearInterval(this.matchInterval);
        this.matchInterval = setInterval(() => this.matchingTimer++, 1000);

        await nakamaManager.findMatch(this.matchPlayerCount, this.matchPlayerCount);
        console.log('Matchmaker ticket requested');
      } catch (e) {
        console.error('Matchmaking error:', e);
        this.isMatching = false;
        clearInterval(this.matchInterval);

        // 提供更友好的错误提示
        let errorMsg = '❌ 匹配失败';
        if (e.message) {
          if (e.message.includes('socket') || e.message.includes('APIScope')) {
            errorMsg = '❌ 网络连接异常，请重试';
          } else if (e.message.includes('timeout')) {
            errorMsg = '❌ 连接超时，请检查网络';
          } else {
            errorMsg = '❌ ' + e.message;
          }
        }
        this.addLog('system', errorMsg);

        uni.showToast({
          title: errorMsg.replace('❌ ', ''),
          icon: 'none',
          duration: 3000
        });
      }
    },
    cancelMatchmaking() {
      this.isMatching = false;
      clearInterval(this.matchInterval);
      this.addLog('system', '已切断匹配信号');
    },
    handleCellClick(idx) {
      // 观察者模式：画板功能
      if (this.isSpectator) {
        if (!this.gameState?.grid) return;
        if (this.gameState.grid[idx].revealed) return;

        // 根据当前选择的工具执行相应操作
        if (this.spectatorTool === 'eraser') {
          // 橡皮擦：移除该位置的标记
          if (this.spectatorMarks[idx]) {
            const markType = this.spectatorMarks[idx] === 'flag' ? '旗帜' : '勾选';
            this.$set(this.spectatorMarks, idx, undefined);
            this.$nextTick(() => {
              delete this.spectatorMarks[idx];
            });
            this.addLog('system', `🧹 擦除了位置 ${idx} 的${markType}`);
          }
        } else if (this.spectatorTool === 'flag') {
          // 旗帜工具：切换旗帜
          if (this.spectatorMarks[idx] === 'flag') {
            this.$set(this.spectatorMarks, idx, undefined);
            this.$nextTick(() => {
              delete this.spectatorMarks[idx];
            });
            this.addLog('system', `🚩 移除了位置 ${idx} 的旗帜`);
          } else {
            this.$set(this.spectatorMarks, idx, 'flag');
            this.addLog('system', `🚩 在位置 ${idx} 插上了旗帜`);
          }

          // 添加震动反馈
          // #ifndef MP-ALIPAY
          uni.vibrateShort();
          // #endif
        } else if (this.spectatorTool === 'check') {
          // 勾选工具：切换勾选
          if (this.spectatorMarks[idx] === 'check') {
            this.$set(this.spectatorMarks, idx, undefined);
            this.$nextTick(() => {
              delete this.spectatorMarks[idx];
            });
            this.addLog('system', `✅ 移除了位置 ${idx} 的勾选`);
          } else {
            this.$set(this.spectatorMarks, idx, 'check');
            this.addLog('system', `✅ 在位置 ${idx} 标记了勾选`);
          }

          // 添加震动反馈
          // #ifndef MP-ALIPAY
          uni.vibrateShort();
          // #endif
        }
        return;
      }

      // 普通玩家模式
      if (!this.gameState?.gameStarted) {
        uni.showToast({ title: '游戏尚未开始', icon: 'none' });
        return;
      }
      if (this.showResultModal || this.showSettlement) return;
      if (!this.isMyTurn) {
        uni.showToast({ title: '等待对方行动...', icon: 'none' });
        return;
      }
      if (this.gameState.grid[idx].revealed) return;

      // 发送移动指令
      nakamaManager.sendMatchState(this.matchId, 3, JSON.stringify({ index: idx }));
    },
    // 观察者模式：选择工具
    selectTool(tool) {
      this.spectatorTool = tool;
      const toolNames = {
        flag: '🚩 旗帜',
        check: '✅ 勾选',
        eraser: '🧹 橡皮擦'
      };
      this.addLog('system', `切换工具: ${toolNames[tool]}`);
    },
    refreshAndPlayAgain() {
      uni.removeStorageSync('minesweeper_last_match_id');
      uni.navigateBack();
    },
    closeResultModal() {
      this.showResultModal = false;
    },
    resetTurnTimer(initialTime, limit) {
      const turnLimit = limit || (this.gameState?.turnDuration) || 15;
      this.turnTimer = Math.floor(initialTime !== undefined ? initialTime : turnLimit);
      clearInterval(this.turnInterval);
      this.turnInterval = setInterval(() => {
        if (this.turnTimer > 0) this.turnTimer--;
      }, 1000);
    },
    cleanup() {
      clearInterval(this.matchInterval);
      clearInterval(this.turnInterval);
      clearInterval(this.onlineCountInterval);

      nakamaManager.disconnect();
    },
    async fetchOnlineCount() {
      try {
        const result = await nakamaManager.rpc('get_online_count', {});
        if (result && typeof result.online_count === 'number') {
          this.onlineCount = result.online_count;
        }
      } catch (e) {
        console.warn('获取在线人数失败:', e);
      }
    },
    async joinLobby() {
      try {
        // 加入 minesweeper_lobby 频道, Type 1 (Room), Persistence True, Hidden False
        await nakamaManager.joinChat('minesweeper_lobby', 1, true, false);
        console.log('Successfully joined minesweeper lobby');
      } catch (error) {
        console.warn('Failed to join lobby channel:', error);
      }
    },
    startOnlineCountPolling() {
        if (this.onlineCountInterval) clearInterval(this.onlineCountInterval);
        this.fetchOnlineCount(); // Fetch immediately
        this.onlineCountInterval = setInterval(() => {
            if (this.isConnected) {
                this.fetchOnlineCount();
            }
        }, 5000); // Poll every 5 seconds
    },
    getItemIcon(itemId) {
      const map = { medkit: '💊', bomb_timer: '⏰', poison: '☠️', shield: '🛡️', skip: '⏭️', magnifier: '🔍', knife: '🔪', revive: '💖', lightning: '⚡', chest: '📦', curse: '👻' };
      return map[itemId] || '🎁';
    },
    getContentIcon(content) {
      if (content === 'bomb') return '💣';
      if (content === 'empty') return '✅';
      return this.getItemIcon(content);
    },
    getPlayerAvatar(player) {
      // 优先从 character 字段获取 emoji
      const charAvatars = {
        dog: '🐶',
        elephant: '🐘',
        tiger: '🐯',
        monkey: '🐵',
        sloth: '🦥',
        hippo: '🦛',
        cat: '🐱',
        chicken: '🐔'
      };
      if (player.character && charAvatars[player.character]) {
        return charAvatars[player.character];
      }

      // 如果服务器直接发送了 avatar 字段
      if (player.avatar) return player.avatar;

      // 降级：根据 userId 确定性分配
      const avatars = ['🐶', '🐘', '🐯', '🐵', '🦥', '🦛'];
      const seed = player.userId || player.username || player.id || '';
      let hash = 0;
      for (let i = 0; i < seed.length; i++) {
        hash = ((hash << 5) - hash) + seed.charCodeAt(i);
        hash = hash & hash; // 转换为32位整数
      }
      const index = Math.abs(hash) % avatars.length;
      return avatars[index];
    },
    getNumColor(n) { return ['','blue','green','red','purple','orange'][n] || 'black'; },
    getOpCodeName(code) {
      const map = {
        1: '游戏开始',
        2: '状态更新',
        3: '玩家点击格子',
        5: '游戏事件',
        6: '游戏结束',
        100: '请求游戏状态'
      };
      return map[code] || '未知';
    },
    // 获取结算详情信息
    getResultMessage() {
      if (!this.gameState || !this.gameState.players) return '';

      const winnerId = this.gameState.winnerId;

      // 平局情况
      if (winnerId === 'draw') {
        return '所有玩家都倒下了，本局为平局！';
      }

      const winner = this.gameState.players[winnerId];
      const winnerName = winner ? (winner.username || '对手') : '未知';

      if (winnerId === this.myUserId) {
        return '恭喜您战胜了所有对手，获得胜利！';
      } else {
        return `${winnerName.substring(0, 10)} 获得了胜利，下次好运！`;
      }
    },
    // 获取游戏结果表情
    getGameResultEmoji() {
      if (!this.gameState || !this.gameState.winnerId) return '💀';

      if (this.gameState.winnerId === 'draw') {
        return '🤝';
      } else if (this.gameState.winnerId === this.myUserId) {
        return '🏆';
      } else {
        return '💀';
      }
    },
    // 获取游戏结果标题
    getGameResultTitle() {
      if (!this.gameState || !this.gameState.winnerId) return '很遗憾失败了';

      if (this.gameState.winnerId === 'draw') {
        return '平局';
      } else if (this.gameState.winnerId === this.myUserId) {
        return '胜利！';
      } else {
        return '很遗憾失败了';
      }
    }
  }
}
</script>

<style src="./play.scss" lang="scss"></style>
