import 'dart:async';
import 'dart:io';
import 'dart:math';
import 'dart:ui' as ui;

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';
import 'package:puzzleweave/audio/jc_audio_controller.dart';
import 'package:puzzleweave/config/device.dart';
import 'package:puzzleweave/firebase/adjust_helper.dart';
import 'package:puzzleweave/firebase/firebase_helper.dart';
import 'package:puzzleweave/l10n/app_localizations.dart';
import 'package:puzzleweave/models/download.dart';
import 'package:puzzleweave/models/items.dart';
import 'package:puzzleweave/persistence/persistence.dart';
import 'package:puzzleweave/play/board.dart';
import 'package:puzzleweave/play/board_painter.dart';
import 'package:puzzleweave/play/confetti_layer.dart';
import 'package:puzzleweave/play/overlayer.dart';
import 'package:puzzleweave/play/piece.dart';
import 'package:puzzleweave/rating/rating_helper.dart';
import 'package:puzzleweave/rating/rating_utils.dart';
import 'package:puzzleweave/settings/settings_controller.dart';
import 'package:puzzleweave/settings/settings_dialog.dart';
import 'package:puzzleweave/skin/skin.dart';
import 'package:puzzleweave/statistics/statistics.dart';
import 'package:puzzleweave/utils/mybutton.dart';
import 'package:puzzleweave/utils/utils.dart';
import 'package:puzzleweave/utils/memory_monitor.dart';
import 'package:vector_math/vector_math.dart' as vmath;
import 'package:vibration/vibration.dart';
import '../ads/ads_state.dart';

final Logger _log = Logger('board_play.dart');

enum MoveType { group, single }

enum Action { revert, swap }

class BoardPlay extends StatefulWidget {
  final ListItem item;
  final bool firstRun;
  final bool reset;
  final String tag;

  const BoardPlay({super.key, required this.item, this.firstRun = false, this.reset = false, this.tag = 'home'});

  @override
  State<StatefulWidget> createState() => _BoardPlayState();

  static PageRouteBuilder buildRoute(ListItem item, {bool firstRun = false, bool reset = false}) {
    return PageRouteBuilder(
      pageBuilder: (context, animation, secondaryAnimation) => BoardPlay(item: item, firstRun: firstRun, reset: reset),
      transitionsBuilder: (context, animation, secondaryAnimation, child) => FadeTransition(opacity: animation, child: child),
    );
  }
}

class _BoardPlayState extends AdsState<BoardPlay> with TickerProviderStateMixin {
  final GlobalKey boardKey = GlobalKey();
  Board? board;
  bool _isLoading = true;
  int progress = 0;
  bool isDownloadSlow = false;
  late Timer timer;

  late ItemLoader itemLoader;
  late JcAudioController audio;
  late SettingsController settings;
  late ConfettiLayer confettiLayer;

  ui.Image? _fingerImage;
  OverLayer? _overLayer;
  Piece? _draggingPiece;
  List<MoveItem>? moveItems;

  // ✅ 优化：资源清理定时器
  Timer? _resourceCleanupTimer;

  // 动画控制器
  late AnimationController _moveAnimationController;
  late AnimationController _mergeAnimationController;
  late Animation<double> _mergeScaleAnimation;
  List<PieceGroup>? _mergeGroups;

  bool showDealing = true;
  late AnimationController _prepareAnimationController;
  late AnimationController dealingAnimationController;
  late AnimationController flipAnimationController;
  late Animation<double> _dealingAnimation;

  Timer? _dealingPeriodicTimer;
  int _dealingCount = 0;

  late AnimationController _successAnimationController;
  late Animation<double> _offsetAnimation;
  late Animation<double> _bottomSlideAnimation;
  late Animation<double> _topSlideAnimation;

  late AnimationController _hardModeBannerController;
  late Animation<double> _bannerScaleAnimation;
  late Animation<double> _bannerFadeAnimation;
  bool _showHardModeBanner = false;

  // ✅ 保留：重试标记，防止无限循环
  bool _hasRetried = false;

  @override
  initState() {
    super.initState();

    MemoryMonitor.logMemoryUsage('BoardPlay initState');

    final Device device = context.read<Device>();

    itemLoader = ItemLoader.load(widget.item, device.suggestedQuality);
    _onProgressUpdate();
    itemLoader.progress.addListener(_onProgressUpdate);

    timer = Timer(const Duration(seconds: 5), () {
      if (mounted && progress < 50) {
        if (progress <= 1) {
          Fluttertoast.showToast(
            msg: AppLocalizations.of(context)!.networkNotGood,
            toastLength: Toast.LENGTH_SHORT,
            gravity: ToastGravity.CENTER,
            timeInSecForIosWeb: 1,
            backgroundColor: SkinHelper.slotBorderColor,
            textColor: Colors.white,
            fontSize: 16.0,
          );
          Navigator.pop(context);
        } else {
          setState(() => isDownloadSlow = true);
        }
      }
    });

    audio = context.read<JcAudioController>();
    settings = context.read<SettingsController>();
    confettiLayer = ConfettiLayer(this);

    Future.delayed(Duration.zero, () {
      if (mounted) confettiLayer.setup(context);
    });

    _initAnimations();

    try {
      _init();
    } catch (error) {
      _log.info('board init error: $error');
      Fluttertoast.showToast(
        msg: AppLocalizations.of(context)!.networkNotGood,
        toastLength: Toast.LENGTH_SHORT,
        gravity: ToastGravity.CENTER,
        timeInSecForIosWeb: 1,
        backgroundColor: SkinHelper.slotBorderColor,
        textColor: Colors.white,
        fontSize: 16.0,
      );
      Navigator.pop(context);
    }
  }

