import Emitter from '../libs/tinyemitter';
import { SCREEN_WIDTH, SCREEN_HEIGHT, SAFE_AREA_TOP, UI_SCALE } from '../render';
import GameSocket from '../network/socket';

/** 重新开始按钮尺寸 */
const BTN_W = 200 * UI_SCALE;
const BTN_H = 52 * UI_SCALE;

/** 卡片参数（与按钮区域保持同步） */
const CARD_W = 300 * UI_SCALE;
const CARD_H = 390 * UI_SCALE;
const CARD_X = SCREEN_WIDTH / 2 - CARD_W / 2;
const CARD_Y = SCREEN_HEIGHT / 2 - CARD_H / 2 - 20 * UI_SCALE;

export default class GameInfo extends Emitter {
  constructor() {
    super();

    // 重新开始按钮区域，位于卡片内底部（星星下方留 20px）
    this.btnArea = {
      startX: SCREEN_WIDTH / 2 - BTN_W / 2,
      startY: CARD_Y + CARD_H - 62 * UI_SCALE,
      endX: SCREEN_WIDTH / 2 + BTN_W / 2,
      endY: CARD_Y + CARD_H - 62 * UI_SCALE + BTN_H,
    };

    // 退出按钮区域（右上角，单人模式游戏中显示）
    const quitW = 40 * UI_SCALE;
    const quitH = 40 * UI_SCALE;
    this._quitBtn = {
      x: SCREEN_WIDTH - quitW - 10,
      y: SAFE_AREA_TOP + 10,
      w: quitW,
      h: quitH,
    };

    // 触摸事件：保存引用以便后续移除
    this._touchHandlerBound = this.touchEventHandler.bind(this);

    // 监听服务端倒计时到期（交叉校验：网络不好时本地倒计时已触发，此处幂等处理）
    this._onTimeUp = () => {
      if (!GameGlobal.databus.isGameOver) {
        GameGlobal.databus.gameOver();
        GameSocket.emit('room:gameOver', {
          roomId: GameGlobal.databus.roomId,
          score:  GameGlobal.databus.score,
        });
      }
    };

    // 默认不激活触摸监听，由 activate/deactivate 控制
    this._active = false;
  }

  /**
   * 激活触摸监听和 socket 事件（进入游戏时调用）
   */
  activate() {
    if (this._active) return;
    this._active = true;
    wx.onTouchStart(this._touchHandlerBound);
    GameSocket.on('room:timeUp', this._onTimeUp);
  }

  /**
   * 停用触摸监听和 socket 事件（离开游戏时调用）
   */
  deactivate() {
    if (!this._active) return;
    this._active = false;
    wx.offTouchStart(this._touchHandlerBound);
    GameSocket.off('room:timeUp', this._onTimeUp);
  }

  /** 游戏模式：'single' 或 'multi'，由外部设置 */
  gameMode = 'single';

  render(ctx) {
    this.renderGameScore(ctx, GameGlobal.databus.score);

    // 多人模式且设置了时长，渲染倒计时并检测超时
    if (this.gameMode === 'multi' && GameGlobal.databus.gameDuration > 0) {
      this._renderAndCheckTimer(ctx);
    }

    // 单人模式游戏中显示退出按钮（右上角）
    if (this.gameMode === 'single' && !GameGlobal.databus.isGameOver) {
      this._renderQuitBtn(ctx);
    }

    // 游戏结束渲染：单人模式显示旧版结束页，多人模式由外部处理（teamResult）
    if (GameGlobal.databus.isGameOver && this.gameMode !== 'multi') {
      this.renderGameOver(ctx, GameGlobal.databus.score);
    }
  }

