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

import 'package:flutter/material.dart';
import 'package:fluttertoast/fluttertoast.dart';
import 'package:puzzleweave/audio/jc_audio_controller.dart';
import 'package:puzzleweave/config/device.dart';
import 'package:puzzleweave/homepage/home_board.dart';
import 'package:puzzleweave/l10n/app_localizations.dart';
import 'package:puzzleweave/models/cached_request.dart';
import 'package:puzzleweave/models/data.dart';
import 'package:puzzleweave/models/items.dart';
import 'package:puzzleweave/play/confetti_layer.dart';
import 'package:puzzleweave/skin/skin.dart';
import 'package:logging/logging.dart';
import 'package:provider/provider.dart';

final Logger _log = Logger('home_board_play');

// ignore: must_be_immutable
class HomeBoardPlay extends StatefulWidget {
  final double canvasWidth;
  final double canvasHeight;
  final GlobalKey collectionKey; //  接收 Collection Button Key, 方便定位
  VoidCallback? onCollectionDone; // 新增一个合集完成的回调，外部home_screen可能会关心，当合集解锁动画完成，home_screen的左上角合集icon需要放大再复原

  HomeBoardPlay({super.key, required this.canvasWidth, required this.canvasHeight, required this.collectionKey, this.onCollectionDone});

  @override
  State<HomeBoardPlay> createState() => HomeBoardPlayState();
}

class HomeBoardPlayState extends State<HomeBoardPlay> with TickerProviderStateMixin {
  late HomeBoard board;
  late JcAudioController audio;
  late Data data;
  List<ListItem>? collection;
  late CachedRequest collectionCachedRequest;
  late StreamSubscription? collectionSubscription;

  late ConfettiLayer confettiLayer;

  // 翻牌动画，完成一个关卡后翻开一个卡片
  late AnimationController _flipController;
  late Animation<double> _flipAnimation;

  // todo... 合集解锁动画，完成整个合集之后执行， 效果是整个图片缩小并位移到左上角的colleciton iconbutton, 形成合集“收纳”的效果
  late AnimationController _unlockController; //  解锁动画控制器
  late Animation<double> _unlockAnimation; // 0.0 -> 1.0

  // 当前合集是否已经完成(一个合集需要完成5x5即25个关卡才能解锁)(在切换到下一个合集之前调用判断)
  bool get _isCurCollectionDone => data.currentLevel != 0 && (data.currentCollectionIndex) * 25 == data.currentLevel;
  // bool get _isCurCollectionDone => true; // for test

  OverlayEntry? _overlayEntry; // 新增：用于管理全屏动画层

  // 发牌动画
  late AnimationController _dealingController; // 发牌动画控制器
  late Animation<double> _dealingAnimation;
  // 发牌间隔（ms）
  final int _dealingPieceInterval = 70;
  // 每个卡片移动时间（ms）
  final int _dealingPieceDuration = 200;

  // 发牌动画总时长（需要考虑到最后一个卡片的动画）
  // 共有 board.rows * board.cols 个卡片
  int get _totalDealingDuration => (board.rows * board.cols - 1) * _dealingPieceInterval + _dealingPieceDuration;
  Timer? _dealingPeriodicTimer;
  int _dealingCount = 0; // 计数：记录执行次数

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

    Device device = context.read<Device>();
    board = HomeBoard(canvasWidth: widget.canvasWidth, canvasHeight: widget.canvasHeight, device: device);
    board.isReadyNotifier.addListener(_onBoardReady);

    confettiLayer = ConfettiLayer(this);

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

    audio = context.read<JcAudioController>();
    data = context.read<Data>();

    collectionCachedRequest = data.collection;
    // 主动获取缓存数据（关键）
    final collectionCachedData = collectionCachedRequest.cachedData;
    if (collectionCachedData != null) {
      _onCollectionDataUpdate(collectionCachedData);
    }
    collectionSubscription = collectionCachedRequest.stream.listen(_onCollectionDataUpdate, onError: _onCollectionDataError);

