import './render';
import BackGround from './runtime/background';
import GameInfo from './runtime/gameinfo';
import Music from './runtime/music';
import WelcomeScreen from './runtime/welcome';
import TeamScreen from './runtime/team';
import CreateRoomScreen from './runtime/createRoom';
import JoinTeamSelectScreen from './runtime/joinTeamSelect';
import WaitingRoomScreen from './runtime/waitingRoom';
import TeamResultScreen from './runtime/teamResult';
import WaitingResultScreen from './runtime/waitingResult';
import DataBus from './databus';
import BubbleGrid from './bubble/bubbleGrid';
import Shooter from './bubble/shooter';
import { BUBBLE_RADIUS, getActiveColorCount } from './bubble/bubble';
import { SAFE_AREA_TOP, IS_PAD } from './render';
import Explosion from './effects/explosion';
import GameSocket, { API_BASE } from './network/socket';

const ctx = canvas.getContext('2d');

GameGlobal.databus = new DataBus();
GameGlobal.musicManager = new Music();
GameGlobal._gameSocket = GameSocket;

// 启动时从本地恢复昵称
try {
  const saved = wx.getStorageSync('player_nickname');
  if (saved) GameGlobal.databus.nickname = saved;
} catch (_) {}

/**
 * 游戏主函数
 */
export default class Main {
  aniId = 0;
  /** 当前激活的循环标识，每次切换页面时递增，旧循环检测到不匹配后自动退出 */
  _loopToken = 0;
  bg = new BackGround();
  gameInfo = new GameInfo();
  welcomeScreen = null;
  teamScreen = null;
  createRoomScreen = null;
  waitingRoomScreen = null;
  gameMode = null;      // 'single' | 'multi'
  teamAction = null;    // 'create' | 'join'
  roomPlayerCount = 1;  // 单边人数 1~3
  gameDuration = 0;     // 游戏时长（秒），0=无限制
  selectedTeam = 'A';   // 选择的队伍
  myPlayerId = 1;       // 当前玩家ID
  teamResultScreen = null;  // 队伍结果页面
  waitingResultScreen = null; // 等待其他玩家完成的画面
  playerScores = {};    // 存储所有玩家分数 {playerId: score}
  waitingRoomPlayers = [];  // 等待房间的玩家列表
  _teamResultShown = false; // 防止重复显示结果
  _waitingForScores = false; // 是否正在等待对手分数

  constructor() {
    this.gameInfo.on('restart', this.restart.bind(this));
    // 显示欢迎页面，等待用户选择模式
    this.showWelcome();
  }

  /**
   * 重置 canvas 上下文的关键状态，防止跨页面 shadow/alpha 等残留污染
   */
  _resetCtx() {
    ctx.globalAlpha = 1;
    ctx.shadowBlur = 0;
    ctx.shadowColor = 'transparent';
    ctx.shadowOffsetX = 0;
    ctx.shadowOffsetY = 0;
  }

  /**
   * 显示欢迎页面
   */
  showWelcome() {
    if (this.welcomeScreen) { this.welcomeScreen.destroy(); this.welcomeScreen = null; }
    this.welcomeScreen = new WelcomeScreen((action) => {
      this.gameMode = 'multi';
      this.teamAction = action;
      console.log('[Game] 首页操作:', action === 'create' ? '创建队伍' : '加入队伍');

      // 首页直接进入创建/加入流程，无昵称时先补昵称
      this.showNicknameInput(false, action);
    });

    // 递增 token，所有旧循环下一帧检测到 token 不符后自动退出
    const token = ++this._loopToken;
    requestAnimationFrame(() => this.welcomeLoop(token));
  }