  /**
   * 渲染倒计时卡片，时间到时触发游戏结束
   */
  _renderAndCheckTimer(ctx) {
    const elapsed  = (Date.now() - GameGlobal.databus.gameStartTime) / 1000;
    const remain   = Math.max(0, GameGlobal.databus.gameDuration - elapsed);
    const remainInt = Math.ceil(remain);

    // 时间到且游戏尚未结束 → 触发结束
    if (remain <= 0 && !GameGlobal.databus.isGameOver) {
      GameGlobal.databus.gameOver();
      // 复用多人模式游戏结束上报
      const GameSocket = GameGlobal._gameSocket;
      if (GameSocket) {
        GameSocket.emit('room:gameOver', {
          roomId: GameGlobal.databus.roomId,
          score:  GameGlobal.databus.score,
        });
      }
      return;
    }

    if (GameGlobal.databus.isGameOver) return;

    // ── 绘制倒计时卡片（顶部居中）──
    const S = UI_SCALE;
    const cx     = SCREEN_WIDTH / 2;
    const boxH   = 44 * S;
    const boxY   = SAFE_AREA_TOP + 8;
    const mins   = String(Math.floor(remainInt / 60)).padStart(2, '0');
    const secs   = String(remainInt % 60).padStart(2, '0');
    const timeStr = `${mins}:${secs}`;

    ctx.save();
    ctx.font = `bold ${Math.round(20 * S)}px Arial`;
    const tw = ctx.measureText(timeStr).width;
    const boxW = Math.max(tw + 28 * S, 80 * S);
    const boxX = cx - boxW / 2;
    const r = 10 * S;

    // 背景（最后10秒变红警示）
    const isUrgent = remainInt <= 10;
    ctx.shadowColor   = isUrgent ? 'rgba(239,68,68,0.5)' : 'rgba(0,0,0,0.4)';
    ctx.shadowBlur    = 8 * S;
    ctx.shadowOffsetY = 3 * S;

    const bg = ctx.createLinearGradient(boxX, boxY, boxX, boxY + boxH);
    if (isUrgent) {
      bg.addColorStop(0, 'rgba(220,38,38,0.92)');
      bg.addColorStop(1, 'rgba(185,28,28,0.95)');
    } else {
      bg.addColorStop(0, 'rgba(139,92,246,0.9)');
      bg.addColorStop(1, 'rgba(124,58,237,0.95)');
    }
    ctx.fillStyle = bg;
    this._roundRectPath(ctx, boxX, boxY, boxW, boxH, r);
    ctx.fill();
    ctx.shadowBlur = 0;

    // 边框
    ctx.strokeStyle = isUrgent ? 'rgba(252,165,165,0.6)' : 'rgba(167,139,250,0.5)';
    ctx.lineWidth = 1.5 * S;
    this._roundRectPath(ctx, boxX, boxY, boxW, boxH, r);
    ctx.stroke();

    // 标签
    ctx.textAlign    = 'center';
    ctx.textBaseline = 'alphabetic';
    ctx.font = `bold ${Math.round(11 * S)}px Arial`;
    ctx.fillStyle = isUrgent ? 'rgba(254,202,202,0.9)' : 'rgba(221,214,254,0.9)';
    ctx.fillText('时间', cx, boxY + 18 * S);

    // 时间数字
    ctx.font = `bold ${Math.round(20 * S)}px Arial`;
    const tg = ctx.createLinearGradient(cx - 20 * S, 0, cx + 20 * S, 0);
    if (isUrgent) {
      tg.addColorStop(0, '#FEE2E2');
      tg.addColorStop(1, '#FECACA');
    } else {
      tg.addColorStop(0, '#FDE68A');
      tg.addColorStop(1, '#F59E0B');
    }
    ctx.fillStyle = tg;
    ctx.fillText(timeStr, cx, boxY + 38 * S);

    ctx.restore();
  }

  /** 在屏幕左上角显示当前得分，多人模式右上角显示房间号 */
  renderGameScore(ctx, score) {
    ctx.save();

    // 单人模式：精美得分展示
    if (this.gameMode === 'single') {
      this._renderSingleModeScore(ctx, score);
    } else {
      // 多人模式：原有样式
      this._renderMultiModeScore(ctx, score);
    }

    ctx.restore();
  }

  /** 单人模式得分显示 - 使用共用方法 */
  _renderSingleModeScore(ctx, score) {
    this._renderScoreCard(ctx, score, 10, SAFE_AREA_TOP + 8);
  }

  /** 多人模式得分显示 - 与单人模式一致，房间号配套样式 */
  _renderMultiModeScore(ctx, score) {
    // 左上：得分 - 使用与单人模式相同的紫色卡片样式
    this._renderScoreCard(ctx, score, 10, SAFE_AREA_TOP + 8);

    // 右上：房间号 - 配套紫色卡片样式
    this._renderRoomCard(ctx, SAFE_AREA_TOP + 8);
  }