    _flipController = AnimationController(duration: const Duration(milliseconds: 1000), vsync: this);
    _flipAnimation = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(parent: _flipController, curve: Curves.easeOut))
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _checkCollectionDone();
        }
      });

    _unlockController = AnimationController(duration: const Duration(milliseconds: 800), vsync: this);
    _unlockAnimation = Tween<double>(begin: 0, end: 1).animate(CurvedAnimation(parent: _unlockController, curve: Curves.easeInBack))
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          _overlayEntry?.remove();
          _overlayEntry = null;
          widget.onCollectionDone?.call();
          _startDealingAnimation();
        }
      });

    _dealingController = AnimationController(
      duration: Duration(milliseconds: _totalDealingDuration),
      vsync: this,
    );
    _dealingAnimation = CurvedAnimation(parent: _dealingController, curve: Curves.easeOut)
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) {
          switchToNextCollection();
          if (mounted) {
            setState(() {
              board.status = HomeBoardStatus.playing;
              board.invalidate();
            });
          }
        }
      });
  }

  _onCollectionDataUpdate(colldata) async {
    _log.info('_onCollectionDataUpdate.... ');
    if (colldata != null) {
      collection = colldata as List<ListItem>;
      if (collection != null && collection!.isNotEmpty) {
        if ((data.completedWorks.value.length / 25).floor() > data.currentCollectionIndex) {
          if (currentCollectionItem != null) {
            _log.info('合集落后于关卡，没有正常切换，矫正！');
            data.collectionDone(currentCollectionItem!);
          }
        }
        board.currentCollectionItem = currentCollectionItem;
      }
      if (mounted) setState(() {});
      if (colldata.length < 5) {
        Future.delayed(const Duration(seconds: 3), () => refresh());
      }
    }
  }

  _onCollectionDataError(error) {
    _log.info('_onCollectionDataError.... $error');
    if (collection == null || collection!.isEmpty || collection!.length <= 2) {
      // 列表数据如果少于20，说明只是内置图，仍然刷新远程请求
      _log.warning("_onCollectionDataError, retry again");
      Future.delayed(Duration(seconds: 3), () => refresh());
    }
  }

  Future<void> refresh() async {
    _log.info('refresh...');
    await collectionCachedRequest.refresh();
  }

  // board 图片等资源加载完成的回调
  _onBoardReady() {
    if (board.status == HomeBoardStatus.loading && mounted) {
      setState(() {
        board.status = HomeBoardStatus.playing;
      });
    }
  }

  // !!! 改造点 2: 启动发牌动画
  void _startDealingAnimation() {
    if (!mounted) return;
    setState(() {
      board.status = HomeBoardStatus.dealing;
    });
    _dealingController.duration = Duration(milliseconds: _totalDealingDuration);
    _dealingController.forward(from: 0.0);
    _dealingCount = 0;
    audio.playSfx(SfxType.card);
    _dealingPeriodicTimer?.cancel();
    _dealingPeriodicTimer = Timer.periodic(const Duration(milliseconds: 130), (timer) {
      if (mounted) {
        _dealingCount++;
        if (_dealingCount >= (_totalDealingDuration / 130) - 2) {
          timer.cancel();
        } else {
          audio.playSfx(SfxType.card);
        }
      } else {
        timer.cancel();
      }
    });
  }

  ListItem? get currentCollectionItem {
    if (collection != null && collection!.isNotEmpty && data.currentCollectionIndex < collection!.length) {
      return collection![data.currentCollectionIndex];
    }
    return null;
  }

  @override
  void dispose() {
    board.isReadyNotifier.removeListener(_onBoardReady);
    _dealingPeriodicTimer?.cancel();
    _overlayEntry?.remove();
    _overlayEntry = null;
    board.dispose(); // 调用优化后的 dispose
    confettiLayer.dispose();
    _flipController.dispose();
    _unlockController.dispose();
    _dealingController.dispose();
    collectionSubscription?.cancel();
    super.dispose();
  }

  void _checkCollectionDone() async {
    if (_isCurCollectionDone) {
      if (mounted) {
        setState(() {
          board.status = HomeBoardStatus.done;
          board.invalidate();
        });
      }
      _startUnlockAnimation();
    }
  }

  void _startUnlockAnimation() {
    // 防御检查 1: 确保图片已加载，否则跳过动画直接发牌
    if (board.image == null) {
      _log.warning('Unlock image not ready, skipping animation.');
      widget.onCollectionDone?.call();
      _startDealingAnimation();
      return;
    }

    final RenderBox? targetRenderBox = widget.collectionKey.currentContext?.findRenderObject() as RenderBox?;
    final RenderBox? canvasRenderBox = context.findRenderObject() as RenderBox?;

    if (targetRenderBox == null || canvasRenderBox == null || !mounted) return;

    final targetPosition = targetRenderBox.localToGlobal(targetRenderBox.size.center(Offset.zero));
    final canvasGlobalTL = canvasRenderBox.localToGlobal(Offset.zero);
    final canvasCenter = canvasRenderBox.localToGlobal(canvasRenderBox.size.center(Offset.zero));
    final Offset delta = targetPosition - canvasCenter;

    board.setUnlockAnimationTarget(targetOffset: delta, targetScale: targetRenderBox.size.width * 0.2 / widget.canvasWidth);

    _overlayEntry = OverlayEntry(
      builder: (context) {
        return Positioned(
          left: canvasGlobalTL.dx,
          top: canvasGlobalTL.dy,
          child: SizedBox(
            width: widget.canvasWidth,
            height: widget.canvasHeight,
            child: CustomPaint(
              painter: CanvasPainter(
                board: board,
                level: data.currentLevel,
                collectionIndex: data.currentCollectionIndex,
                flipAnimation: _flipAnimation,
                unlockAnimation: _unlockAnimation,
                forceStatus: HomeBoardStatus.unlocking,
                dealingAnimation: _dealingAnimation,
                dealingPieceInterval: _dealingPieceInterval,
                dealingPieceDuration: _dealingPieceDuration,
              ),
            ),
          ),
        );
      },
    );

    Overlay.of(context).insert(_overlayEntry!);

    if (mounted) {
      setState(() {
        board.status = HomeBoardStatus.unlocking;
      });
    }
    _unlockController.forward(from: 0.0);
  }

  void startFlipAnimation() {
    _flipController.forward(from: 0.0);
    audio.playSfx(SfxType.flip);
    if (data.currentLevel != 0 && (data.currentCollectionIndex + 1) * 25 == data.currentLevel && currentCollectionItem != null) {
      data.collectionDone(currentCollectionItem!);
      audio.playSfx(SfxType.star);
      confettiLayer.play();
    }
  }

  void switchToNextCollection() {
    if (currentCollectionItem == null) {
      Fluttertoast.showToast(msg: AppLocalizations.of(context)!.noMorePicture);
      return;
    }
    board.switchToNextCollection(currentCollectionItem!);
  }

  @override
  Widget build(BuildContext context) {
    final isUnlocking = _unlockController.isAnimating || board.status == HomeBoardStatus.unlocking;
    return CustomPaint(
      size: Size(widget.canvasWidth, widget.canvasHeight),
      painter: isUnlocking && _overlayEntry != null
          ? null
          : CanvasPainter(
              board: board,
              level: data.currentLevel,
              collectionIndex: data.currentCollectionIndex,
              flipAnimation: _flipAnimation,
              unlockAnimation: _unlockAnimation,
              dealingAnimation: _dealingAnimation,
              dealingPieceDuration: _dealingPieceDuration,
              dealingPieceInterval: _dealingPieceInterval,
            ),
      child: board.status == HomeBoardStatus.loading
          ? Center(child: CircularProgressIndicator(valueColor: AlwaysStoppedAnimation<Color>(SkinHelper.slotBorderColor)))
          : Container(),
    );
  }
}