  /**
   * 多人模式：输入昵称
   */
  showNicknameInput(forceShow = false, pendingAction = '') {
    const savedNick = GameGlobal.databus.nickname;

    // 已有昵称且非强制修改 → 直接进入目标流程
    if (savedNick && !forceShow) {
      this._enterLobbyAction(pendingAction);
      return;
    }

    wx.showModal({
      title: '输入昵称',
      content: '',
      editable: true,
      placeholderText: savedNick || '请输入你的昵称（1~8字符）',
      confirmText: '确认',
      cancelText: '取消',
      success: (res) => {
        if (!res.confirm) {
          // 取消 → 回到欢迎页
          this.showWelcome();
          return;
        }

        const input = (res.content || '').trim();

        // 未输入且有历史昵称，直接复用
        if (!input && savedNick) {
          this._enterLobbyAction(pendingAction);
          return;
        }

        // 校验：1~8个字符
        if (!input || input.length > 8) {
          wx.showToast({ title: '昵称 1~8 个字符', icon: 'error', duration: 3000 });
          GameGlobal.setTimeout(() => this.showNicknameInput(forceShow, pendingAction), 3000);
          return;
        }

        GameGlobal.databus.nickname = input;
        try { wx.setStorageSync('player_nickname', input); } catch (_) {}
        console.log('[Game] 昵称:', input);
        this._enterLobbyAction(pendingAction);
      },
    });
  }

  /**
   * 进入首页当前选中的大厅操作
   */
  _enterLobbyAction(action) {
    if (!action) {
      this.showWelcome();
      return;
    }

    this.gameMode = 'multi';
    this.teamAction = action;
    console.log('[Game] 队伍操作:', action === 'create' ? '创建队伍' : '加入队伍');

    if (action === 'create') {
      this.showCreateRoomScreen();
    } else {
      this.showJoinRoomInput();
    }
  }

  /**
   * 显示队伍选择页面（多人模式）
   */
  showTeamScreen() {
    if (this.teamScreen) { this.teamScreen.destroy(); this.teamScreen = null; }
    this.showWelcome();
  }

  /**
   * 加入队伍：弹出原生输入框让用户输入房间号（1~6位数字）
   */
  showJoinRoomInput() {
    wx.showModal({
      title: '加入房间',
      content: '',
      editable: true,
      placeholderText: '请输入房间号',
      confirmText: '加入',
      cancelText: '取消',
      success: (res) => {
        if (!res.confirm) {
          // 用户点取消，重新显示队伍选择页面
          this.showTeamScreen();
          return;
        }

        const input = (res.content || '').trim();

        // 校验：1~6 位纯数字
        if (!/^\d{1,6}$/.test(input)) {
          wx.showToast({ title: '请输入6位以内数字房间号', icon: 'error', duration: 3000 });
          GameGlobal.setTimeout(() => this.showJoinRoomInput(), 3000);
          return;
        }

        const roomId = input;
        console.log('[Game] 加入房间号:', roomId);

        // 先向服务端校验房间是否存在且 waiting
        wx.showLoading({ title: '验证房间中…', mask: true });
        wx.request({
          url: `${API_BASE}/rooms/${roomId}/check`,
          method: 'GET',
          success: (res) => {
            wx.hideLoading();
            const data = res.data;
            if (!data.ok) {
              wx.showToast({ title: data.message || '房间不可用', icon: 'error', duration: 3000 });
              GameGlobal.setTimeout(() => this.showJoinRoomInput(), 3000);
              return;
            }

            // 房间合法，进入队伍选择页面
            GameGlobal.databus.roomId = roomId;
            this.roomPlayerCount = data.totalSeats / 2;
            this.showJoinTeamSelectScreen(roomId, data.totalSeats);
          },
          fail: () => {
            wx.hideLoading();
            wx.showToast({ title: '网络错误，请重试', icon: 'error', duration: 3000 });
            GameGlobal.setTimeout(() => this.showJoinRoomInput(), 3000);
          },
        });
      },
    });
  }

  /**
   * 加入房间后选择队伍页面
   */
  showJoinTeamSelectScreen(roomId, totalSeats) {
    if (this.teamSelectScreen) { this.teamSelectScreen.destroy(); this.teamSelectScreen = null; }
    this.teamSelectScreen = new JoinTeamSelectScreen(
      roomId,
      (team) => {
        // 选择队伍后，进入等待页
        console.log('[Game] 选择队伍:', team);
        this.selectedTeam = team;
        this.showJoinWaitingScreen(roomId, totalSeats, team);
      },
      () => {
        // 返回，重新输入房间号
        this.showJoinRoomInput();
      }
    );

    const token = ++this._loopToken;
    requestAnimationFrame(() => this.teamSelectLoop(token));
  }