  /** 绘制得分卡片（单人/多人共用） */
  _renderScoreCard(ctx, score, boxX, boxY) {
    const S = UI_SCALE;
    const padding = 12 * S;
    const boxHeight = 44 * S;

    // 得分文本
    const scoreText = String(score);
    ctx.font = `bold ${Math.round(28 * S)}px Arial`;
    const scoreWidth = ctx.measureText(scoreText).width;

    // 标签文本
    ctx.font = `bold ${Math.round(14 * S)}px Arial`;
    const labelWidth = ctx.measureText('分数').width;

    // 计算总宽度
    const totalWidth = Math.max(scoreWidth, labelWidth) + padding * 2 + 8 * S;
    const boxWidth = Math.max(totalWidth, 80 * S);

    // 绘制卡片背景（圆角）
    const r = 10 * S;
    ctx.save();

    // 阴影
    ctx.shadowColor = 'rgba(0,0,0,0.4)';
    ctx.shadowBlur = 8 * S;
    ctx.shadowOffsetY = 3 * S;

    // 背景渐变 - 紫色调
    const bgGrad = ctx.createLinearGradient(boxX, boxY, boxX, boxY + boxHeight);
    bgGrad.addColorStop(0, 'rgba(139, 92, 246, 0.9)');
    bgGrad.addColorStop(1, 'rgba(124, 58, 237, 0.95)');
    ctx.fillStyle = bgGrad;

    this._roundRectPath(ctx, boxX, boxY, boxWidth, boxHeight, r);
    ctx.fill();
    ctx.shadowBlur = 0;

    // 顶部高光条
    const hlGrad = ctx.createLinearGradient(boxX, boxY, boxX + boxWidth, boxY);
    hlGrad.addColorStop(0, 'rgba(167, 139, 250, 0.6)');
    hlGrad.addColorStop(0.5, 'rgba(196, 181, 253, 0.8)');
    hlGrad.addColorStop(1, 'rgba(167, 139, 250, 0.6)');
    ctx.fillStyle = hlGrad;
    ctx.fillRect(boxX + 4 * S, boxY + 4 * S, boxWidth - 8 * S, 3 * S);

    // 边框
    ctx.strokeStyle = 'rgba(167, 139, 250, 0.5)';
    ctx.lineWidth = 1.5 * S;
    this._roundRectPath(ctx, boxX, boxY, boxWidth, boxHeight, r);
    ctx.stroke();

    ctx.restore();

    // 绘制 分数 标签
    ctx.textAlign = 'center';
    ctx.textBaseline = 'alphabetic';
    ctx.font = `bold ${Math.round(11 * S)}px Arial`;
    ctx.fillStyle = 'rgba(221, 214, 254, 0.9)';
    ctx.fillText('分数', boxX + boxWidth / 2, boxY + 18 * S);

    // 绘制分数（金色渐变效果）
    const centerX = boxX + boxWidth / 2;
    const scoreY = boxY + 38 * S;

    // 分数阴影
    ctx.fillStyle = 'rgba(0,0,0,0.3)';
    ctx.font = `bold ${Math.round(24 * S)}px Arial`;
    ctx.fillText(scoreText, centerX + 1, scoreY + 1);

    // 分数主体（金色）
    const scoreGrad = ctx.createLinearGradient(centerX - 20 * S, scoreY - 15 * S, centerX + 20 * S, scoreY + 5 * S);
    scoreGrad.addColorStop(0, '#FDE68A');
    scoreGrad.addColorStop(0.5, '#FCD34D');
    scoreGrad.addColorStop(1, '#F59E0B');
    ctx.fillStyle = scoreGrad;
    ctx.fillText(scoreText, centerX, scoreY);
  }