class CanvasPainter extends CustomPainter {
  final HomeBoard board;
  final int level;
  final int collectionIndex;
  final Animation<double> flipAnimation;
  final Animation<double> unlockAnimation;
  final HomeBoardStatus? forceStatus;
  final Animation<double> dealingAnimation;
  final int dealingPieceDuration;
  final int dealingPieceInterval;

  CanvasPainter({
    required this.board,
    required this.level,
    required this.collectionIndex,
    required this.flipAnimation,
    required this.unlockAnimation,
    this.forceStatus,
    required this.dealingAnimation,
    required this.dealingPieceDuration,
    required this.dealingPieceInterval,
  }) : super(repaint: Listenable.merge([board.boardNotifier, flipAnimation, unlockAnimation, dealingAnimation]));

  @override
  void paint(Canvas canvas, Size size) {
    final statusToPaint = forceStatus ?? board.status;

    // 根据不同状态执行绘制，每个方法内部现在都有 null 检查
    switch (statusToPaint) {
      case HomeBoardStatus.playing:
        _paintPlaying(canvas, size);
        break;
      case HomeBoardStatus.done:
        _paintSuccess(canvas, size);
        break;
      case HomeBoardStatus.unlocking:
        _paintUnlocking(canvas, size);
        break;
      case HomeBoardStatus.dealing:
        _paintDealing(canvas, size);
        break;
      case HomeBoardStatus.loading:
        break;
    }
  }