  /**
   * 保存等待房间的玩家列表
   */
  _saveWaitingRoomPlayers(players, myPlayerId, team) {
    this.waitingRoomPlayers = players || [];
    if (myPlayerId) {
      this.myPlayerId = myPlayerId;
    }
    // 服务端可能因队伍满员调整了队伍
    if (team) {
      this.selectedTeam = team;
    }
  }

  /**
   * 队伍选择页面渲染循环
   */
  teamSelectLoop(token) {
    if (token !== this._loopToken) return;
    if (!this.teamSelectScreen || !this.teamSelectScreen.visible) return;

    this._resetCtx();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.teamSelectScreen.render(ctx);
    requestAnimationFrame(() => this.teamSelectLoop(token));
  }

  /**
   * 加入方等待页面（与创建方共用 WaitingRoomScreen，role='join'）
   */
  showJoinWaitingScreen(roomId, totalSeats, team) {
    if (this.waitingRoomScreen) { this.waitingRoomScreen.destroy(); this.waitingRoomScreen = null; }
    this.waitingRoomScreen = new WaitingRoomScreen({
      roomId,
      totalSeats,
      role: 'join',
      team,  // 传入选择的队伍
      onStart: () => {
        console.log('[Game] 全员到齐，开始游戏');
        this.start();
      },
      onCancel: () => {
        GameGlobal.databus.roomId = null;
        GameSocket.disconnect();
        this.showTeamScreen();
      },
      onPlayersUpdate: (players, myPlayerId, team) => {
        this._saveWaitingRoomPlayers(players, myPlayerId, team);
      },
      onTeamFull: () => {
        // 队伍已满，跳回选队页面（会重新拉取最新数据）
        console.log('[Game] 队伍已满，返回选队页面');
        this.showJoinTeamSelectScreen(roomId, totalSeats);
      },
    });

    const token = ++this._loopToken;
    requestAnimationFrame(() => this.waitingRoomLoop(token));
  }

  /**
   * 队伍页面渲染循环
   */
  teamLoop(token) {
    if (token !== this._loopToken) return;
    if (!this.teamScreen || !this.teamScreen.visible) return;

    this._resetCtx();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.teamScreen.render(ctx);
    requestAnimationFrame(() => this.teamLoop(token));
  }

  /**
   * 显示创建房间页面（选择人数）
   */
  showCreateRoomScreen() {
    if (this.createRoomScreen) { this.createRoomScreen.destroy(); this.createRoomScreen = null; }
    this.createRoomScreen = new CreateRoomScreen(
      // 确认：带着人数、时长进入等待房间页面
      (playerCount, gameDuration) => {
        this.roomPlayerCount = playerCount;
        this.gameDuration = gameDuration;
        console.log('[Game] 创建房间，单边人数:', playerCount, '时长:', gameDuration, 's');
        this.showWaitingRoomScreen();
      },
      // 返回：回到队伍选择页面
      () => {
        this.showTeamScreen();
      }
    );

    const token = ++this._loopToken;
    requestAnimationFrame(() => this.createRoomLoop(token));
  }

  /**
   * 创建房间页面渲染循环
   */
  createRoomLoop(token) {
    if (token !== this._loopToken) return;
    if (!this.createRoomScreen || !this.createRoomScreen.visible) return;

    this._resetCtx();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.createRoomScreen.render(ctx);
    requestAnimationFrame(() => this.createRoomLoop(token));
  }

