import 'dart:ui' as ui;

import 'package:asciineverdie/src/shared/animation/canvas/ascii_cell.dart';
import 'package:asciineverdie/src/shared/animation/canvas/ascii_layer.dart';
import 'package:asciineverdie/src/shared/theme/ascii_colors.dart';
import 'package:flutter/material.dart';

/// Paragraph 캐시 키
class _ParagraphCacheKey {
  const _ParagraphCacheKey({
    required this.char,
    required this.color,
    required this.fontSize,
    required this.cellWidth,
    required this.opacity,
  });

  final String char;
  final AsciiCellColor color;
  final double fontSize;
  final double cellWidth;
  final double opacity;

  @override
  bool operator ==(Object other) =>
      identical(this, other) ||
      other is _ParagraphCacheKey &&
          char == other.char &&
          color == other.color &&
          fontSize == other.fontSize &&
          cellWidth == other.cellWidth &&
          opacity == other.opacity;

  @override
  int get hashCode => Object.hash(char, color, fontSize, cellWidth, opacity);
}

/// ASCII Canvas 페인터 (CustomPainter 구현)
///
/// 레이어 기반으로 ASCII 문자를 Canvas에 그린다.
/// 각 문자는 고정 크기 그리드 셀에 배치된다.
/// Paragraph 캐싱으로 GC 압박 최소화.
/// 테마 인식 색상 지원 (Phase 5).
class AsciiCanvasPainter extends CustomPainter {
  AsciiCanvasPainter({
    required this.layers,
    this.gridWidth = 60,
    this.gridHeight = 8,
    this.backgroundColor = AsciiColors.background,
    this.backgroundOpacity = 0.5,
    this.layerVersion = 0,
    this.objectColor = AsciiColors.object,
    this.positiveColor = AsciiColors.positive,
    this.negativeColor = AsciiColors.negative,
    this.rarityUncommonColor = AsciiColors.rarityUncommon,
    this.rarityRareColor = AsciiColors.rarityRare,
    this.rarityEpicColor = AsciiColors.rarityEpic,
    this.rarityLegendaryColor = AsciiColors.rarityLegendary,
  });

  /// 렌더링할 레이어 목록 (z-order 정렬 필요)
  final List<AsciiLayer> layers;

  /// 그리드 너비 (열 수)
  final int gridWidth;

  /// 그리드 높이 (행 수)
  final int gridHeight;

  /// 배경색 (테마 인식 가능)
  final Color backgroundColor;

  /// 배경 투명도 (0.0 ~ 1.0, 기본값 0.5 = 50%)
  final double backgroundOpacity;

  /// 레이어 버전 (변경 감지용)
  final int layerVersion;

  /// 오브젝트 색상 (테마 인식)
  final Color objectColor;

  /// 포지티브 이펙트 색상 (테마 인식)
  final Color positiveColor;

  /// 네거티브 이펙트 색상 (테마 인식)
  final Color negativeColor;

  // ═══════════════════════════════════════════════════════════════════════
  // 무기 등급(ItemRarity) 색상 (Phase 9)
  // ═══════════════════════════════════════════════════════════════════════

  /// Uncommon 등급 색상 (초록)
  final Color rarityUncommonColor;

  /// Rare 등급 색상 (파랑)
  final Color rarityRareColor;

  /// Epic 등급 색상 (보라)
  final Color rarityEpicColor;

  /// Legendary 등급 색상 (금색)
  final Color rarityLegendaryColor;

  /// Paragraph 캐시 (문자+색상+크기 조합별)
  static final Map<_ParagraphCacheKey, ui.Paragraph> _paragraphCache = {};

  /// 캐시 크기 제한
  static const int _maxCacheSize = 256;

  /// 마지막 사용된 폰트 크기 (크기 변경 시 캐시 무효화)
  static double _lastFontSize = 0;

  @override
  void paint(Canvas canvas, Size size) {
    // 1. 셀 크기 계산
    final cellWidth = size.width / gridWidth;
    final cellHeight = size.height / gridHeight;
    final fontSize = cellHeight * 0.85;

    // 폰트 크기 변경 시 캐시 무효화
    if ((fontSize - _lastFontSize).abs() > 0.5) {
      _paragraphCache.clear();
      _lastFontSize = fontSize;
    }

    // 2. 배경 채우기 (투명도 적용)
    canvas.drawRect(
      Rect.fromLTWH(0, 0, size.width, size.height),
      Paint()..color = backgroundColor.withValues(alpha: backgroundOpacity),
    );

    // 3. 레이어별 렌더링 (z-order 순)
    for (final layer in layers) {
      _renderLayer(canvas, layer, cellWidth, cellHeight, fontSize);
    }
  }