  /** 绘制房间号卡片（多人模式专用） */
  _renderRoomCard(ctx, boxY) {
    const S = UI_SCALE;
    const roomId = GameGlobal.databus.roomId;
    const roomLabel = String(roomId);
    const padding = 12 * S;
    const boxHeight = 44 * S;

    // 计算宽度
    ctx.font = `bold ${Math.round(14 * S)}px Arial`;
    const labelWidth = ctx.measureText('房间').width;
    ctx.font = `bold ${Math.round(20 * S)}px Arial`;
    const roomWidth = ctx.measureText(roomLabel).width;

    const totalWidth = Math.max(labelWidth, roomWidth) + padding * 2 + 8 * S;
    const boxWidth = Math.max(totalWidth, 100 * S);
    const boxX = SCREEN_WIDTH - boxWidth - 10;

    // 绘制卡片背景（圆角）
    const r = 10 * S;
    ctx.save();

    // 阴影
    ctx.shadowColor = 'rgba(0,0,0,0.4)';
    ctx.shadowBlur = 8 * S;
    ctx.shadowOffsetY = 3 * S;

    // 背景渐变 - 紫色调（与得分卡片配套）
    const bgGrad = ctx.createLinearGradient(boxX, boxY, boxX, boxY + boxHeight);
    bgGrad.addColorStop(0, 'rgba(139, 92, 246, 0.9)');
    bgGrad.addColorStop(1, 'rgba(124, 58, 237, 0.95)');
    ctx.fillStyle = bgGrad;

    this._roundRectPath(ctx, boxX, boxY, boxWidth, boxHeight, r);
    ctx.fill();
    ctx.shadowBlur = 0;

    // 顶部高光条
    const hlGrad = ctx.createLinearGradient(boxX, boxY, boxX + boxWidth, boxY);
    hlGrad.addColorStop(0, 'rgba(167, 139, 250, 0.6)');
    hlGrad.addColorStop(0.5, 'rgba(196, 181, 253, 0.8)');
    hlGrad.addColorStop(1, 'rgba(167, 139, 250, 0.6)');
    ctx.fillStyle = hlGrad;
    ctx.fillRect(boxX + 4 * S, boxY + 4 * S, boxWidth - 8 * S, 3 * S);

    // 边框
    ctx.strokeStyle = 'rgba(167, 139, 250, 0.5)';
    ctx.lineWidth = 1.5 * S;
    this._roundRectPath(ctx, boxX, boxY, boxWidth, boxHeight, r);
    ctx.stroke();

    ctx.restore();

    const centerX = boxX + boxWidth / 2;

    // 绘制 房间 标签
    ctx.textAlign = 'center';
    ctx.textBaseline = 'alphabetic';
    ctx.font = `bold ${Math.round(11 * S)}px Arial`;
    ctx.fillStyle = 'rgba(221, 214, 254, 0.9)';
    ctx.fillText('房间', centerX, boxY + 18 * S);

    // 绘制房间号（青色/蓝色调，与金色分数区分）
    const roomY = boxY + 38 * S;

    // 阴影
    ctx.fillStyle = 'rgba(0,0,0,0.3)';
    ctx.font = `bold ${Math.round(16 * S)}px Arial`;
    ctx.fillText(roomLabel, centerX + 1, roomY + 1);

    // 房间号主体（青蓝色渐变，与得分卡片区分）
    const roomGrad = ctx.createLinearGradient(centerX - 30 * S, roomY - 12 * S, centerX + 30 * S, roomY + 4 * S);
    roomGrad.addColorStop(0, '#67E8F9');
    roomGrad.addColorStop(0.5, '#22D3EE');
    roomGrad.addColorStop(1, '#06B6D4');
    ctx.fillStyle = roomGrad;
    ctx.fillText(roomLabel, centerX, roomY);
  }

  /** 游戏结束：遮罩 + 卡片 + 按钮 */
  renderGameOver(ctx, score) {
    this.drawOverlay(ctx);
    this.drawCard(ctx, score);
    this.drawRestartButton(ctx);
  }

  // ─── 内部辅助 ─────────────────────────────────────────

  /**
   * 根据得分返回点亮的星星数（1~3）
   * < 30 → 1星   30~69 → 2星   ≥ 70 → 3星
   */
  _getStarCount(score) {
    if (score >= 70) return 3;
    if (score >= 30) return 2;
    return 1;
  }