  /**
   * 显示等待房间页面
   * 先向服务端申请一个可用房间号，再进入等待页
   */
  showWaitingRoomScreen() {
    // 请求服务端分配房间号
    wx.request({
      url: `${API_BASE}/rooms/allocate`,
      method: 'POST',
      success: (res) => {
        if (res.statusCode !== 200 || !res.data.roomId) {
          wx.showToast({ title: res.data.error || '获取房间号失败', icon: 'error', duration: 3000 });
          return;
        }

        const roomId = res.data.roomId; // 服务端返回的 3 位字符串，如 "042"
        GameGlobal.databus.roomId = roomId;
        // 提前写入 databus，waitingRoom.js 发 room:create 时会读取
        GameGlobal.databus.gameDuration = this.gameDuration;
        console.log('[Game] 服务端分配房间号:', roomId, '时长:', this.gameDuration);

        if (this.waitingRoomScreen) { this.waitingRoomScreen.destroy(); this.waitingRoomScreen = null; }
        this.waitingRoomScreen = new WaitingRoomScreen({
          roomId,
          totalSeats: this.roomPlayerCount * 2,
          role: 'create',
          onStart: () => {
            console.log('[Game] 全员到齐，开始游戏');
            this.start();
          },
          onCancel: () => {
            GameGlobal.databus.roomId = null;
            GameSocket.disconnect();
            this.showTeamScreen();
          },
          onPlayersUpdate: (players, myPlayerId, team) => {
            this._saveWaitingRoomPlayers(players, myPlayerId, team);
          }
        });

        const token = ++this._loopToken;
        requestAnimationFrame(() => this.waitingRoomLoop(token));
      },
      fail: () => {
        wx.showToast({ title: '网络错误，请重试', icon: 'error', duration: 3000 });
      },
    });
  }

  /**
   * 等待房间页面渲染循环
   */
  waitingRoomLoop(token) {
    if (token !== this._loopToken) return;
    if (!this.waitingRoomScreen || !this.waitingRoomScreen.visible) return;

    this._resetCtx();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.waitingRoomScreen.render(ctx);
    requestAnimationFrame(() => this.waitingRoomLoop(token));
  }

  /**
   * 欢迎页面渲染循环
   */
  welcomeLoop(token) {
    if (token !== this._loopToken) return;
    if (!this.welcomeScreen || !this.welcomeScreen.visible) return;

    this._resetCtx();
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    this.bg.render(ctx);
    this.welcomeScreen.render(ctx);

    requestAnimationFrame(() => this.welcomeLoop(token));
  }

  /**
   * 清理所有活动的页面和资源，防止内存泄漏
   */
  _cleanupAll() {
    // 停用 GameInfo 触摸和 socket 监听
    this.gameInfo.deactivate();

    // 销毁射击器（含 wx 触摸事件）
    if (this.shooter) {
      this.shooter.destroy();
      this.shooter = null;
    }

    // 销毁所有可能存活的页面（每个 destroy 内部会 offTouchStart）
    if (this.welcomeScreen) { this.welcomeScreen.destroy(); this.welcomeScreen = null; }
    if (this.teamScreen) { this.teamScreen.destroy(); this.teamScreen = null; }
    if (this.createRoomScreen) { this.createRoomScreen.destroy(); this.createRoomScreen = null; }
    if (this.teamSelectScreen) { this.teamSelectScreen.destroy(); this.teamSelectScreen = null; }
    if (this.waitingRoomScreen) { this.waitingRoomScreen.destroy(); this.waitingRoomScreen = null; }
    if (this.waitingResultScreen) { this.waitingResultScreen.destroy(); this.waitingResultScreen = null; }
    if (this.teamResultScreen) { this.teamResultScreen.destroy(); this.teamResultScreen = null; }

    // 移除游戏结束相关监听
    GameSocket.off('room:gameOver');
    GameSocket.off('room:playerFinished');
    GameSocket.off('room:allFinished');

    // 清理等待超时计时器
    if (this._waitingTimeout) {
      GameGlobal.clearTimeout(this._waitingTimeout);
      this._waitingTimeout = null;
    }

    // 重置结果页相关状态
    this._teamResultShown = false;
    this._waitingForScores = false;
    this.playerScores = {};

    // 立即释放游戏资源（bubbleGrid、fireBubbles、explosions 的 native 内存）
    GameGlobal.databus.reset();
  }

  /**
   * 重新开始游戏（游戏结束后）
   */
  restart() {
    this._cleanupAll();
    this.showWelcome();
  }