  void _paintDealing(Canvas canvas, Size size) {
    if (board.cardImage == null) return; // 防御性检查

    final totalPieces = board.rows * board.cols;
    final totalAnimationDuration = dealingPieceInterval * (totalPieces - 1) + dealingPieceDuration;

    final startX = board.cols - 1;
    final startY = board.rows - 1;
    final startPieceCenterX = startX * board.pieceLogicalWidth + board.pieceLogicalWidth / 2;
    final startPieceCenterY = startY * board.pieceLogicalHeight + board.pieceLogicalHeight / 2;

    for (var i = 0; i < board.rows; i++) {
      for (var j = 0; j < board.cols; j++) {
        final pieceIndex = i * board.cols + j;
        final animationStartTime = pieceIndex * dealingPieceInterval;
        final animationEndTime = animationStartTime + dealingPieceDuration;
        final currentGlobalTime = dealingAnimation.value * totalAnimationDuration;

        double progress;
        if (currentGlobalTime < animationStartTime) {
          progress = 0.0;
        } else if (currentGlobalTime >= animationEndTime) {
          progress = 1.0;
        } else {
          progress = (currentGlobalTime - animationStartTime) / dealingPieceDuration;
        }

        double startOffX = startPieceCenterX - (j * board.pieceLogicalWidth + board.pieceLogicalWidth / 2);
        double startOffY = startPieceCenterY - (i * board.pieceLogicalHeight + board.pieceLogicalHeight / 2);

        _drawDealingPiece(canvas, size, i, j, startOffX, startOffY, progress);
      }
    }
  }

  void _drawDealingPiece(Canvas canvas, Size size, int row, int col, double startOffsetX, double startOffsetY, double progress) {
    final cardImg = board.cardImage;
    if (cardImg == null) return;

    final curIndex = collectionIndex * board.count + row * board.cols + col;

    final w = board.pieceLogicalWidth;
    final h = board.pieceLogicalHeight;

    final targetLeft = col * w;
    final targetTop = row * h;

    final currentOffsetX = ui.lerpDouble(startOffsetX, 0.0, progress) ?? 0.0;
    final currentOffsetY = ui.lerpDouble(startOffsetY, 0.0, progress) ?? 0.0;

    canvas.save();
    canvas.translate(targetLeft + currentOffsetX, targetTop + currentOffsetY);

    final cardSourceRect = Rect.fromLTWH(0, 0, cardImg.width.toDouble(), cardImg.height.toDouble());
    final rect = Rect.fromLTWH(0, 0, w, h);
    canvas.drawImageRect(cardImg, cardSourceRect, rect, Paint()..isAntiAlias = true);

    final textStyle = TextStyle(
      color: Colors.white,
      fontSize: h * 0.25,
      fontWeight: FontWeight.bold,
      shadows: const [Shadow(offset: Offset(1, 1), blurRadius: 2, color: Colors.black38)],
    );
    final textPainter = TextPainter(
      text: TextSpan(text: (curIndex + 1).toString(), style: textStyle),
      textDirection: TextDirection.ltr,
      textAlign: TextAlign.center,
    );
    textPainter.layout(minWidth: 0, maxWidth: w);
    textPainter.paint(canvas, Offset((w - textPainter.width) / 2, (h - textPainter.height) / 2));
    canvas.restore();

    _drawBorders(canvas, targetLeft + currentOffsetX, targetTop + currentOffsetY, w, h);
  }

  void _paintUnlocking(Canvas canvas, Size size) {
    final img = board.image;
    if (img == null) return;

    final progress = unlockAnimation.value;
    final currentScale = ui.lerpDouble(1.0, board.unlockTargetScale, progress) ?? 1.0;
    final currentOffset = Offset(
      ui.lerpDouble(0.0, board.unlockTargetOffset.dx, progress) ?? 0.0,
      ui.lerpDouble(0.0, board.unlockTargetOffset.dy, progress) ?? 0.0,
    );

    canvas.save();
    final centerX = size.width / 2;
    final centerY = size.height / 2;

    canvas.translate(centerX + currentOffset.dx, centerY + currentOffset.dy);
    canvas.scale(currentScale);
    canvas.translate(-centerX, -centerY);

    final rect = Rect.fromLTWH(0, 0, size.width, size.height);
    final sourceRect = Rect.fromLTWH(0, 0, img.width.toDouble(), img.height.toDouble());
    canvas.drawImageRect(img, sourceRect, rect, Paint()..isAntiAlias = true);
    canvas.restore();
  }

