// board.dart

import 'dart:ui' as ui;

import 'package:flutter/material.dart';
import 'package:logging/logging.dart';
import 'package:puzzleweave/config/device.dart';
import 'package:puzzleweave/play/piece.dart';
import 'package:puzzleweave/skin/skin.dart';
import 'package:puzzleweave/utils/utils.dart';
import 'package:vector_math/vector_math.dart' as vmath;

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

// 增加一个preparing 和 shuffle洗牌的状态
// preparing: Opacity透明度动画展示核心绘制区
// shuffle： 发牌和翻牌动画构成
enum BoardStatus { loading, preparing, shuffle, playing, success }

// 发牌阶段，dealing：发牌中（发牌动画）， fliping：翻转中（翻转动画）
enum ShuffleStep { dealing, flipping }

class Board {
  // 原图
  final ui.Image image;

  // 纸牌背面图，用于
  final ui.Image cardImage;

  // 所有拼图碎片
  final List<Piece> pieces = [];

  /// 拼图行数（3/4/5，对应9/16/25宫格）
  final int rows;

  /// 拼图列数（3/4/5）
  final int cols;

  /// 困难模式
  final bool hard;

  /// 整个拼图在屏幕上的目标区域（最终完整显示的位置和大小）
  final Rect targetRect;

  // 初始等于targetRect， 关卡成功后， 核心绘制区上移, 这个rect用于做动画控制，success之后使用这个
  Rect finalRect;

  // 碎片的逻辑宽高
  double get pieceLogicalWidth => targetRect.width / cols;
  double get pieceLogicalHeight => targetRect.height / rows;

  // 碎片的圆角半径，宫格越多圆角半径越小
  double get cornerRadius => 10.0 - rows.toDouble();

  // 设备信息
  final Device device;

  // 静态背景绘图 Picture (只绘制一次)
  ui.Picture? backgroundPicture;

  // 卡片绘图
  ui.Picture? cardPicture;

  ValueNotifier boardNotifier = ValueNotifier(1);

  BoardStatus _status = BoardStatus.loading;
  BoardStatus get status => _status;

  // 备份的 groups (PieceGroup 对象的列表)
  List<PieceGroup> backupGroups = [];

  ShuffleStep _shuffleStep = ShuffleStep.dealing;
  ShuffleStep get shuffleStep => _shuffleStep;

  void shuffle(ShuffleStep step) {
    _status = BoardStatus.shuffle;
    _shuffleStep = step;
    invalidate();
  }

  void prepare() {
    _status = BoardStatus.preparing;
    invalidate();
  }

  void start() {
    _status = BoardStatus.playing;
    invalidate();
  }

  void success() {
    _status = BoardStatus.success;
    invalidate();
  }

  void invalidate() {
    boardNotifier.value++;
  }

  // 是否全部完成
  bool get isAllDone => pieces.every((p) => p.isOK);

  final TickerProviderStateMixin ticker;

  static Future<Board> create(
    TickerProviderStateMixin ticker,
    ui.Image image,
    ui.Image cardImage,
    int rows,
    int cols,
    bool hard,
    Rect targetRect,
    Device device,
  ) async {
    _log.info('Image.size=${image.width}x${image.height}');
    final board = Board(ticker, image, cardImage, rows, cols, hard, targetRect, device);
    return board;
  }

  static Future<Board> restore(
    TickerProviderStateMixin ticker,
    ui.Image image,
    ui.Image cardImage,
    int rows,
    int cols,
    bool hard,
    Rect targetRect,
    Device device,
    String jsonPath,
  ) async {
    _log.info('Image.size=${image.width}x${image.height}');

    try {
      Map<String, dynamic> json = await loadJson(jsonPath) as Map<String, dynamic>;
      if (json['pieces'] == null || json['pieces'][0] == null) {
        throw Exception('invalid json: $json');
      }
      rows = json['pieces'][0]['rows'];
      cols = json['pieces'][0]['cols'];

      final board = Board(ticker, image, cardImage, rows, cols, hard, targetRect, device, json: json);
      return board;
    } catch (e) {
      _log.warning("board restore failed: $e");
      _log.info("游戏恢复失败，转为重新创建");
      final board = Board(ticker, image, cardImage, rows, cols, hard, targetRect, device);
      return board;
    }
  }