  /** 绘制圆角矩形路径（不 fill/stroke，由调用方决定） */
  _roundRectPath(ctx, x, y, w, h, r) {
    ctx.beginPath();
    ctx.moveTo(x + r, y);
    ctx.lineTo(x + w - r, y);
    ctx.arcTo(x + w, y, x + w, y + r, r);
    ctx.lineTo(x + w, y + h - r);
    ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
    ctx.lineTo(x + r, y + h);
    ctx.arcTo(x, y + h, x, y + h - r, r);
    ctx.lineTo(x, y + r);
    ctx.arcTo(x, y, x + r, y, r);
    ctx.closePath();
  }

  /** 绘制五角星 */
  _drawStar(ctx, cx, cy, outerR, innerR, color, alpha) {
    ctx.save();
    if (alpha !== undefined) ctx.globalAlpha = alpha;
    ctx.fillStyle = color;
    ctx.beginPath();
    for (let i = 0; i < 10; i++) {
      const angle = (i * Math.PI) / 5 - Math.PI / 2;
      const r = i % 2 === 0 ? outerR : innerR;
      const px = cx + r * Math.cos(angle);
      const py = cy + r * Math.sin(angle);
      if (i === 0) ctx.moveTo(px, py);
      else ctx.lineTo(px, py);
    }
    ctx.closePath();
    ctx.fill();
    ctx.restore();
  }

  // ─── 遮罩 ─────────────────────────────────────────────

  /** 径向渐变遮罩：中心略透，四周深暗 */
  drawOverlay(ctx) {
    ctx.save();
    const cx = SCREEN_WIDTH / 2;
    const cy = SCREEN_HEIGHT / 2;
    const grad = ctx.createRadialGradient(cx, cy, 60, cx, cy, SCREEN_HEIGHT * 0.75);
    grad.addColorStop(0, 'rgba(0,0,0,0.55)');
    grad.addColorStop(1, 'rgba(0,0,0,0.90)');
    ctx.fillStyle = grad;
    ctx.fillRect(0, 0, SCREEN_WIDTH, SCREEN_HEIGHT);
    ctx.restore();
  }

  // ─── 主卡片 ───────────────────────────────────────────