  /**
   * 开始游戏
   */
  start() {
    // 销毁旧射击器的触摸事件监听，防止多实例叠加
    if (this.shooter) this.shooter.destroy();

    // 重置队伍结果相关状态
    this._teamResultShown = false;
    this._waitingForScores = false;
    this.teamResultScreen = null;
    this.playerScores = {};

    // reset 前先保存多人模式下服务端下发的时长（room:allReady 写入）
    const savedDuration = GameGlobal.databus.gameDuration;

    GameGlobal.databus.reset();

    // 还原时长
    if (this.gameMode === 'multi') {
      GameGlobal.databus.gameDuration = savedDuration;
    } else {
      GameGlobal.databus.gameDuration = this.gameDuration;
    }
    GameGlobal.databus.gameStartTime = Date.now();

    // 设置游戏信息模式并激活触摸/事件监听
    this.gameInfo.gameMode = this.gameMode;
    this.gameInfo.activate();

    // 初始化泡泡网格和射击器，并同步到全局数据
    // 传入 roomId 作为随机种子，相同房间 ID 的玩家初始布局一致
    GameGlobal.databus.bubbleGrid = new BubbleGrid(GameGlobal.databus.roomId);
    this.shooter = new Shooter();

    // 多人模式才建立 WebSocket 连接
    if (this.gameMode === 'multi') {
      console.log('[Game] 多人模式，建立 WebSocket 连接');
      console.log('[Game] 队伍操作:', this.teamAction);
      console.log('[Game] 对战人数:', `${this.roomPlayerCount} v ${this.roomPlayerCount}`);
      console.log('[Game] 当前房间号 roomId:', GameGlobal.databus.roomId);

      // socket 已在等待页面（WaitingRoomScreen）建立并发送 room:create / room:join
      // 此处无需重复操作

      // 监听游戏结束相关事件
      GameSocket.off('room:gameOver');
      GameSocket.off('room:playerFinished');
      GameSocket.off('room:allFinished');
      GameSocket.on('room:gameOver', this._onGameOver.bind(this));
      GameSocket.on('room:playerFinished', this._onPlayerFinished.bind(this));
      GameSocket.on('room:allFinished', this._onAllFinished.bind(this));
    } else {
      console.log('[Game] 单人模式，跳过 WebSocket 连接');
    }

    const token = ++this._loopToken;
    requestAnimationFrame(() => this.loop(token));
  }

  /**
   * 碰撞检测：判断飞行泡泡是否与网格泡泡或顶部边界发生碰撞。
   * 碰撞后依次执行：吸附入网格 → 同色连通消除 → 悬空掉落 → 游戏结束判定。
   * @param {import('./bubble/fireBubble').default} fb
   * @returns {boolean} 是否发生碰撞
   */
  collisionDetection(fb) {
    const R = BUBBLE_RADIUS;
    const grid = GameGlobal.databus.bubbleGrid;

    // 检测碰到顶部边界
    const hitTop = fb.y - R <= SAFE_AREA_TOP;

    // 检测与网格中任意泡泡的圆心距 < 2R（略收 5% 避免视觉穿透）
    let hitGrid = false;
    if (!hitTop) {
      const threshold = R * 2 * 0.95;
      outer: for (let row = 0; row < grid.grid.length; row++) {
        const rowArr = grid.grid[row];
        if (!rowArr) continue;
        for (let col = 0; col < rowArr.length; col++) {
          const bubble = rowArr[col];
          if (!bubble) continue;
          if (Math.hypot(fb.x - bubble.x, fb.y - bubble.y) < threshold) {
            hitGrid = true;
            break outer;
          }
        }
      }
    }

    if (!hitTop && !hitGrid) return false;

    // 停止飞行泡泡
    fb.active = false;

    // 吸附入网格
    const snapped = grid.snapToGrid(fb.x, fb.y, fb.color);
    if (!snapped) return true;

    // 每 N 次发射，从顶部下推 2 行新泡泡增加压力
    // iPad 屏幕矮，每 5 次触发；手机每 3 次触发
    // 注意：补行后不立即判定结束，等消除、悬空清除完成后统一判定
    GameGlobal.databus.shotCount++;
    const pushInterval = IS_PAD ? 5 : 3;
    if (GameGlobal.databus.shotCount % pushInterval === 0) {
      const colorCount = getActiveColorCount(GameGlobal.databus.score);
      grid.pushTwoRows(colorCount);
    }

    // 查找同色连通并尝试消除（≥3 才消）
    const connected = grid.findConnected(snapped.row, snapped.col);
    // 消除前收集每颗泡泡的位置与颜色，用于触发爆炸特效
    const connectedInfo = connected.map(([r, c]) => {
      const b = grid.getBubble(r, c);
      return b ? { x: b.x, y: b.y, color: b.color } : null;
    }).filter(Boolean);
    const cleared = grid.clearGroup(connected);
    if (cleared) {
      GameGlobal.musicManager.playExplosion();
      for (const { x, y, color } of connectedInfo) {
        GameGlobal.databus.explosions.push(new Explosion(x, y, color, false));
      }
      // 消除成功时触发短振动，兼容 iOS（iPhone 7+）和 Android
      // type 参数低版本不支持时会走 fail 回调，静默处理不影响体验
      wx.vibrateShort({ type: 'medium', fail: () => {} });
    }

    // 消除后检查并清除悬空泡泡
    const floating = grid.findFloating();
    // 收集悬空泡泡位置用于特效
    const floatingInfo = floating.map(([r, c]) => {
      const b = grid.getBubble(r, c);
      return b ? { x: b.x, y: b.y, color: b.color } : null;
    }).filter(Boolean);
    grid.dropFloating(floating);
    for (const { x, y, color } of floatingInfo) {
      GameGlobal.databus.explosions.push(new Explosion(x, y, color, true));
    }

    // 检查游戏结束：延迟 500ms 让下推动画播放完毕再弹出结束弹窗
    if (grid.isGameOver()) {
      GameGlobal.setTimeout(() => {
        if (GameGlobal.databus.isGameOver) return; // 防重复
        GameGlobal.databus.gameOver();
        if (this.gameMode === 'multi') {
          GameSocket.emit('room:gameOver', {
            roomId: GameGlobal.databus.roomId,
            score: GameGlobal.databus.score,
          });
          this._waitAndShowTeamResult();
        }
      }, 500);
      return true;
    }

    // 消除后若活跃行数不足，从顶部补充新行，始终保持约 12 行
    this._maintainRows();

    return true;
  }