  Board(this.ticker, this.image, this.cardImage, this.rows, this.cols, this.hard, this.targetRect, this.device, {Map<String, dynamic>? json})
    : finalRect = targetRect {
    _recordBackground(); // 录制静态背景，提升性能
    _recordCard(); // 新增：录制卡片 Picture
    if (json != null) {
      _restorePieces(json);
    } else {
      _initPieces();
    }
    rebuildAllGroups();
  }

  /// 初始化碎片
  void _initPieces() {
    pieces.clear();
    final imageWidth = image.width.toDouble();
    final imageHeight = image.height.toDouble();
    final pieceWidth = imageWidth / cols;
    final pieceHeight = imageHeight / rows;

    for (int i = 0; i < rows; i++) {
      for (int j = 0; j < cols; j++) {
        final index = i * cols + j;
        final sourceRect = Rect.fromLTWH(j * pieceWidth, i * pieceHeight, pieceWidth, pieceHeight);
        final transform = getTransformByCoordinate(i, j);

        pieces.add(
          Piece(board: this, index: index, row: i, col: j, rows: rows, cols: cols, sourceRect: sourceRect, curRow: i, curCol: j, transform: transform),
        );
      }
    }

    _shufflePieces();
    _sortPieces();
    _log.info('_initPieces');
  }

  // restore 碎片
  void _restorePieces(Map<String, dynamic> json) {
    pieces.clear();
    final imageWidth = image.width.toDouble();
    final imageHeight = image.height.toDouble();
    final pieceWidth = imageWidth / cols;
    final pieceHeight = imageHeight / rows;

    for (var i = 0; i < (json['pieces'] as List).length; i++) {
      var jsonPiece = json['pieces'][i];
      final int index = jsonPiece['index'];
      final int row = jsonPiece['row'];
      final int col = jsonPiece['col'];
      final int curRow = jsonPiece['curRow'];
      final int curCol = jsonPiece['curCol'];

      final sourceRect = Rect.fromLTWH(col * pieceWidth, row * pieceHeight, pieceWidth, pieceHeight);
      final transform = getTransformByCoordinate(curRow, curCol);

      pieces.add(
        Piece(
          board: this,
          index: index,
          row: row,
          col: col,
          rows: rows,
          cols: cols,
          sourceRect: sourceRect,
          curRow: curRow,
          curCol: curCol,
          transform: transform,
        ),
      );
    }

    _sortPieces();
    _log.info('_restorePieces');
  }

  // 洗牌（随机打乱碎片位置）
  void _shufflePieces() {
    final shuffledPositions = List.generate(pieces.length, (i) => i)..shuffle();
    for (int i = 0; i < pieces.length; i++) {
      final targetIndex = shuffledPositions[i];
      pieces[i].curRow = targetIndex ~/ cols;
      pieces[i].curCol = targetIndex % cols;
      pieces[i].transform = getTransformByCoordinate(pieces[i].curRow, pieces[i].curCol);
    }
  }

  void resetAllPieces() {
    for (var p in pieces) {
      p.transform = getTransformByCoordinate(p.curRow, p.curCol);
    }
  }

  void setAllPieceToBottomRight() {
    for (var p in pieces) {
      p.transform = getBottomRightTransform();
    }
  }

  // 是否应该根据当前的curRow和curCol对pieces做一次排序，以方便dealing动画实现依次从左上到右下的发牌效果
  void _sortPieces() {
    pieces.sort((Piece a, Piece b) {
      int ret = a.curRow - b.curRow;
      if (ret == 0) {
        ret = a.curCol - b.curCol;
      }
      return ret;
    });
  }