  drawCard(ctx, score) {
    const cardW = CARD_W;
    const cardH = CARD_H;
    const cardX = CARD_X;
    const cardY = CARD_Y;
    const S = UI_SCALE;
    const r = 24 * S;
    const cx = SCREEN_WIDTH / 2;

    ctx.save();

    // 卡片投影
    ctx.shadowColor = 'rgba(0,0,0,0.75)';
    ctx.shadowBlur = 50 * S;
    ctx.shadowOffsetY = 16 * S;

    // 卡片背景：深紫 → 深蓝
    const bgGrad = ctx.createLinearGradient(cardX, cardY, cardX, cardY + cardH);
    bgGrad.addColorStop(0, '#2d1b69');
    bgGrad.addColorStop(1, '#0f0c29');
    ctx.fillStyle = bgGrad;
    this._roundRectPath(ctx, cardX, cardY, cardW, cardH, r);
    ctx.fill();

    ctx.shadowBlur = 0;
    ctx.shadowOffsetY = 0;

    // 顶部彩色光条
    ctx.save();
    ctx.beginPath();
    ctx.moveTo(cardX + r, cardY);
    ctx.lineTo(cardX + cardW - r, cardY);
    ctx.arcTo(cardX + cardW, cardY, cardX + cardW, cardY + r, r);
    ctx.lineTo(cardX + cardW, cardY + 8 * S);
    ctx.lineTo(cardX, cardY + 8 * S);
    ctx.arcTo(cardX, cardY, cardX + r, cardY, r);
    ctx.closePath();
    const topBar = ctx.createLinearGradient(cardX, 0, cardX + cardW, 0);
    topBar.addColorStop(0, '#a855f7');
    topBar.addColorStop(0.5, '#ec4899');
    topBar.addColorStop(1, '#f97316');
    ctx.fillStyle = topBar;
    ctx.fill();
    ctx.restore();

    // 装饰散星（背景）
    this._drawStar(ctx, cardX + 36 * S, cardY + 55 * S, 13 * S, 5 * S, '#fbbf24', 0.55);
    this._drawStar(ctx, cardX + cardW - 36 * S, cardY + 55 * S, 10 * S, 4 * S, '#f472b6', 0.5);
    this._drawStar(ctx, cardX + 22 * S, cardY + 155 * S, 8 * S, 3 * S, '#60a5fa', 0.4);
    this._drawStar(ctx, cardX + cardW - 22 * S, cardY + 155 * S, 9 * S, 4 * S, '#fbbf24', 0.4);
    this._drawStar(ctx, cardX + 50 * S, cardY + 230 * S, 6 * S, 2 * S, '#a78bfa', 0.35);
    this._drawStar(ctx, cardX + cardW - 50 * S, cardY + 230 * S, 7 * S, 3 * S, '#f9a8d4', 0.35);

    // ── "游戏结束" 标题 ──────────────────────────
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';

    ctx.shadowColor = 'rgba(236,72,153,0.9)';
    ctx.shadowBlur = 22 * S;
    ctx.font = `bold ${Math.round(36 * S)}px Arial`;
    const titleGrad = ctx.createLinearGradient(cx - 90 * S, 0, cx + 90 * S, 0);
    titleGrad.addColorStop(0, '#f9a8d4');
    titleGrad.addColorStop(0.5, '#ffffff');
    titleGrad.addColorStop(1, '#c4b5fd');
    ctx.fillStyle = titleGrad;
    ctx.fillText('游戏结束', cx, cardY + 75 * S);
    ctx.shadowBlur = 0;

    // ── 渐变分割线 ────────────────────────────────
    const divGrad = ctx.createLinearGradient(cardX + 20 * S, 0, cardX + cardW - 20 * S, 0);
    divGrad.addColorStop(0, 'rgba(255,255,255,0)');
    divGrad.addColorStop(0.5, 'rgba(255,255,255,0.25)');
    divGrad.addColorStop(1, 'rgba(255,255,255,0)');
    ctx.strokeStyle = divGrad;
    ctx.lineWidth = 1;
    ctx.beginPath();
    ctx.moveTo(cardX + 20 * S, cardY + 118 * S);
    ctx.lineTo(cardX + cardW - 20 * S, cardY + 118 * S);
    ctx.stroke();

    // ── "本局得分" 标签 ───────────────────────────
    ctx.font = `${Math.round(16 * S)}px Arial`;
    ctx.fillStyle = 'rgba(255,255,255,0.55)';
    ctx.fillText('本局得分', cx, cardY + 152 * S);

    // ── 得分数字（金色光晕）────────────────────────
    ctx.shadowColor = 'rgba(251,191,36,0.65)';
    ctx.shadowBlur = 28 * S;
    ctx.font = `bold ${Math.round(76 * S)}px Arial`;
    const scoreGrad = ctx.createLinearGradient(cx - 55 * S, 0, cx + 55 * S, 0);
    scoreGrad.addColorStop(0, '#fde047');
    scoreGrad.addColorStop(0.5, '#fbbf24');
    scoreGrad.addColorStop(1, '#f59e0b');
    ctx.fillStyle = scoreGrad;
    ctx.fillText(String(score), cx, cardY + 230 * S);
    ctx.shadowBlur = 0;

    // ── 得分下方星星（按分数决定亮几颗） ─────────────
    const starY = cardY + 300 * S;
    const litCount = this._getStarCount(score);
    // 未点亮：灰暗半透明；点亮：金色
    const starConfigs = [
      { x: cx - 46 * S, outerR: 13 * S, innerR: 5 * S },
      { x: cx,          outerR: 17 * S, innerR: 7 * S },
      { x: cx + 46 * S, outerR: 13 * S, innerR: 5 * S },
    ];
    starConfigs.forEach((cfg, i) => {
      if (i < litCount) {
        this._drawStar(ctx, cfg.x, starY, cfg.outerR, cfg.innerR, '#fbbf24');
      } else {
        this._drawStar(ctx, cfg.x, starY, cfg.outerR, cfg.innerR, '#4a4a6a', 0.55);
      }
    });

    ctx.restore();
  }

  // ─── 重新开始按钮 ──────────────────────────────────────