  void _paintSuccess(Canvas canvas, Size size) {
    final img = board.image;
    if (img == null) return;

    final rect = Rect.fromLTWH(0, 0, size.width, size.height);
    final rrect = RRect.fromRectAndRadius(rect, const Radius.circular(4.0));
    final sourceRect = Rect.fromLTWH(0, 0, img.width.toDouble(), img.height.toDouble());

    canvas.save();
    canvas.clipRRect(rrect);
    canvas.drawImageRect(img, sourceRect, rect, Paint()..isAntiAlias = true);
    canvas.restore();
    _drawBorders(canvas, 0, 0, size.width, size.height);
  }

  void _paintPlaying(Canvas canvas, Size size) {
    for (var i = 0; i < board.rows; i++) {
      for (var j = 0; j < board.cols; j++) {
        final int curIndex = i * board.rows + j;
        bool flipped = level > collectionIndex * board.count + curIndex;
        _drawPiece(canvas, size, i, j, flipped);
      }
    }
  }

  void _drawPiece(Canvas canvas, Size size, int row, int col, bool flipped) {
    final img = board.image;
    final cardImg = board.cardImage;
    if (img == null || cardImg == null) return; // 双重检查

    final w = size.width / board.cols;
    final h = size.height / board.rows;
    final left = col * w;
    final top = row * h;
    final rect = Rect.fromLTWH(left, top, w, h);
    final rrect = RRect.fromRectAndRadius(rect, const Radius.circular(4.0));

    final pieceWidth = img.width / board.cols;
    final pieceHeight = img.height / board.rows;
    final imageSourceRect = Rect.fromLTWH(pieceWidth * col, pieceHeight * row, pieceWidth, pieceHeight);
    final cardSourceRect = Rect.fromLTWH(0, 0, cardImg.width.toDouble(), cardImg.height.toDouble());

    final curIndex = collectionIndex * board.count + row * board.rows + col;
    double flipProgress = (flipAnimation.isAnimating && curIndex == level - 1) ? flipAnimation.value : 0.0;
    if (flipProgress > 0) flipped = flipProgress > 0.5;

    canvas.save();
    final centerX = left + w / 2;
    final centerY = top + h / 2;
    canvas.translate(centerX, centerY);

    if (flipProgress > 0.0) {
      double angle = flipProgress * pi;
      Matrix4 transform = Matrix4.identity()
        ..setEntry(3, 2, 0.0015)
        ..rotateY(angle);
      if (flipProgress > 0.5) transform.scale(-1.0, 1.0, 1.0);
      canvas.transform(transform.storage);
    }

    canvas.translate(-centerX, -centerY);
    canvas.clipRRect(rrect);

    if (flipped) {
      canvas.drawImageRect(img, imageSourceRect, rect, Paint()..isAntiAlias = true);
    } else {
      final targetImg = flipProgress > 0.5 ? img : cardImg;
      final sourceRect = flipProgress > 0.5 ? imageSourceRect : cardSourceRect;
      canvas.drawImageRect(targetImg, sourceRect, rect, Paint()..isAntiAlias = true);

      if (flipProgress <= 0.5) {
        final textStyle = TextStyle(
          color: Colors.white,
          fontSize: h * 0.25,
          fontWeight: FontWeight.bold,
          shadows: const [Shadow(offset: Offset(1, 1), blurRadius: 2, color: Colors.black38)],
        );
        final textPainter = TextPainter(
          text: TextSpan(text: (curIndex + 1).toString(), style: textStyle),
          textDirection: TextDirection.ltr,
          textAlign: TextAlign.center,
        );
        textPainter.layout(minWidth: 0, maxWidth: w);
        textPainter.paint(canvas, Offset(left + (w - textPainter.width) / 2, top + (h - textPainter.height) / 2));
      }
    }
    canvas.restore();
    _drawBorders(canvas, left, top, w, h);
  }

  void _drawBorders(Canvas canvas, double x, double y, double w, double h) {
    final rect = Rect.fromLTWH(x, y, w, h);
    canvas.drawRRect(
      RRect.fromRectAndRadius(rect.deflate(0.5), const Radius.circular(4.0)),
      Paint()
        ..color = SkinHelper.outLineBorderColor
        ..style = PaintingStyle.stroke
        ..strokeWidth = 1.0
        ..isAntiAlias = true,
    );
    canvas.drawRRect(
      RRect.fromRectAndRadius(rect.deflate(1.5), const Radius.circular(4.0)),
      Paint()
        ..color = SkinHelper.innerLineBorderColor
        ..style = PaintingStyle.stroke
        ..strokeWidth = 1.0
        ..isAntiAlias = true,
    );
  }

  @override
  bool shouldRepaint(covariant CanvasPainter oldDelegate) => true;
}