  // 根据坐标查找指定位置的碎片
  Piece? findPieceAt(Offset localPos) {
    for (var piece in pieces.reversed) {
      // 从上层开始找
      // 计算碎片在画布上的绝对位置board
      final transform = piece.transform;
      final posX = transform.storage[12];
      final posY = transform.storage[13];
      final pieceRect = Rect.fromLTWH(posX, posY, pieceLogicalWidth, pieceLogicalHeight);

      if (pieceRect.contains(localPos)) {
        return piece;
      }
    }
    return null;
  }

  // 查找某个坐标上的碎片，排除某个piece
  Piece? findPieceAtExclude(Offset localPos, Piece excludePiece) {
    for (var piece in pieces.reversed) {
      // 从上层开始找
      if (piece == excludePiece) continue;
      // 计算碎片在画布上的绝对位置
      final transform = piece.transform;
      final posX = transform.storage[12];
      final posY = transform.storage[13];
      final pieceRect = Rect.fromLTWH(posX, posY, pieceLogicalWidth, pieceLogicalHeight);

      if (pieceRect.contains(localPos)) {
        return piece;
      }
    }
    return null;
  }

  // 获取右下角变换矩阵
  vmath.Matrix4 getBottomRightTransform() {
    return getTransformByCoordinate(rows - 1, cols - 1);
  }

  // 根据格子位置（row， col）获取变换矩阵
  vmath.Matrix4 getTransformByCoordinate(int row, int col) {
    final x = targetRect.left + col * pieceLogicalWidth;
    final y = targetRect.top + row * pieceLogicalHeight;

    final transform = vmath.Matrix4.translationValues(x, y, 0.0);
    return transform;
  }

  // 根据坐标获取该位置上的piece
  Piece? getPieceByCoordinate(int row, int col) {
    return pieces.firstWhereOrNull((p) => p.curRow == row && p.curCol == col);
  }

  // 根据坐标获取该位置上的piece
  Piece? getPieceByIndex(int index) {
    return pieces.firstWhereOrNull((p) => p.index == index);
  }

  // 根据当前坐标查找邻居碎片
  /// 获取碎片在其当前网格位置上四个方向的邻居碎片。
  List<Piece> getCurNeighbors(Piece piece) {
    final List<Piece> neighbors = [];
    final int r = piece.curRow;
    final int c = piece.curCol;

    // 上邻居 (curRow - 1, curCol)
    Piece? top = getPieceByCoordinate(r - 1, c);
    if (top != null) {
      neighbors.add(top);
    }

    // 下邻居 (curRow + 1, curCol)
    Piece? bottom = getPieceByCoordinate(r + 1, c);
    if (bottom != null) {
      neighbors.add(bottom);
    }

    // 左邻居 (curRow, curCol - 1)
    Piece? left = getPieceByCoordinate(r, c - 1);
    if (left != null) {
      neighbors.add(left);
    }

    // 右邻居 (curRow, curCol + 1)
    Piece? right = getPieceByCoordinate(r, c + 1);
    if (right != null) {
      neighbors.add(right);
    }

    return neighbors;
  }