  /// 단일 레이어 렌더링
  void _renderLayer(
    Canvas canvas,
    AsciiLayer layer,
    double cellWidth,
    double cellHeight,
    double fontSize,
  ) {
    for (var row = 0; row < layer.height; row++) {
      for (var col = 0; col < layer.width; col++) {
        final cell = layer.cells[row][col];
        if (cell.isEmpty) continue;

        // 오프셋 적용된 실제 그리드 위치
        final gridX = col + layer.offsetX;
        final gridY = row + layer.offsetY;

        // 그리드 범위 확인
        if (gridX < 0 || gridX >= gridWidth) continue;
        if (gridY < 0 || gridY >= gridHeight) continue;

        // 픽셀 좌표 계산
        final x = gridX * cellWidth;
        final y = gridY * cellHeight;

        // 레이어 투명도를 셀 렌더링에 전달
        _drawCell(
          canvas,
          cell,
          x,
          y,
          cellWidth,
          cellHeight,
          fontSize,
          layer.opacity,
        );
      }
    }
  }

  /// 단일 셀 그리기 (캐시 활용)
  void _drawCell(
    Canvas canvas,
    AsciiCell cell,
    double x,
    double y,
    double cellWidth,
    double cellHeight,
    double fontSize,
    double opacity,
  ) {
    final cacheKey = _ParagraphCacheKey(
      char: cell.char,
      color: cell.color,
      fontSize: fontSize,
      cellWidth: cellWidth,
      opacity: opacity,
    );

    // 캐시 히트 확인
    var paragraph = _paragraphCache[cacheKey];

    if (paragraph == null) {
      // 캐시 미스: 새 Paragraph 생성 (opacity 적용)
      final color = _getColor(cell.color).withValues(alpha: opacity);

      final paragraphBuilder = ui.ParagraphBuilder(
        ui.ParagraphStyle(
          fontFamily: 'JetBrainsMono',
          fontSize: fontSize,
          textAlign: TextAlign.center,
          height: 1.0,
        ),
      );

      paragraphBuilder.pushStyle(ui.TextStyle(color: color));
      paragraphBuilder.addText(cell.char);

      paragraph = paragraphBuilder.build();
      paragraph.layout(ui.ParagraphConstraints(width: cellWidth));

      // 캐시 크기 제한 (LRU 대신 단순 클리어)
      if (_paragraphCache.length >= _maxCacheSize) {
        _paragraphCache.clear();
      }

      _paragraphCache[cacheKey] = paragraph;
    }

    // 셀 중앙에 문자 배치
    final offsetX = x + (cellWidth - paragraph.maxIntrinsicWidth) / 2;
    final offsetY = y + (cellHeight - paragraph.height) / 2;

    canvas.drawParagraph(paragraph, Offset(offsetX, offsetY));
  }

  /// AsciiCellColor를 Flutter Color로 변환 (테마 인식 색상 사용)
  Color _getColor(AsciiCellColor cellColor) {
    return switch (cellColor) {
      AsciiCellColor.background => backgroundColor,
      AsciiCellColor.object => objectColor,
      AsciiCellColor.positive => positiveColor,
      AsciiCellColor.negative => negativeColor,
      // 무기 등급 색상 (Phase 9)
      AsciiCellColor.rarityUncommon => rarityUncommonColor,
      AsciiCellColor.rarityRare => rarityRareColor,
      AsciiCellColor.rarityEpic => rarityEpicColor,
      AsciiCellColor.rarityLegendary => rarityLegendaryColor,
    };
  }

  @override
  bool shouldRepaint(AsciiCanvasPainter oldDelegate) {
    // 레이어 버전으로 빠른 비교
    if (layerVersion != oldDelegate.layerVersion) return true;

    // 동일 참조면 스킵
    if (identical(layers, oldDelegate.layers)) return false;

    // 그리드 설정 변경 확인
    if (gridWidth != oldDelegate.gridWidth ||
        gridHeight != oldDelegate.gridHeight) {
      return true;
    }

    return true;
  }
}