  drawRestartButton(ctx) {
    const { startX, startY } = this.btnArea;
    const r = BTN_H / 2;

    ctx.save();

    // 按钮外发光
    ctx.shadowColor = 'rgba(249,115,22,0.55)';
    ctx.shadowBlur = 22 * UI_SCALE;
    ctx.shadowOffsetY = 5 * UI_SCALE;

    // 渐变按钮底色（橙 → 粉）
    const btnGrad = ctx.createLinearGradient(startX, startY, startX + BTN_W, startY);
    btnGrad.addColorStop(0, '#f97316');
    btnGrad.addColorStop(1, '#ec4899');
    ctx.fillStyle = btnGrad;
    this._roundRectPath(ctx, startX, startY, BTN_W, BTN_H, r);
    ctx.fill();

    ctx.shadowBlur = 0;
    ctx.shadowOffsetY = 0;

    // 按钮顶部高光条
    ctx.save();
    this._roundRectPath(ctx, startX, startY, BTN_W, BTN_H, r);
    ctx.clip();
    const hlGrad = ctx.createLinearGradient(startX, startY, startX, startY + BTN_H * 0.55);
    hlGrad.addColorStop(0, 'rgba(255,255,255,0.28)');
    hlGrad.addColorStop(1, 'rgba(255,255,255,0)');
    ctx.fillStyle = hlGrad;
    ctx.fillRect(startX, startY, BTN_W, BTN_H * 0.55);
    ctx.restore();

    // 按钮文字
    ctx.textAlign = 'center';
    ctx.textBaseline = 'middle';
    ctx.shadowColor = 'rgba(0,0,0,0.35)';
    ctx.shadowBlur = 4 * UI_SCALE;
    ctx.shadowOffsetY = 2 * UI_SCALE;
    ctx.font = `bold ${Math.round(20 * UI_SCALE)}px Arial`;
    ctx.fillStyle = '#ffffff';
    ctx.fillText('重新开始', SCREEN_WIDTH / 2, startY + BTN_H / 2);

    ctx.restore();
  }

  // ─── 退出按钮（单人模式右上角）─────────────────────────

  _renderQuitBtn(ctx) {
    const { x, y, w, h } = this._quitBtn;
    const cx = x + w / 2;
    const cy = y + h / 2;
    const r = w / 2;

    ctx.save();

    // 半透明圆形背景
    ctx.fillStyle = 'rgba(0, 0, 0, 0.35)';
    ctx.beginPath();
    ctx.arc(cx, cy, r, 0, Math.PI * 2);
    ctx.fill();

    // 边框
    ctx.strokeStyle = 'rgba(255, 255, 255, 0.25)';
    ctx.lineWidth = 1.5;
    ctx.beginPath();
    ctx.arc(cx, cy, r, 0, Math.PI * 2);
    ctx.stroke();

    // X 图标
    const s = 9 * UI_SCALE;
    ctx.strokeStyle = 'rgba(255, 255, 255, 0.8)';
    ctx.lineWidth = 2.5 * UI_SCALE;
    ctx.lineCap = 'round';
    ctx.beginPath();
    ctx.moveTo(cx - s, cy - s);
    ctx.lineTo(cx + s, cy + s);
    ctx.moveTo(cx + s, cy - s);
    ctx.lineTo(cx - s, cy + s);
    ctx.stroke();

    ctx.restore();
  }

  // ─── 触摸事件 ──────────────────────────────────────────

  touchEventHandler(event) {
    const { clientX, clientY } = event.touches[0];

    // 单人模式游戏中：退出按钮
    if (this.gameMode === 'single' && !GameGlobal.databus.isGameOver) {
      const { x, y, w, h } = this._quitBtn;
      if (clientX >= x && clientX <= x + w && clientY >= y && clientY <= y + h) {
        wx.showModal({
          title: '退出游戏',
          content: '确定要退出当前游戏吗？',
          confirmText: '退出',
          cancelText: '继续',
          success: (res) => {
            if (res.confirm) {
              this.emit('restart');
            }
          },
        });
        return;
      }
    }

    if (GameGlobal.databus.isGameOver) {
      const { startX, startY, endX, endY } = this.btnArea;
      if (clientX >= startX && clientX <= endX && clientY >= startY && clientY <= endY) {
        this.emit('restart');
      }
    }
  }
}