  // 初始化检查合并分组 (改进版：固定点迭代合并)
  void rebuildAllGroups() {
    _log.info('rebuildAllGroups');
    // 1. 清除所有旧组 和 原path
    for (var p in pieces) {
      p.group = null;
      p.path = null;
      p.outLinePath = null;
      p.innerLinePath = null;
    }

    bool mergedInPass;

    // 2. 迭代合并，直到一轮循环中没有发生任何合并
    do {
      mergedInPass = false;

      // 3. 遍历所有碎片对 (i, j)
      for (int i = 0; i < pieces.length; i++) {
        for (int j = i + 1; j < pieces.length; j++) {
          final piece = pieces[i];
          final otherPiece = pieces[j];

          // 4. 检查是否可以合并，并且它们不属于同一个组
          if (piece.canMerge(otherPiece)) {
            if (!piece.isSameGroup(otherPiece)) {
              // 5. 合并组
              // piece.groupWith(otherPiece) 会创建一个新的 PieceGroup，
              // 并将 piece 和 otherPiece (以及它们可能已有的组员) 的 group 引用全部指向新组。
              piece.groupWith(otherPiece);
              mergedInPass = true;
            }
          }
        }
      }
      // 如果 mergedInPass 为 true，说明本轮循环发生了合并，需要重新开始下一轮遍历
      // 因为新的合并可能促使其他碎片或碎片组也得以连接
    } while (mergedInPass);
  }

  // 备份所有group， 方便进行比较， 发现新合成的group，以便呈现动画特效
  void backupAllGroups() {
    backupGroups.clear();
    for (var p in pieces) {
      if (p.group != null && !backupGroups.contains(p.group)) {
        backupGroups.add(p.group!);
      }
    }
    _log.info('backupAllGroups: ${backupGroups.length}');
  }

  // 当前group与之前备份的group进行比较，返回新合并的group列表，这些group需要展示动画特效
  List<PieceGroup> compareAllGroups() {
    _log.info('compareAllGroups');
    List<PieceGroup> newGroups = [];

    List<PieceGroup> currentGroups = [];
    for (var p in pieces) {
      if (p.group != null && !currentGroups.contains(p.group)) {
        currentGroups.add(p.group!);
      }
    }

    if (backupGroups.isEmpty) {
      // 之前都不存在群组，那么当前新合成的所有group都是新group
      newGroups.addAll(currentGroups);
    } else {
      for (var g in currentGroups) {
        bool alreadyExists = false;
        for (var bakgroup in backupGroups) {
          if (bakgroup.containsGroup(g)) {
            alreadyExists = true;
            break;
          }
        }
        if (!alreadyExists) {
          newGroups.add(g);
        }
      }
    }

    if (newGroups.isNotEmpty) {
      for (var g in newGroups) {
        _log.info('发现新群组：');
        g.print();
      }
    }

    return newGroups;
  }

  // 检查游戏是否全部完成
  bool checkWinCondition() {
    return pieces.every((p) => p.isOK);
  }

  /// 退出释放资源
  dispose() {
    backgroundPicture?.dispose();
    backgroundPicture = null;
    cardPicture?.dispose();
    cardPicture = null;
    image.dispose();
    boardNotifier.dispose();
  }

  // 录制背景，避免每次都重复绘制，提升性能
  void _recordBackground() {
    // 确保在录制前释放旧的 Picture 资源
    backgroundPicture?.dispose();
    backgroundPicture = null;

    final recorder = ui.PictureRecorder();

    // 录制器的边界设置为整个屏幕尺寸，因为我们在这里绘制的是 full screen background。
    final recordBounds = Rect.fromLTWH(0, 0, device.screenSize.width, device.screenSize.height);

    final canvas = Canvas(recorder, recordBounds);

    // --- 静态绘制配置 ---
    const double strokeWidth = 1.0; // 拼图槽位的线宽
    final double halfStroke = strokeWidth / 2.0;

    // 1. 绘制整个屏幕背景
    canvas.drawRect(
      recordBounds,
      Paint()
        ..color = SkinHelper.wholeBgColor
        ..style = PaintingStyle.fill,
    );

    // 2. 绘制每个拼图槽位（圆角矩形）作为背景的一部分 (静态内容)
    final slotFillPaint = Paint()
      ..color = SkinHelper.slotBgColor
      // .shade100 // 槽位填充色
      ..style = PaintingStyle.fill;

    final slotStrokePaint = Paint()
      ..color = SkinHelper.slotBorderColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = strokeWidth;

    for (int r = 0; r < rows; r++) {
      for (int c = 0; c < cols; c++) {
        // 计算当前槽位的边界 (Canvas坐标系)
        final left = targetRect.left + c * pieceLogicalWidth;
        final top = targetRect.top + r * pieceLogicalHeight;
        final right = left + pieceLogicalWidth;
        final bottom = top + pieceLogicalHeight;

        // 为了避免描边溢出到相邻槽位，将矩形向内收缩 halfStroke
        final slotRect = Rect.fromLTRB(left + halfStroke, top + halfStroke, right - halfStroke, bottom - halfStroke);

        final slotRRect = RRect.fromRectAndRadius(slotRect, Radius.circular(cornerRadius));

        // 绘制填充
        canvas.drawRRect(slotRRect, slotFillPaint);

        // 绘制描边
        canvas.drawRRect(slotRRect, slotStrokePaint);
      }
    }

    // --- 结束录制并存储 ---
    backgroundPicture = recorder.endRecording();
    _log.info('Static background picture recorded. Size: ${recordBounds.size}');
  }