  void _initAnimations() {
    final Device device = context.read<Device>();

    _moveAnimationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 200));
    _moveAnimationController.addListener(_moveAnimationListener);
    _moveAnimationController.addStatusListener(_moveAnimationStatusListener);

    _mergeAnimationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 300));
    _mergeAnimationController.addListener(_mergeAnimationListener);
    _mergeAnimationController.addStatusListener(_mergeAnimationStatusListener);
    _mergeScaleAnimation = TweenSequence<double>([
      TweenSequenceItem(tween: Tween<double>(begin: 1.0, end: 1.06), weight: 50),
      TweenSequenceItem(tween: Tween<double>(begin: 1.06, end: 1.0), weight: 50),
    ]).animate(CurvedAnimation(parent: _mergeAnimationController, curve: Curves.easeInOut));

    _prepareAnimationController = AnimationController(vsync: this, duration: const Duration(milliseconds: 800));
    _prepareAnimationController.addListener(_prepareAnimationListener);
    _prepareAnimationController.addStatusListener(_prepareAnimationStatusListener);

    dealingAnimationController = AnimationController(vsync: this);
    _dealingAnimation = CurvedAnimation(parent: dealingAnimationController, curve: Curves.linear);
    dealingAnimationController.addListener(_dealingAnimationListener);
    dealingAnimationController.addStatusListener(_dealingAnimationStatusListener);

    flipAnimationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
    flipAnimationController.addListener(_flipAnimationListener);
    flipAnimationController.addStatusListener(_flipAnimationStatusListener);

    _successAnimationController = AnimationController(vsync: this, duration: Duration(milliseconds: 500));
    final deltaY = (device.targetRect.top - device.appBarHeight) / 3;
    _offsetAnimation = Tween<double>(begin: 0.0, end: -deltaY).animate(_successAnimationController);
    _bottomSlideAnimation = Tween<double>(
      begin: -500,
      end: device.screenSize.height - device.targetRect.bottom + deltaY - 60,
    ).animate(CurvedAnimation(parent: _successAnimationController, curve: Curves.easeOut));
    _topSlideAnimation = Tween<double>(
      begin: -200,
      end: device.targetRect.top - deltaY - 70,
    ).animate(CurvedAnimation(parent: _successAnimationController, curve: Curves.easeOut));
    _successAnimationController.addListener(_successAnimationListener);
    _successAnimationController.addStatusListener(_successAnimationStatusListener);

    _hardModeBannerController = AnimationController(vsync: this, duration: const Duration(milliseconds: 1500));
    _bannerScaleAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
      CurvedAnimation(
        parent: _hardModeBannerController,
        curve: const Interval(0.0, 0.4, curve: Curves.easeOut),
      ),
    );
    _bannerFadeAnimation = Tween<double>(begin: 1.0, end: 0.0).animate(
      CurvedAnimation(
        parent: _hardModeBannerController,
        curve: const Interval(0.3, 1.0, curve: Curves.easeIn),
      ),
    );
    _hardModeBannerController.addStatusListener((status) {
      if (status == AnimationStatus.completed) {
        if (mounted) setState(() => _showHardModeBanner = false);
      }
    });
  }

  _onProgressUpdate() {
    progress = (itemLoader.progress.value * 100).ceil();
    _log.info('onProgressUpdate: progress=$progress');
    setState(() {});
  }

  void saveProgress() async {
    _log.info('saveProgress');
    if (board != null && board!.isAllDone == false) {
      await saveJson(widget.item.jsonPath, board!.toJson());
    }
  }

  // ✅ 优化后的 _init() 方法 - 保留图片损坏检测和重试逻辑
  _init() async {
    Device device = context.read<Device>();

    setState(() => _isLoading = true);

    try {
      final dpr = device.effectivePixelRatio;
      final targetRect = device.targetRect;
      final bestImageSize = device.bestImageSize;

      // ✅ 1. 尝试获取并解码图片（保留原有的错误处理逻辑）
      ui.Image image;
      try {
        image = await itemLoader.getImageBySize(bestImageSize.width.round(), bestImageSize.height.round());
      } catch (e) {
        _log.severe('Image decode failed: $e. Possible corrupted file.');

        // ✅ 保留：如果解码失败且还没重试过，尝试删除缓存重来
        if (!_hasRetried) {
          _hasRetried = true;
          final file = await localFile(widget.item.cachePath);
          if (await file.exists()) {
            await file.delete();
            _log.warning('Deleted corrupted cache file: ${widget.item.cachePath}');
          }

          // 重新初始化下载逻辑
          itemLoader = ItemLoader.load(widget.item, device.suggestedQuality);
          // 递归调用一次
          return _init();
        } else {
          // 已经重试过还是失败，抛出异常进入 catch 块
          throw Exception("Image data invalid after retry");
        }
      }

      _log.info('imageSize: (${image.width},${image.height}), bestImageSize: ($bestImageSize)');

      // ✅ 2. 加载扑克背面图片
      final Size bestCardImageSize = Size(targetRect.width * dpr / widget.item.rows, targetRect.height * dpr / widget.item.cols);
      final ByteData cardData = await rootBundle.load(widget.item.hard ? 'assets/images/backcard_red.png' : 'assets/images/backcard_blue.png');

      ui.Image cardImage;
      final ui.Codec cardCodec = await ui.instantiateImageCodec(
        cardData.buffer.asUint8List(),
        targetWidth: bestCardImageSize.width.round(),
        targetHeight: bestCardImageSize.height.round(),
      );
      final ui.FrameInfo cardFrameInfo = await cardCodec.getNextFrame();
      cardImage = cardFrameInfo.image;

      // ✅ 3. 构建或恢复 Board 实例
      if (widget.reset) {
        board = await Board.create(this, image, cardImage, widget.item.rows, widget.item.cols, widget.item.hard, targetRect, device);
      } else {
        final jsonFile = await localFile(widget.item.jsonPath);
        if (await jsonFile.exists()) {
          showDealing = false;
          board = await Board.restore(this, image, cardImage, widget.item.rows, widget.item.cols, widget.item.hard, targetRect, device, widget.item.jsonPath);
        } else {
          board = await Board.create(this, image, cardImage, widget.item.rows, widget.item.cols, widget.item.hard, targetRect, device);
          _reportLevelStart();
        }
      }

      // 4. 初始化准备
      board!.prepare();
      _loadFingerImageAndSetupHint();

      if (mounted) {
        setState(() => _isLoading = false);
      }

      // 5. 启动入场动画
      if (showDealing) {
        _prepareAnimationController.forward(from: 0.0);
      } else {
        board!.start();
      }
    } catch (error) {
      _log.severe('Board _init critical error: $error');

      if (mounted) {
        Fluttertoast.showToast(
          msg: AppLocalizations.of(context)!.networkNotGood,
          toastLength: Toast.LENGTH_LONG,
          gravity: ToastGravity.CENTER,
          backgroundColor: SkinHelper.slotBorderColor,
          textColor: Colors.white,
        );
        Navigator.pop(context);
      }
    }
  }

  _reportLevelStart() {
    FirebaseHelper.logEvent("level_start", {'id': widget.item.id, 'level': data.currentLevel + 1});
    AdjustHelper.trackEvent(AdjustHelper.levelStartToken);
    Statistics.postEvent({
      "project_id": Persistence().projectId,
      "user_id": Persistence().uuid,
      "library_name": Persistence().libraryName,
      "library_version": Persistence().packageVersion,
      "name": 'level_start',
      "tab_source": widget.tag,
      "sku_id": widget.item.id,
    });
  }

  Future<void> _loadFingerImageAndSetupHint() async {
    if (!widget.firstRun) return;

    try {
      _fingerImage = await loadUiImageFromAsset('assets/images/finger.png');
    } catch (e) {
      _log.severe('Failed to load assets/images/finger.png: $e');
      return;
    }

    if (!mounted || board == null) return;

    _overLayer = OverLayer(board!, this);
    _overLayer!.setup(context);

    if (widget.firstRun) {
      Future.delayed(const Duration(seconds: 1), () => hint());
      Future.delayed(const Duration(seconds: 3), () {
        if (!mounted) return;
        if (_overLayer != null && _overLayer!.isHinting) {
          Fluttertoast.showToast(
            msg: AppLocalizations.of(context)!.moveToComplete,
            toastLength: Toast.LENGTH_SHORT,
            gravity: ToastGravity.BOTTOM,
            timeInSecForIosWeb: 1,
            backgroundColor: SkinHelper.slotBorderColor,
            textColor: Colors.white,
            fontSize: 16.0,
          );
        }
      });
    }
  }

  hint() async {
    if (_fingerImage == null || _overLayer == null || board == null || board!.status != BoardStatus.playing) return;

    double fingerSize = board!.pieceLogicalWidth / 2.5;

    final Offset centerStart = Offset(
      board!.targetRect.topLeft.dx + board!.pieceLogicalWidth,
      board!.targetRect.topLeft.dy + board!.pieceLogicalHeight * 5 / 2,
    );
    final Offset centerEnd = Offset(board!.targetRect.topLeft.dx + board!.pieceLogicalWidth, board!.targetRect.topLeft.dy + board!.pieceLogicalHeight * 3 / 2);

    final rectStart = Rect.fromCenter(center: centerStart, width: fingerSize, height: fingerSize);
    final rectEnd = Rect.fromCenter(center: centerEnd, width: fingerSize, height: fingerSize);

    final hintItem = HintItem(_fingerImage!, rectStart, rectEnd);
    _overLayer?.doHint(hintItem);
  }

  // 🔥 关键修复：成功回调，延迟资源清理到页面退出时
  _onSuccess() {
    _log.info('success! 游戏完成！');
    MemoryMonitor.logMemoryUsage('Level completed');

    data.workDone(widget.item);

    board!.success();
    audio.playSfx(SfxType.success);
    confettiLayer.play();

    _successAnimationController.forward(from: 0.0);
    setState(() {});

    // 数据上报
    FirebaseHelper.logEvent("level_done", {'id': widget.item.id, 'level': data.currentLevel});
    AdjustHelper.trackEvent(AdjustHelper.levelDoneToken);
    Statistics.postEvent({
      "project_id": Persistence().projectId,
      "user_id": Persistence().uuid,
      "library_name": Persistence().libraryName,
      "library_version": Persistence().packageVersion,
      "name": 'level_done',
      "tab_source": widget.tag,
      "sku_id": widget.item.id,
    });

    // 里程碑上报
    if (data.currentLevel == 3) {
      FirebaseHelper.logEvent("level_done_3", {});
      AdjustHelper.trackEvent(AdjustHelper.levelDone3Token);
    } else if (data.currentLevel == 10) {
      FirebaseHelper.logEvent("level_done_10", {});
      AdjustHelper.trackEvent(AdjustHelper.levelDone10Token);
    } else if (data.currentLevel == 20) {
      FirebaseHelper.logEvent("level_done_20", {});
      AdjustHelper.trackEvent(AdjustHelper.levelDone20Token);
    } else if (data.currentLevel == 30) {
      FirebaseHelper.logEvent("level_done_30", {});
      AdjustHelper.trackEvent(AdjustHelper.levelDone30Token);
    }
  }

  // 动画监听器方法（保持原有逻辑）
  void _successAnimationListener() {
    final delta = _offsetAnimation.value;
    board!.finalRect = board!.targetRect.translate(0, delta);
    board!.invalidate();
  }

  void _successAnimationStatusListener(AnimationStatus status) async {
    if (status == AnimationStatus.completed) {
      Future.delayed(Duration(seconds: 1), () async {
        if (!mounted) return;
        final bool shouldShowRateDialog = await RatingHelper.shouldShowRateDialog(data.currentLevel);
        if (shouldShowRateDialog && mounted) {
          showRateDialog(context);
        }
      });
    }
  }

  // 其他动画监听器方法保持原有逻辑...
  void _dealingAnimationListener() {
    if (board == null) return;
    final currentTime = _dealingAnimation.value * _totalDealingDuration;
    for (int i = 0; i < board!.pieces.length - 1; i++) {
      final piece = board!.pieces[i];
      final startTime = i * _dealingPieceInterval;
      final duration = _dealingPieceDuration;
      if (currentTime < startTime) continue;
      double progress = (currentTime - startTime) / duration;
      progress = progress.clamp(0.0, 1.0);
      final startTransform = board!.getBottomRightTransform();
      final endTransform = board!.getTransformByCoordinate(piece.curRow, piece.curCol);
      final tween = Matrix4Tween(begin: startTransform, end: endTransform);
      piece.transform = tween.lerp(progress);
    }
    board!.invalidate();
  }

  void _dealingAnimationStatusListener(AnimationStatus status) {
    if (status == AnimationStatus.completed) {
      board!.resetAllPieces();
      board!.shuffle(ShuffleStep.flipping);
      flipAnimationController.forward(from: 0.0);
      audio.playSfx(SfxType.flip);
    }
  }

  void _flipAnimationListener() {
    if (board == null) return;
    final flipValue = flipAnimationController.value;
    for (final piece in board!.pieces) {
      final angle = flipValue * pi;
      final targetTranslate = board!.getTransformByCoordinate(piece.curRow, piece.curCol);
      piece.updateFlipTransform(angle, targetTranslate);
    }
    board!.invalidate();
  }

  void _flipAnimationStatusListener(AnimationStatus status) {
    if (status == AnimationStatus.completed) {
      board!.resetAllPieces();
      board!.rebuildAllGroups();
      final mergeGroups = board!.compareAllGroups();
      if (mergeGroups.isNotEmpty) {
        _log.info('Merge animation start for ${mergeGroups.length} groups.');
        _mergeGroups = mergeGroups;
        _mergeAnimationController.forward(from: 0.0);
        audio.playSfx(SfxType.pop);
      }
      board!.start();
    }
  }

  void _moveAnimationListener() {
    if (moveItems == null || moveItems!.isEmpty) return;
    for (var item in moveItems!) {
      item.move();
    }
    board!.invalidate();
  }

  void _moveAnimationStatusListener(AnimationStatus status) {
    if (status == AnimationStatus.completed) {
      if (moveItems != null) {
        bool needRebuildGroup = moveItems!.any((item) => item.action == Action.swap);
        for (var item in moveItems!) {
          item.stop();
        }
        if (needRebuildGroup) {
          board!.backupAllGroups();
          board!.rebuildAllGroups();
          final mergeGroups = board!.compareAllGroups();
          if (mergeGroups.isNotEmpty) {
            _log.info('Merge animation start for ${mergeGroups.length} groups.');
            _mergeGroups = mergeGroups;
            _mergeAnimationController.forward(from: 0.0);
            audio.playSfx(SfxType.pop);
            moveItems = null;
            return;
          }
        }
        moveItems = null;
      }
    }
  }

  void _mergeAnimationListener() {
    if (_mergeGroups == null || _mergeGroups!.isEmpty || board == null) return;
    final double scale = _mergeScaleAnimation.value;
    for (var group in _mergeGroups!) {
      final groupCenter = group.center;
      for (var piece in group.pieces) {
        final baseTransform = board!.getTransformByCoordinate(piece.curRow, piece.curCol);
        final pieceTopLeft = Offset(baseTransform.storage[12], baseTransform.storage[13]);
        final offsetToCenter = groupCenter - pieceTopLeft;
        final scaleMatrix = vmath.Matrix4.identity()
          ..translate(offsetToCenter.dx, offsetToCenter.dy)
          ..scale(scale, scale, 1.0)
          ..translate(-offsetToCenter.dx, -offsetToCenter.dy);
        piece.transform = baseTransform * scaleMatrix;
      }
    }
    board!.invalidate();
  }

  void _mergeAnimationStatusListener(AnimationStatus status) {
    if (status == AnimationStatus.completed) {
      if (_mergeGroups != null && _mergeGroups!.isNotEmpty) {
        for (var group in _mergeGroups!) {
          for (var piece in group.pieces) {
            piece.transform = board!.getTransformByCoordinate(piece.curRow, piece.curCol);
          }
        }
      }
      _mergeGroups = null;
      if (board!.checkWinCondition()) {
        _onSuccess();
      }
      board!.invalidate();
    }
  }

  void _prepareAnimationListener() {
    board!.invalidate();
  }

  void _prepareAnimationStatusListener(AnimationStatus status) {
    if (status == AnimationStatus.completed) {
      if (board != null && board!.hard == true) {
        setState(() => _showHardModeBanner = true);
        _hardModeBannerController.forward(from: 0.0);
      }
      board!.setAllPieceToBottomRight();
      board!.shuffle(ShuffleStep.dealing);
      dealingAnimationController.duration = Duration(milliseconds: _totalDealingDuration);
      dealingAnimationController.forward(from: 0.0);
      audio.playSfx(SfxType.card);
      _dealingPeriodicTimer = Timer.periodic(Duration(milliseconds: 130), (timer) {
        if (mounted) {
          _dealingCount++;
          if (_dealingCount >= (_totalDealingDuration / 130) - 2) {
            timer.cancel();
          } else {
            audio.playSfx(SfxType.card);
          }
        }
      });
    }
  }

  // 发牌动画参数
  int get _dealingPieceInterval {
    if (board!.rows <= 3) return 90;
    if (board!.rows == 4) return 80;
    if (board!.rows == 5) return 70;
    if (board!.rows == 6) return 50;
    return 50;
  }

  int get _dealingPieceDuration {
    if (board!.rows <= 3) return 400;
    if (board!.rows == 4) return 300;
    if (board!.rows == 5) return 200;
    if (board!.rows == 6) return 150;
    return 100;
  }

  int get _totalDealingDuration => (board!.pieces.length - 1) * _dealingPieceInterval + _dealingPieceDuration;

  @override
  void didChangeDependencies() async {
    super.didChangeDependencies();
    _log.info("didChangeDependencies");
  }

  // ✅ 优化：增强资源释放顺序和完整性
  @override
  dispose() {
    MemoryMonitor.logMemoryUsage('BoardPlay dispose (before)');
    _log.info('dispose - starting cleanup');

    // ✅ 1. 先停止所有动画（防止回调访问已销毁的资源）
    _moveAnimationController.stop();
    _mergeAnimationController.stop();
    _prepareAnimationController.stop();
    dealingAnimationController.stop();
    flipAnimationController.stop();
    _successAnimationController.stop();
    _hardModeBannerController.stop();

    // ✅ 2. 取消所有定时器
    timer.cancel();
    _dealingPeriodicTimer?.cancel();
    _resourceCleanupTimer?.cancel();

    // ✅ 3. 清理原生 Banner
    cleanBanner();

    // ✅ 4. 移除监听器
    itemLoader.progress.removeListener(_onProgressUpdate);

    _moveAnimationController.removeListener(_moveAnimationListener);
    _moveAnimationController.removeStatusListener(_moveAnimationStatusListener);
    _mergeAnimationController.removeListener(_mergeAnimationListener);
    _mergeAnimationController.removeStatusListener(_mergeAnimationStatusListener);
    _prepareAnimationController.removeListener(_prepareAnimationListener);
    _prepareAnimationController.removeStatusListener(_prepareAnimationStatusListener);
    dealingAnimationController.removeListener(_dealingAnimationListener);
    dealingAnimationController.removeStatusListener(_dealingAnimationStatusListener);
    flipAnimationController.removeListener(_flipAnimationListener);
    flipAnimationController.removeStatusListener(_flipAnimationStatusListener);
    _successAnimationController.removeListener(_successAnimationListener);
    _successAnimationController.removeStatusListener(_successAnimationStatusListener);

    // ✅ 5. 释放动画控制器
    _moveAnimationController.dispose();
    _mergeAnimationController.dispose();
    _prepareAnimationController.dispose();
    dealingAnimationController.dispose();
    flipAnimationController.dispose();
    _successAnimationController.dispose();
    _hardModeBannerController.dispose();

    // ✅ 6. 清空动画数据
    moveItems = null;
    _mergeGroups = null;

    // ✅ 7. 释放其他资源
    confettiLayer.dispose();
    _overLayer?.destroy();
    _overLayer = null;

    // ✅ 8. 最后释放 Board（最大的内存占用）
    board?.dispose();
    board = null;

    MemoryMonitor.logMemoryUsage('BoardPlay dispose (after)');
    _log.info('dispose - cleanup complete');
    super.dispose();
  }

  @override
  onInactive() {
    super.onInactive();
    saveProgress();
  }

  // 🔥 优化：页面退出时先保存再清理
  void _onWillPop(bool didPop, dynamic result) async {
    _log.info('board play will pop, didPop=$didPop, result=$result');

    if (didPop) return;

    // 立即停止所有动画，防止在销毁过程中动画还在执行 setState
    _moveAnimationController.stop();
    _mergeAnimationController.stop();
    _prepareAnimationController.stop();
    dealingAnimationController.stop();

    // 🔥 先保存进度，再清理资源
    saveProgress();

    // 延迟清理，确保保存完成
    Future.delayed(const Duration(milliseconds: 100)).then((_) {
      if (board != null) {
        _log.info('Cleaning up board resources on page exit');
        board!.dispose();
        board = null;
      }
    });

    if (!mounted) return;
    Navigator.of(context).pop(result);
  }

  @override
  Widget build(BuildContext context) {
    Device device = context.read<Device>();

    return PopScope(
      canPop: false,
      onPopInvokedWithResult: _onWillPop,
      child: Scaffold(
        body: Stack(
          children: <Widget>[
            if (!_isLoading) Positioned.fill(child: _buildPuzzleCanvas(device.screenSize.width, device.screenSize.height)),
            if (board == null || board!.status != BoardStatus.success) Positioned(top: 0, left: 0, right: 0, child: appBar),
            Positioned(
              bottom: 0,
              left: 0,
              right: 0,
              child: SafeArea(
                child: SizedBox(
                  height: context.read<Device>().bannerHeight,
                  width: double.infinity,
                  child: isBannerVisible ? getBanner('playBottom') : const SizedBox.shrink(),
                ),
              ),
            ),
            successBanner,
            nextButton,
            // Release模式下显示内存信息
            // Positioned(top: 100, right: 10, child: MemoryMonitor.getMemoryWidget()),
            if (_isLoading)
              Positioned.fill(
                child: Container(
                  color: SkinHelper.wholeBgColor,
                  child: const Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(Colors.white))),
                ),
              ),
          ],
        ),
      ),
    );
  }

  Widget get appBar => SafeArea(
    child: Padding(
      padding: const EdgeInsets.symmetric(horizontal: 30.0, vertical: 10.0),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          const SizedBox(width: 30),
          Text(
            board != null && board!.status == BoardStatus.success
                ? AppLocalizations.of(context)!.levelPass
                : '${AppLocalizations.of(context)!.level} ${data.currentLevel + 1}',
            style: const TextStyle(color: Colors.white, fontSize: 20, fontWeight: FontWeight.w600),
          ),
          SizedBox(
            width: 30,
            height: 30,
            child: IconButton(
              icon: const Icon(Icons.settings, color: Colors.white, size: 22),
              iconSize: 22,
              padding: EdgeInsets.zero,
              onPressed: () {
                audio.playSfx(SfxType.click);
                saveProgress();
                Navigator.push(context, SettingsDialog.buildRoute(showReturn: true, showRestart: true, item: widget.item));
              },
              style: ButtonStyle(
                backgroundColor: WidgetStateProperty.all(SkinHelper.slotBorderColor),
                shape: WidgetStateProperty.all(RoundedRectangleBorder(borderRadius: BorderRadius.circular(15))),
                minimumSize: WidgetStateProperty.all(const Size(30, 30)),
                maximumSize: WidgetStateProperty.all(const Size(30, 30)),
              ),
            ),
          ),
        ],
      ),
    ),
  );

  Widget _buildPuzzleCanvas(double width, double height) {
    // ✅ 此时如果为了播广告已经把 board 释放了，直接返回空，避免任何 GPU 绘制指令
    if (board == null) {
      return Container(color: SkinHelper.wholeBgColor);
    }

    return RepaintBoundary(
      child: CustomPaint(
        painter: BoardPainter(board: board!, prepareAnimation: _prepareAnimationController),
        size: Size(width, height),
        child: GestureDetector(
          key: boardKey,
          onPanStart: _onPanStart,
          onPanUpdate: _onPanUpdate,
          onPanEnd: _onPanEnd,
          child: board != null && board!.hard && _showHardModeBanner ? _hardModeBanner : Container(color: Colors.transparent),
        ),
      ),
    );
  }

  Widget get _hardModeBanner => Center(
    child: AnimatedBuilder(
      animation: _hardModeBannerController,
      builder: (context, child) {
        return FadeTransition(
          opacity: _bannerFadeAnimation,
          child: ScaleTransition(scale: _bannerScaleAnimation, child: child),
        );
      },
      child: Container(
        width: double.infinity,
        height: 60,
        margin: const EdgeInsets.symmetric(horizontal: 10),
        decoration: BoxDecoration(
          color: Colors.red,
          borderRadius: BorderRadius.circular(10),
          border: Border.all(color: const Color.fromARGB(255, 247, 143, 135), width: 2),
          boxShadow: [BoxShadow(color: Color.fromRGBO(0, 0, 0, 0.3), blurRadius: 5, offset: const Offset(0, 3))],
        ),
        child: Center(
          child: Text(
            AppLocalizations.of(context)!.hardMode,
            style: TextStyle(color: Colors.white, fontSize: 30, fontWeight: FontWeight.bold),
          ),
        ),
      ),
    ),
  );

  bool _isExiting = false;
  Widget get nextButton {
    Device device = context.read<Device>();
    return AnimatedBuilder(
      animation: _bottomSlideAnimation,
      builder: (context, child) {
        return AnimatedPositioned(
          duration: _successAnimationController.duration!,
          bottom: _bottomSlideAnimation.value,
          left: (device.screenSize.width - 200) / 2,
          child: child!,
        );
      },
      child: MyElevatedButton(
        width: 200,
        borderRadius: BorderRadius.circular(20),
        gradient: LinearGradient(colors: [SkinHelper.coreBgColor, SkinHelper.slotBorderColor]),
        onPressed: () async {
          if (_isExiting) return;
          _isExiting = true;

          audio.playSfx(SfxType.click);

          // 🔥 策略：在广告弹出前，先主动销毁最占资源的 Board 和 UI Image
          // 此时用户已经看到成功界面，底层画布即使变白也没关系（被动画层挡住了）
          // MemoryMonitor().manualCleanup(); // 广告前清理
          if (board != null) {
            _log.info('Pre-Ad Cleanup: Disposing board to free GPU memory');
            board!.dispose();
            board = null;
          }

          // 尝试直接pop up, 不等待返回结果，避免用户等待广告结束后才看到界面响应
          if (data.currentLevel % 25 != 0) {
            showInterstitialAd('level_done', widget.item.id, data.currentLevel - 1);
          }

          _safePop(result: true); // 返回true表示关卡完成
        },
        child: Text(AppLocalizations.of(context)!.next, style: TextStyle(color: Colors.white, fontSize: 20)),
      ),
    );
  }

  void _safePop({bool result = true}) {
    if (!mounted) return;
    final navigator = Navigator.of(context);
    if (navigator.canPop()) {
      navigator.pop(result);
    }
  }

  Widget get successBanner {
    Device device = context.read<Device>();
    final bannerWidth = device.screenSize.width - 40 * 2;
    final bannerHeight = 60.0;

    return AnimatedBuilder(
      animation: _bottomSlideAnimation,
      builder: (context, child) {
        return AnimatedPositioned(duration: _successAnimationController.duration!, top: _topSlideAnimation.value, left: 40, child: child!);
      },
      child: SizedBox(
        width: bannerWidth,
        height: bannerHeight,
        child: Stack(
          children: [
            Image.asset(
              'assets/images/banner.png',
              width: double.infinity,
              height: double.infinity,
              fit: BoxFit.cover,
              cacheWidth: (context.watch<Device>().realPixelRatio * bannerWidth).toInt(),
              cacheHeight: (context.watch<Device>().realPixelRatio * bannerHeight).toInt(),
            ),
            Center(
              child: Padding(
                padding: EdgeInsets.only(top: 16.0),
                child: Text(
                  AppLocalizations.of(context)!.levelPass,
                  style: TextStyle(
                    color: Colors.white,
                    fontSize: 22,
                    fontWeight: FontWeight.bold,
                    shadows: [Shadow(color: Colors.black54, offset: Offset(1, 1), blurRadius: 2)],
                  ),
                ),
              ),
            ),
          ],
        ),
      ),
    );
  }

  // 手势处理方法保持原有逻辑...
  Offset _globalToLocal(Offset globalPosition) {
    final RenderBox renderBox = boardKey.currentContext!.findRenderObject() as RenderBox;
    return renderBox.globalToLocal(globalPosition);
  }

  void _onPanStart(DragStartDetails details) {
    _log.info('_onPanStart');
    _overLayer?.stopHint();

    if (board!.status != BoardStatus.playing) {
      _log.info('不是playing状态，不响应onPanStart');
      return;
    }

    if (board!.checkWinCondition()) {
      _log.info('游戏已经完成，不再响应onPanStart');
      return;
    }

    // 动画中断逻辑
    if (_moveAnimationController.isAnimating && moveItems != null) {
      _log.info('移动动画中断，强制归位/交换');
      for (var item in moveItems!) {
        item.stop();
      }
      bool needRebuildGroup = moveItems!.any((item) => item.action == Action.swap);
      if (needRebuildGroup) {
        board!.backupAllGroups();
        board!.rebuildAllGroups();
      }
      moveItems = null;
      _moveAnimationController.stop();
      board!.invalidate();
    }

    if (_mergeAnimationController.isAnimating && _mergeGroups != null) {
      _log.info('合并动画中断，强制归位');
      for (var group in _mergeGroups!) {
        for (var piece in group.pieces) {
          piece.transform = board!.getTransformByCoordinate(piece.curRow, piece.curCol);
        }
      }
      _mergeGroups = null;
      _mergeAnimationController.stop();
      board!.invalidate();
    }

    _moveAnimationController.stop();
    _mergeAnimationController.stop();
    moveItems = null;

    final localPosition = _globalToLocal(details.globalPosition);
    final touchedPiece = board?.findPieceAt(localPosition);

    if (touchedPiece != null) {
      audio.playSfx(SfxType.panstart);
      if (settings.vibrate.value) {
        if (Platform.isAndroid) {
          Vibration.vibrate(duration: 60, amplitude: 50);
        } else {
          HapticFeedback.mediumImpact();
        }
      }

      _draggingPiece = touchedPiece;

      final draggingGroup = _draggingPiece!.group;
      if (draggingGroup != null) {
        board!.pieces.removeWhere((p) => draggingGroup.contains(p));
        board!.pieces.addAll(draggingGroup.pieces);
      } else {
        board!.pieces.remove(_draggingPiece);
        board!.pieces.add(_draggingPiece!);
      }
      board!.invalidate();
    }
  }

  void _onPanUpdate(DragUpdateDetails details) {
    _overLayer?.stopHint();
    if (_draggingPiece == null) return;

    final Offset delta = details.delta;
    final draggingGroup = _draggingPiece!.group;
    if (draggingGroup != null) {
      for (var piece in draggingGroup.pieces) {
        piece.applyDelta(delta);
      }
    } else {
      _draggingPiece!.applyDelta(delta);
    }
    board!.invalidate();
  }

  void _onPanEnd(DragEndDetails details) {
    _log.info('_onPanEnd');
    _overLayer?.stopHint();

    if (_draggingPiece == null) return;

    audio.playSfx(SfxType.tap);

    Piece leaderPiece = _draggingPiece!;
    _draggingPiece = null;
    board!.invalidate();

    Piece? targetPiece = board!.findPieceAtExclude(leaderPiece.currentCenter, leaderPiece);

    if (targetPiece == null && leaderPiece.group != null) {
      for (var p in leaderPiece.group!.pieces) {
        targetPiece = board!.findPieceAtExclude(p.currentCenter, p);
        if (targetPiece != null) {
          _log.info('推举 ${p.toString()} 为新leader');
          leaderPiece = p;
          break;
        }
      }
    }

    if (targetPiece != null && targetPiece != leaderPiece && leaderPiece.canPlaceTo(targetPiece)) {
      _log.info("swap animation start");
      _animateSwap(leaderPiece, targetPiece);
    } else {
      _log.info("revert animation start");
      _animateRevert(leaderPiece);
    }
  }

  void _animateSwap(Piece leaderPiece, Piece targetPiece) {
    List<MoveItem> items = [];

    final List<Piece> draggingPieces = leaderPiece.group != null ? leaderPiece.group!.pieces : [leaderPiece];
    final int dr = targetPiece.curRow - leaderPiece.curRow;
    final int dc = targetPiece.curCol - leaderPiece.curCol;

    List<Piece> displacedPieces = [];
    if (leaderPiece.group != null) {
      for (var p in draggingPieces) {
        final int targetRow = p.curRow + dr;
        final int targetCol = p.curCol + dc;
        final Piece? other = board!.getPieceByCoordinate(targetRow, targetCol);
        if (other != null && !p.isSameGroup(other)) {
          displacedPieces.add(other);
        }
      }
    } else {
      displacedPieces.add(targetPiece);
    }

    for (var p in draggingPieces) {
      p.curRow += dr;
      p.curCol += dc;
    }

    if (leaderPiece.group == null) {
      targetPiece.curRow -= dr;
      targetPiece.curCol -= dc;
    } else {
      for (var p in displacedPieces) {
        int newRow = p.curRow - dr;
        int newCol = p.curCol - dc;
        do {
          final Piece? pieceInSlot = board!.getPieceByCoordinate(newRow, newCol);
          if (pieceInSlot == null) {
            p.curRow = newRow;
            p.curCol = newCol;
            break;
          } else {
            newRow -= dr;
            newCol -= dc;
          }
        } while (newRow >= 0 && newRow < p.rows && newCol >= 0 && newCol < p.cols);
      }
    }

    for (var p in displacedPieces) {
      final startTransform = p.transform;
      final endTransform = board!.getTransformByCoordinate(p.curRow, p.curCol);
      final animation = Matrix4Tween(begin: startTransform, end: endTransform).animate(_moveAnimationController);
      items.add(MoveItem(piece: p, animation: animation, startTransform: startTransform, endTransform: endTransform, action: Action.swap));
      board!.pieces.remove(p);
      board!.pieces.add(p);
    }

    for (var p in draggingPieces) {
      final startTransform = p.transform;
      final endTransform = board!.getTransformByCoordinate(p.curRow, p.curCol);
      final animation = Matrix4Tween(begin: startTransform, end: endTransform).animate(_moveAnimationController);
      items.add(MoveItem(piece: p, animation: animation, startTransform: startTransform, endTransform: endTransform, action: Action.swap));
      board!.pieces.remove(p);
      board!.pieces.add(p);
    }

    moveItems = items;
    _moveAnimationController.forward(from: 0.0);
  }

  void _animateRevert(Piece piece) {
    List<MoveItem> items = [];
    final List<Piece> groupPieces = piece.group != null ? piece.group!.pieces : [piece];

    for (var p in groupPieces) {
      final startTransform = p.transform;
      final endTransform = board!.getTransformByCoordinate(p.curRow, p.curCol);
      final animation = Matrix4Tween(begin: startTransform, end: endTransform).animate(_moveAnimationController);
      items.add(MoveItem(piece: p, animation: animation, startTransform: startTransform, endTransform: endTransform, action: Action.revert));
    }

    moveItems = items;
    _moveAnimationController.forward(from: 0.0);
  }
}

class Matrix4Tween extends Tween<vmath.Matrix4> {
  Matrix4Tween({required vmath.Matrix4 begin, required vmath.Matrix4 end}) : super(begin: begin, end: end);

  @override
  vmath.Matrix4 lerp(double t) {
    if (begin == null || end == null) return begin ?? end ?? vmath.Matrix4.identity();
    final List<double> lerpedStorage = List.generate(16, (i) {
      return ui.lerpDouble(begin!.storage[i], end!.storage[i], t)!;
    });
    return vmath.Matrix4.fromList(lerpedStorage.cast<double>());
  }
}

class MoveItem {
  final Piece piece;
  final Animation<vmath.Matrix4> animation;
  final vmath.Matrix4 startTransform;
  final vmath.Matrix4 endTransform;
  final Action action;

  MoveItem({required this.piece, required this.animation, required this.startTransform, required this.endTransform, required this.action});

  void move() {
    piece.transform = animation.value;
  }

  void stop() {
    piece.transform = endTransform;
  }
}