  /**
   * 检查活跃行数，不足时从顶部每次插入 2 行（保持奇偶对齐），
   * 直到行数达标或新行压低触发游戏结束。
   */
  _maintainRows() {
    const grid = GameGlobal.databus.bubbleGrid;
    const colorCount = getActiveColorCount(GameGlobal.databus.score);
    const minRows = IS_PAD ? 7 : 10;
    while (grid.getLiveRowCount() < minRows) {
      grid.pushTwoRows(colorCount);
      if (grid.isGameOver()) {
        GameGlobal.setTimeout(() => {
          if (GameGlobal.databus.isGameOver) return;
          GameGlobal.databus.gameOver();
          if (this.gameMode === 'multi') {
            GameSocket.emit('room:gameOver', {
              roomId: GameGlobal.databus.roomId,
              score: GameGlobal.databus.score,
            });
            this._waitAndShowTeamResult();
          }
        }, 500);
        break;
      }
    }
  }

  /**
   * canvas重绘函数
   * 每一帧重新绘制所有需要展示的元素
   */
  render() {
    this._resetCtx();

    // 如果显示了队伍结果页面，只渲染结果页面
    if (this.teamResultScreen && this.teamResultScreen.visible) {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      this.teamResultScreen.render(ctx);
      return;
    }

    // 如果显示了等待画面，只渲染等待画面
    if (this.waitingResultScreen && this.waitingResultScreen.visible) {
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      this.waitingResultScreen.render(ctx);
      return;
    }

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    this.bg.render(ctx);
    GameGlobal.databus.bubbleGrid.render(ctx);

    // 渲染飞行中的泡泡
    GameGlobal.databus.fireBubbles.forEach(fb => fb.render(ctx));

    // 渲染爆炸特效（叠加在泡泡上方）
    GameGlobal.databus.explosions.forEach(e => e.render(ctx));

    this.shooter.render(ctx);
    this.gameInfo.render(ctx);
  }