  // 提前录制卡牌（包含边框），避免每次都重复绘制，提升效率
  void _recordCard() {
    // 确保在录制前释放旧的 Picture 资源
    cardPicture?.dispose();
    cardPicture = null;

    final recorder = ui.PictureRecorder();

    // 录制器的边界设置为单个碎片区域
    final recordBounds = Rect.fromLTWH(0, 0, pieceLogicalWidth, pieceLogicalHeight);

    final canvas = Canvas(recorder, recordBounds);

    final double w = pieceLogicalWidth;
    final double h = pieceLogicalHeight;
    final double cornerRadius = this.cornerRadius;

    // 1. 裁剪区域
    final rrect = RRect.fromRectAndRadius(Rect.fromLTWH(0, 0, w, h), Radius.circular(cornerRadius));

    // 绘制图片的 Paint
    final imagePaint = Paint()
      ..isAntiAlias
      ..filterQuality = FilterQuality.none; // 禁用过滤（发牌动画中肉眼无差异） = true;

    // 边框 Paint
    const double pieceStrokeWidth = 1.0;
    final Paint outerBorderPaint = Paint()
      ..color = SkinHelper.outLineBorderColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = pieceStrokeWidth
      ..isAntiAlias = true;

    final Paint innerBorderPaint = Paint()
      ..color = SkinHelper.innerLineBorderColor
      ..style = PaintingStyle.stroke
      ..strokeWidth = pieceStrokeWidth
      ..isAntiAlias = true;

    // 边框 RRects
    final outerRRect = RRect.fromRectAndRadius(Rect.fromLTWH(0.5, 0.5, w - 1.0, h - 1.0), Radius.circular(cornerRadius));
    final innerRRect = RRect.fromRectAndRadius(Rect.fromLTWH(2.5, 2.5, w - 5.0, h - 5.0), Radius.circular(cornerRadius));

    // --- 录制流程 ---

    // 1. 绘制图片内容 (需要裁剪)
    canvas.save();
    canvas.clipRRect(rrect); // 应用裁剪

    final Rect sourceRect = Rect.fromLTWH(0, 0, cardImage.width.toDouble(), cardImage.height.toDouble());
    final Rect dstRect = Rect.fromLTWH(0, 0, w, h);

    canvas.drawImageRect(cardImage, sourceRect, dstRect, imagePaint);

    canvas.restore(); // 移除裁剪

    // 2. 绘制边框 (不需要裁剪，因为边框是为了在图片外部描绘边界)
    canvas.drawRRect(outerRRect, outerBorderPaint);
    canvas.drawRRect(innerRRect, innerBorderPaint);

    // --- 结束录制并存储 ---
    cardPicture = recorder.endRecording();
    _log.info('Card picture recorded (with border). Size: ${recordBounds.size}');
  }

  Map<String, dynamic> toJson() => {'pieces': pieces.map((e) => e.toJson()).toList()};
}