  /**
   * 游戏逻辑更新主函数
   */
  update() {
    GameGlobal.databus.frame++;

    // 如果显示了队伍结果页面或等待页面，停止更新
    if ((this.teamResultScreen && this.teamResultScreen.visible) ||
        (this.waitingResultScreen && this.waitingResultScreen.visible)) {
      return;
    }

    if (GameGlobal.databus.isGameOver) {
      // 多人模式且未显示结果页面时，等待对手分数后显示队伍比分
      if (this.gameMode === 'multi' && !this._teamResultShown) {
        this._waitAndShowTeamResult();
      }
      return;
    }

    this.bg.update();
    GameGlobal.databus.bubbleGrid.update();

    // 更新爆炸特效，移除已播放完毕的实例
    const explosions = GameGlobal.databus.explosions;
    for (let i = explosions.length - 1; i >= 0; i--) {
      explosions[i].update();
      if (!explosions[i].alive) explosions.splice(i, 1);
    }

    // 更新飞行中的泡泡，每帧检测碰撞，失活后自动切换到下一颗
    const fireBubbles = GameGlobal.databus.fireBubbles;
    for (let i = fireBubbles.length - 1; i >= 0; i--) {
      const fb = fireBubbles[i];

      if (!fb.active) {
        fireBubbles.splice(i, 1);
        continue;
      }

      fb.update();

      // 先检测碰撞（碰撞会将 fb.active 置为 false 并完成吸附/消除）
      if (fb.active && this.collisionDetection(fb)) {
        fireBubbles.splice(i, 1);
        continue;
      }

      // 泡泡因飞出屏幕而失活（update 内部设置）
      if (!fb.active) {
        fireBubbles.splice(i, 1);
      }
    }
  }

  /**
   * 游戏帧循环
   */
  loop(token) {
    if (token !== this._loopToken) return; // 已被新循环替代，退出

    this.update();
    this.render();

    // 多人模式下，每 4 帧上报一次游戏状态（~15fps，平衡流畅度和性能）
    if (this.gameMode === 'multi' &&
        GameGlobal.databus.frame % 4 === 0 &&
        !GameGlobal.databus.isGameOver &&
        !(this.teamResultScreen && this.teamResultScreen.visible)) {
      const state = GameGlobal.databus.serialize();
      state.roomId = GameGlobal.databus.roomId;
      state.playerId = this.myPlayerId;
      state.nickname = GameGlobal.databus.nickname;
      state.team = this.selectedTeam;
      state.shooter = {
        aimAngle: parseFloat(this.shooter._aimAngle.toFixed(4)),
        isAiming: this.shooter._isAiming,
        currentColor: this.shooter.currentColor,
        nextColor: this.shooter.nextColor,
        bounceScaleX: parseFloat(this.shooter._bounceScaleX.toFixed(3)),
        bounceScaleY: parseFloat(this.shooter._bounceScaleY.toFixed(3)),
      };
      // 屏幕尺寸信息，供大屏端适配不同设备（手机/iPad）的画面比例
      state.screenRatio = parseFloat((canvas.width / canvas.height).toFixed(4));
      state.screenWidth = canvas.width;
      GameSocket.sendState(state);
    }

    requestAnimationFrame(() => this.loop(token));
  }

  /**
   * 游戏结束后显示等待画面，等待服务端 room:allFinished 事件
   * 不再用本地 5 秒轮询，改为真正等待全员完成
   */
  _waitAndShowTeamResult() {
    // 已经在等待或已显示结果，跳过
    if (this._teamResultShown || this._waitingForScores) return;
    this._waitingForScores = true;

    // 先将自己的分数写入
    const currentPlayerId = this.myPlayerId || 1;
    this.playerScores[currentPlayerId] = GameGlobal.databus.score;

    const totalPlayers = (this.waitingRoomPlayers && this.waitingRoomPlayers.length) || 1;

    console.log('[Game] 进入等待画面，等待全员完成');

    // 显示等待画面
    if (this.waitingResultScreen) this.waitingResultScreen.destroy();
    this.waitingResultScreen = new WaitingResultScreen({
      myScore: GameGlobal.databus.score,
      finishedCount: 1,
      totalCount: totalPlayers,
    });

    // 保底超时：60 秒后强制显示结果（防止无限等待）
    this._waitingTimeout = GameGlobal.setTimeout(() => {
      console.log('[Game] 等待超时（60s），强制显示结果');
      this._waitingForScores = false;
      if (!this._teamResultShown) {
        this._showTeamResult();
      }
    }, 60000);
  }

  /**
   * 处理 room:gameOver 事件 —— 服务端通知某个玩家的分数
   * 现在只用于收集分数，不直接触发显示
   */
  _onGameOver(data) {
    console.log('[Game] 收到 room:gameOver 事件:', data);
    if (data && data.scores) {
      this.playerScores = { ...this.playerScores, ...data.scores };
    }
  }

  /**
   * 处理 room:playerFinished 事件 —— 某个玩家完成，更新等待画面进度
   */
  _onPlayerFinished(data) {
    console.log('[Game] 收到 room:playerFinished:', data);
    if (!data) return;

    // 更新等待画面进度
    if (this.waitingResultScreen && this.waitingResultScreen.visible) {
      this.waitingResultScreen.updateProgress(data.finishedCount, data.totalCount);
    }
  }

  /**
   * 处理 room:allFinished 事件 —— 全员完成，显示最终结果
   */
  _onAllFinished(data) {
    console.log('[Game] 收到 room:allFinished，全员完成:', data);
    if (!data) return;

    // 清除超时计时器
    if (this._waitingTimeout) {
      GameGlobal.clearTimeout(this._waitingTimeout);
      this._waitingTimeout = null;
    }

    // 合并最终分数
    if (data.scores) {
      this.playerScores = { ...this.playerScores, ...data.scores };
    }

    this._waitingForScores = false;
    this._showTeamResult();
  }

  /**
   * 显示队伍比分结果页面（多人模式游戏结束）
   */
  _showTeamResult() {
    // 防止重复显示
    if (this._teamResultShown) {
      console.log('[Game] 队伍比分页面已显示，跳过');
      return;
    }
    this._teamResultShown = true;

    // 隐藏等待画面
    if (this.waitingResultScreen) {
      this.waitingResultScreen.destroy();
      this.waitingResultScreen = null;
    }

    console.log('[Game] ===== 显示队伍比分页面 =====');
    console.log('[Game] gameMode:', this.gameMode);
    console.log('[Game] selectedTeam:', this.selectedTeam);
    console.log('[Game] myPlayerId:', this.myPlayerId);
    console.log('[Game] waitingRoomPlayers:', this.waitingRoomPlayers);
    console.log('[Game] current score:', GameGlobal.databus.score);

    // 构建玩家列表（包含当前玩家）
    const currentPlayerId = this.myPlayerId || 1;

    // 如果没有玩家列表，创建一个默认的
    let players = this.waitingRoomPlayers;
    if (!players || players.length === 0) {
      console.log('[Game] 使用默认玩家列表');
      players = [{
        playerId: currentPlayerId,
        nickname: GameGlobal.databus.nickname || '玩家',
        team: this.selectedTeam
      }];
    }

    // 添加当前玩家分数
    this.playerScores[currentPlayerId] = GameGlobal.databus.score;

    // 结果页优先使用玩家列表中“我”的真实队伍，避免 selectedTeam 残留导致胜负标题误判
    const meInPlayers = players.find(p => String(p.playerId) === String(currentPlayerId));
    const resolvedMyTeam = (meInPlayers && (meInPlayers.team === 'A' || meInPlayers.team === 'B'))
      ? meInPlayers.team
      : this.selectedTeam;
    this.selectedTeam = resolvedMyTeam;

    console.log('[Game] 创建 TeamResultScreen', {
      myTeam: resolvedMyTeam,
      players: players,
      scores: this.playerScores
    });

    // 创建结果页面
    this.teamResultScreen = new TeamResultScreen({
      myTeam: resolvedMyTeam,
      myPlayerId: currentPlayerId,
      myNickname: GameGlobal.databus.nickname || '',
      players: players,
      scores: this.playerScores,
      onConfirm: () => {
        // 断开 socket 连接（服务端也会主动断开，这里双重保险）
        GameSocket.disconnect();
        this.restart();
      }
    });

    console.log('[Game] TeamResultScreen 创建完成:', this.teamResultScreen);
  }
}
