import 'package:flutter/foundation.dart'
    show kIsWeb, defaultTargetPlatform, TargetPlatform;
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show SchedulerBinding, SchedulerPhase;

import 'package:asciineverdie/data/game_text_l10n.dart' as game_l10n;
import 'package:asciineverdie/src/core/engine/iap_service.dart';
import 'package:asciineverdie/src/core/model/treasure_chest.dart';
import 'package:asciineverdie/data/story_data.dart';
import 'package:asciineverdie/l10n/app_localizations.dart';
import 'package:asciineverdie/src/shared/animation/ascii_animation_type.dart';

import 'package:asciineverdie/src/core/model/game_state.dart';
import 'package:asciineverdie/src/core/notification/notification_service.dart';
import 'package:asciineverdie/src/features/game/game_session_controller.dart';
import 'package:asciineverdie/src/features/hall_of_fame/hall_of_fame_screen.dart';
import 'package:asciineverdie/src/features/game/widgets/cinematic_view.dart';
import 'package:asciineverdie/src/features/game/widgets/death_overlay.dart';
import 'package:asciineverdie/src/features/game/widgets/victory_overlay.dart';
import 'package:asciineverdie/src/features/game/widgets/notification_overlay.dart';
import 'package:asciineverdie/src/features/game/layouts/desktop_game_layout.dart';
import 'package:asciineverdie/src/features/game/widgets/return_rewards_dialog.dart';
import 'package:asciineverdie/src/features/game/layouts/mobile_carousel_layout.dart';
import 'package:asciineverdie/src/features/settings/settings_screen.dart';
import 'package:asciineverdie/src/features/game/widgets/statistics_dialog.dart';
import 'package:asciineverdie/src/features/game/widgets/help_dialog.dart';
import 'package:asciineverdie/src/core/storage/settings_repository.dart';
import 'package:asciineverdie/src/core/audio/audio_service.dart';
import 'package:asciineverdie/src/features/game/controllers/combat_log_controller.dart';
import 'package:asciineverdie/src/features/game/controllers/game_audio_controller.dart';

/// 게임 진행 화면 (Main.dfm 기반 3패널 레이아웃)
///
/// Phase 7: colorTheme 제거됨, 고정 4색 팔레트 사용
class GamePlayScreen extends StatefulWidget {
  const GamePlayScreen({
    super.key,
    required this.controller,
    this.audioService,
    this.forceCarouselLayout = false,
    this.forceDesktopLayout = false,
  });

  final GameSessionController controller;

  /// 오디오 서비스 (BGM/SFX 재생)
  final AudioService? audioService;

  /// 테스트 모드: 웹에서도 모바일 캐로셀 레이아웃 강제 사용
  final bool forceCarouselLayout;

  /// 테스트 모드: 모바일에서도 데스크톱 3패널 레이아웃 강제 사용
  final bool forceDesktopLayout;

  @override
  State<GamePlayScreen> createState() => _GamePlayScreenState();
}

class _GamePlayScreenState extends State<GamePlayScreen>
    with WidgetsBindingObserver {
  AsciiAnimationType? _specialAnimation;

  // Phase 8: 알림 서비스 (Notification Service)
  late final NotificationService _notificationService;

  // Phase 9: 스토리 Act 추적
  StoryAct _lastAct = StoryAct.prologue;
  bool _showingCinematic = false;

  // 이전 상태 추적 (레벨업/퀘스트/Act 완료 감지용)
  int _lastLevel = 0;
  int _lastQuestCount = 0;
  int _lastPlotStageCount = 0;

  // Phase 2.4: 오디오 컨트롤러
  late final GameAudioController _audioController;

  // Phase 2.5: 전투 로그 컨트롤러
  late final CombatLogController _combatLogController;

  void _checkSpecialEvents(GameState state) {
    // Phase 8: 태스크 변경 시 로그 추가
    _combatLogController.onTaskChanged(state.progress.currentTask.caption);

    // 전투 이벤트 처리 (Combat Events)
    _combatLogController.processCombatEvents(state.progress.currentCombat);

    // 오디오: TaskType 변경 시 BGM 전환 (애니메이션과 동기화)
    _audioController.updateBgmForTaskType(state);

    // 레벨업 감지
    if (state.traits.level > _lastLevel && _lastLevel > 0) {
      _specialAnimation = AsciiAnimationType.levelUp;
      _notificationService.showLevelUp(state.traits.level);
      _combatLogController.addLevelUpLog(state.traits.level);
      // 오디오: 레벨업 SFX (플레이어 채널)
      _audioController.playLevelUpSfx();
      _resetSpecialAnimationAfterFrame();

      // Phase 9: Act 변경 감지 (레벨 기반)
      final newAct = getActForLevel(state.traits.level);
      if (newAct != _lastAct && !_showingCinematic) {
        _lastAct = newAct;

        // 엔딩은 controller.isComplete 상태에서 VictoryOverlay로 처리
        // 일반 Act 전환 시에만 시네마틱 표시
        if (newAct != StoryAct.ending) {
          WidgetsBinding.instance.addPostFrameCallback((_) {
            if (mounted) _showCinematicForAct(newAct);
          });
        }
      }
    }
    _lastLevel = state.traits.level;

    // 퀘스트 완료 감지
    if (state.progress.questCount > _lastQuestCount && _lastQuestCount > 0) {
      _specialAnimation = AsciiAnimationType.questComplete;
      // 완료된 퀘스트 이름 가져오기
      final completedQuest = state.progress.questHistory
          .where((q) => q.isComplete)
          .lastOrNull;
      if (completedQuest != null) {
        _notificationService.showQuestComplete(completedQuest.caption);
        _combatLogController.addQuestCompleteLog(completedQuest.caption);
      }
      // 오디오: 퀘스트 완료 SFX (플레이어 채널)
      _audioController.playQuestCompleteSfx();
      _resetSpecialAnimationAfterFrame();
    }
    _lastQuestCount = state.progress.questCount;

    // Act 완료 감지 (plotStageCount 증가)
    // plotStageCount: 1=프롤로그 진행, 2=프롤로그 완료, 3=Act1 완료...
    // 완료된 스테이지 인덱스 = plotStageCount - 2 (0=프롤로그, 1=Act1, ...)
    if (state.progress.plotStageCount > _lastPlotStageCount &&
        _lastPlotStageCount > 0) {
      _specialAnimation = AsciiAnimationType.actComplete;
      _notificationService.showActComplete(state.progress.plotStageCount - 2);
      _resetSpecialAnimationAfterFrame();
    }
    _lastPlotStageCount = state.progress.plotStageCount;

    // 사망/엔딩 BGM 전환 (Death/Ending BGM Transition)
    _audioController.updateDeathEndingBgm(
      state,
      isGameComplete: widget.controller.isComplete,
    );
  }

  /// Phase 9: Act 시네마틱 표시 (Show Act Cinematic)
  Future<void> _showCinematicForAct(StoryAct act) async {
    if (_showingCinematic) return;

    _showingCinematic = true;
    // 게임 일시 정지
    await widget.controller.pause(saveOnStop: false);

    // 시네마틱 BGM 재생
    _audioController.playCinematicBgm();

    if (mounted) {
      await showActCinematic(context, act);
    }

    // 게임 재개 (BGM은 _updateBgmForTaskType에서 복원됨)
    if (mounted) {
      await widget.controller.resume();
    }
    _showingCinematic = false;
  }

  /// VictoryOverlay 완료 후 명예의 전당 화면으로 이동
  void _handleVictoryComplete() {
    Navigator.of(context).pushReplacement(
      MaterialPageRoute<void>(builder: (context) => const HallOfFameScreen()),
    );
  }

  void _resetSpecialAnimationAfterFrame() {
    // 다음 프레임에서 리셋 (AsciiAnimationCard가 값을 받은 후)
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (mounted) {
        setState(() {
          _specialAnimation = null;
        });
      }
    });
  }

  @override
  void initState() {
    super.initState();
    _notificationService = NotificationService();

    // 오디오 컨트롤러 초기화
    _audioController = GameAudioController(
      audioService: widget.audioService,
      getSpeedMultiplier: () => widget.controller.loop?.speedMultiplier ?? 1,
    );

    // 전투 로그 컨트롤러 초기화
    _combatLogController = CombatLogController(
      onCombatEvent: (event) => _audioController.playCombatEventSfx(event),
    );

    widget.controller.addListener(_onControllerChanged);
    WidgetsBinding.instance.addObserver(this);

    // 초기 상태 설정
    final state = widget.controller.state;
    if (state != null) {
      _lastLevel = state.traits.level;
      _lastQuestCount = state.progress.questCount;
      _lastPlotStageCount = state.progress.plotStageCount;
      _lastAct = getActForLevel(state.traits.level);

      // 초기 BGM 재생 (TaskType 기반)
      _audioController.playInitialBgm(state);
    } else {
      // 상태가 없으면 기본 마을 BGM
      widget.audioService?.playBgm('town');
    }

    // 누적 통계 로드
    widget.controller.loadCumulativeStats();

    // 오디오 볼륨 초기화
    _audioController.initVolumes();

    // Phase 7: 복귀 보상 콜백 설정
    widget.controller.onReturnRewardAvailable = _showReturnRewardsDialog;
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    // 로케일 변경 시 게임 l10n 동기화 (시네마틱 번역 등에 필수)
    final locale = Localizations.localeOf(context);
    game_l10n.setGameLocale(locale.languageCode);
  }

  @override
  void dispose() {
    _notificationService.dispose();
    WidgetsBinding.instance.removeObserver(this);
    widget.controller.removeListener(_onControllerChanged);
    widget.controller.onReturnRewardAvailable = null; // Phase 7: 콜백 정리
    super.dispose();
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState appState) {
    super.didChangeAppLifecycleState(appState);

    // 모바일 환경 확인 (iOS/Android)
    final isMobile =
        !kIsWeb &&
        (defaultTargetPlatform == TargetPlatform.iOS ||
            defaultTargetPlatform == TargetPlatform.android);

    // 앱이 백그라운드로 가거나 비활성화될 때
    if (appState == AppLifecycleState.paused ||
        appState == AppLifecycleState.inactive ||
        appState == AppLifecycleState.detached) {
      // 저장
      _saveGameState();

      // 모바일: 게임 일시정지 + 전체 오디오 정지
      if (isMobile) {
        widget.controller.pause(saveOnStop: false);
        _audioController.pauseAll();
      }
    }

    // 모바일: 앱이 포그라운드로 돌아올 때 전체 재로드
    // (광고 표시 중 또는 최근 광고 시청 후에는 reload 건너뛰기)
    if (appState == AppLifecycleState.resumed && isMobile) {
      _audioController.resumeAll();
      if (!widget.controller.isShowingAd &&
          !widget.controller.isRecentlyShowedAd) {
        _reloadGameScreen();
      } else {
        // 광고 직후: 게임 재개만 (reload 없이 상태 유지)
        widget.controller.resume();
      }
    }
  }

  /// 모바일 재진입 시 전체 화면 재로드
  Future<void> _reloadGameScreen() async {
    // 세이브 파일에서 다시 로드 (치트 모드는 저장된 상태에서 복원)
    await widget.controller.loadAndStart();

    if (!mounted) return;

    // 화면 재생성 (상태 초기화)
    Navigator.of(context).pushReplacement(
      MaterialPageRoute<void>(
        builder: (_) => GamePlayScreen(
          controller: widget.controller,
          audioService: widget.audioService,
        ),
      ),
    );
  }

  Future<void> _saveGameState() async {
    final currentState = widget.controller.state;
    if (currentState == null || !widget.controller.isRunning) return;

    await widget.controller.saveManager.saveState(
      currentState,
      cheatsEnabled: widget.controller.cheatsEnabled,
    );
  }

  /// 뒤로가기 시 저장 확인 다이얼로그
  Future<bool> _onPopInvoked() async {
    final l10n = L10n.of(context);
    final shouldPop = await showDialog<bool>(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(l10n.exitGame),
        content: Text(l10n.saveProgressQuestion),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(false),
            child: Text(l10n.cancel),
          ),
          TextButton(
            onPressed: () {
              Navigator.of(context).pop(true);
            },
            child: Text(l10n.exitWithoutSaving),
          ),
          FilledButton(
            onPressed: () async {
              await _saveGameState();
              if (context.mounted) {
                Navigator.of(context).pop(true);
              }
            },
            child: Text(l10n.saveAndExit),
          ),
        ],
      ),
    );
    return shouldPop ?? false;
  }

  void _onControllerChanged() {
    final state = widget.controller.state;
    if (state != null) {
      _checkSpecialEvents(state);
    }

    // WASM 안정성: 프레임 빌드 중이면 다음 프레임까지 대기
    if (SchedulerBinding.instance.schedulerPhase ==
        SchedulerPhase.persistentCallbacks) {
      SchedulerBinding.instance.addPostFrameCallback((_) {
        if (mounted) {
          setState(() {});
        }
      });
    } else {
      setState(() {});
    }
  }

  /// 캐로셀 레이아웃 사용 여부 판단
  ///
  /// - forceDesktopLayout (테스트 모드) 활성화 시 데스크톱 레이아웃 사용
  /// - forceCarouselLayout (테스트 모드) 활성화 시 캐로셀 레이아웃 사용
  /// - 실제 모바일 플랫폼 (iOS/Android) 시 캐로셀 사용
  bool _shouldUseCarouselLayout(BuildContext context) {
    // 테스트 모드: 데스크톱 레이아웃 강제
    if (widget.forceDesktopLayout) return false;

    // 테스트 모드: 캐로셀 레이아웃 강제
    if (widget.forceCarouselLayout) return true;

    // 웹에서는 3패널 레이아웃 사용 (테스트 모드가 아닌 경우)
    if (kIsWeb) return false;

    // 모바일 플랫폼(iOS/Android)에서는 캐로셀 사용
    final platform = defaultTargetPlatform;
    return platform == TargetPlatform.iOS || platform == TargetPlatform.android;
  }

  /// 복귀 보상 다이얼로그 표시 (Phase 7)
  void _showReturnRewardsDialog(ReturnChestReward reward) {
    // 잠시 후 다이얼로그 표시 (게임 시작 후)
    WidgetsBinding.instance.addPostFrameCallback((_) {
      if (!mounted) return;
      final state = widget.controller.state;
      if (state == null) return;

      ReturnRewardsDialog.show(
        context,
        reward: reward,
        playerLevel: state.traits.level,
        onClaim: (rewards) {
          widget.controller.applyReturnReward(rewards);
        },
      );
    });
  }

  /// 통계 다이얼로그 표시
  void _showStatisticsDialog(BuildContext context) {
    StatisticsDialog.show(
      context,
      session: widget.controller.sessionStats,
      cumulative: widget.controller.cumulativeStats,
    );
  }

  /// 설정 화면 표시
  void _showSettingsScreen(BuildContext context) {
    final settingsRepo = SettingsRepository();
    SettingsScreen.show(
      context,
      settingsRepository: settingsRepo,
      onLocaleChange: (locale) async {
        // 안전한 언어 변경: 전체 화면 재생성
        final navigator = Navigator.of(this.context);
        await widget.controller.pause(saveOnStop: true);
        game_l10n.setGameLocale(locale);
        if (mounted) {
          await widget.controller.resume();
          navigator.pushReplacement(
            MaterialPageRoute<void>(
              builder: (_) => GamePlayScreen(
                controller: widget.controller,
                audioService: widget.audioService,
              ),
            ),
          );
        }
      },
      onBgmVolumeChange: (volume) {
        _audioController.setBgmVolume(volume);
        setState(() {});
      },
      onSfxVolumeChange: (volume) {
        _audioController.setSfxVolume(volume);
        setState(() {});
      },
      onCreateTestCharacter: () async {
        final navigator = Navigator.of(context);
        final success = await widget.controller.createTestCharacter();
        if (success && mounted) {
          // 프론트 화면으로 이동
          navigator.popUntil((route) => route.isFirst);
        }
      },
    );
  }

  /// 부활 처리 핸들러
  Future<void> _handleResurrect() async {
    // 1. 부활 애니메이션 먼저 설정 (DeathOverlay 사라지기 전에)
    setState(() {
      _specialAnimation = AsciiAnimationType.resurrection;
    });

    // 2. 부활 처리 (HP/MP 회복, 장비 구매 - 게임 재개 없음)
    // 이 시점에서 isDead가 false가 되고 DeathOverlay가 사라지지만,
    // _specialAnimation이 이미 설정되어 있어 부활 애니메이션이 표시됨
    await widget.controller.resurrect();

    // 3. 애니메이션 종료 후 게임 재개
    final duration = getSpecialAnimationDuration(
      AsciiAnimationType.resurrection,
    );
    Future.delayed(Duration(milliseconds: duration), () async {
      if (mounted) {
        // 먼저 게임 재개 (status를 running으로 변경)
        // 이렇게 해야 setState 시 UI가 '일시정지' 상태로 보이지 않음
        await widget.controller.resumeAfterResurrection();
        if (mounted) {
          setState(() {
            _specialAnimation = null;
          });
        }
      }
    });
  }

  /// 광고 부활 핸들러 (HP 100% + 아이템 복구 + 10분 자동부활)
  Future<void> _handleAdRevive() async {
    // 1. 부활 애니메이션 먼저 설정 (DeathOverlay 사라지기 전에)
    setState(() {
      _specialAnimation = AsciiAnimationType.resurrection;
    });

    // 2. 광고 부활 처리 (HP 100%, 아이템 복구, 10분 자동부활 버프)
    await widget.controller.adRevive();

    // 3. 애니메이션 종료 후 게임 재개
    final duration = getSpecialAnimationDuration(
      AsciiAnimationType.resurrection,
    );
    Future.delayed(Duration(milliseconds: duration), () async {
      if (mounted) {
        await widget.controller.resumeAfterResurrection();
        if (mounted) {
          setState(() {
            _specialAnimation = null;
          });
        }
      }
    });
  }

  /// 속도 부스트 활성화 핸들러 (Phase 6)
  Future<void> _handleSpeedBoost() async {
    final activated = await widget.controller.activateSpeedBoost();
    if (activated && mounted) {
      _notificationService.show(
        GameNotification(
          type: NotificationType.info,
          title: game_l10n.speedBoostActive,
          duration: const Duration(seconds: 2),
        ),
      );
      setState(() {});
    }
  }

  @override
  Widget build(BuildContext context) {
    final state = widget.controller.state;
    if (state == null) {
      return const Scaffold(body: Center(child: CircularProgressIndicator()));
    }

    final localeKey = ValueKey(game_l10n.currentGameLocale);

    if (_shouldUseCarouselLayout(context)) {
      return _buildMobileLayout(context, state, localeKey);
    }

    return _buildDesktopLayout(context, state, localeKey);
  }

  /// 모바일 캐로셀 레이아웃
  Widget _buildMobileLayout(
    BuildContext context,
    GameState state,
    ValueKey<String> localeKey,
  ) {
    return NotificationOverlay(
      key: localeKey,
      notificationService: _notificationService,
      child: PopScope(
        canPop: false,
        onPopInvokedWithResult: (didPop, result) async {
          if (didPop) return;
          final shouldPop = await _onPopInvoked();
          if (shouldPop && context.mounted) {
            await widget.controller.pause(saveOnStop: false);
            if (context.mounted) {
              Navigator.of(context).pop();
            }
          }
        },
        child: Stack(
          children: [
            MobileCarouselLayout(
              state: state,
              combatLogEntries: _combatLogController.entries,
              speedMultiplier: widget.controller.loop?.speedMultiplier ?? 1,
              onSpeedCycle: () {
                widget.controller.loop?.cycleSpeed();
                setState(() {});
              },
              onSetSpeed: (speed) {
                widget.controller.loop?.setSpeed(speed);
                setState(() {});
              },
              isPaused:
                  !widget.controller.isRunning && _specialAnimation == null,
              onPauseToggle: () async {
                await widget.controller.togglePause();
                setState(() {});
              },
              onSave: _saveGameState,
              onExit: () async {
                final shouldExit = await _onPopInvoked();
                if (shouldExit && context.mounted) {
                  await widget.controller.pause(saveOnStop: false);
                  if (context.mounted) {
                    Navigator.of(context).pop();
                  }
                }
              },
              notificationService: _notificationService,
              specialAnimation: _specialAnimation,
              onLanguageChange: (locale) async {
                final navigator = Navigator.of(context);
                await widget.controller.pause(saveOnStop: true);
                game_l10n.setGameLocale(locale);
                if (context.mounted) {
                  await widget.controller.resume();
                  navigator.pushReplacement(
                    MaterialPageRoute<void>(
                      builder: (_) => GamePlayScreen(
                        controller: widget.controller,
                        audioService: widget.audioService,
                      ),
                    ),
                  );
                }
              },
              onDeleteSaveAndNewGame: () async {
                await widget.controller.pause(saveOnStop: false);
                await widget.controller.saveManager.deleteSave();
                if (context.mounted) {
                  Navigator.of(context).pop();
                }
              },
              bgmVolume: _audioController.bgmVolume,
              sfxVolume: _audioController.sfxVolume,
              onBgmVolumeChange: (volume) {
                _audioController.setBgmVolume(volume);
                setState(() {});
              },
              onSfxVolumeChange: (volume) {
                _audioController.setSfxVolume(volume);
                setState(() {});
              },
              onShowStatistics: () => _showStatisticsDialog(context),
              onShowHelp: () => HelpDialog.show(context),
              cheatsEnabled: widget.controller.cheatsEnabled,
              onCheatTask: () => widget.controller.loop?.cheatCompleteTask(),
              onCheatQuest: () => widget.controller.loop?.cheatCompleteQuest(),
              onCheatPlot: () => widget.controller.loop?.cheatCompletePlot(),
              onCreateTestCharacter: () async {
                final navigator = Navigator.of(context);
                final success = await widget.controller.createTestCharacter();
                if (success && mounted) {
                  navigator.popUntil((route) => route.isFirst);
                }
              },
              autoReviveEndMs: widget.controller.monetization.autoReviveEndMs,
              speedBoostEndMs: widget.controller.monetization.speedBoostEndMs,
              isPaidUser: widget.controller.monetization.isPaidUser,
              onSpeedBoostActivate: _handleSpeedBoost,
              isSpeedBoostActive: widget.controller.isSpeedBoostActive,
              adSpeedMultiplier: widget.controller.adSpeedMultiplier,
              has2xUnlocked: widget.controller.has2xUnlocked,
            ),
            ..._buildOverlays(state),
          ],
        ),
      ),
    );
  }

  /// 데스크톱 3패널 레이아웃
  Widget _buildDesktopLayout(
    BuildContext context,
    GameState state,
    ValueKey<String> localeKey,
  ) {
    return NotificationOverlay(
      key: localeKey,
      notificationService: _notificationService,
      child: PopScope(
        canPop: false,
        onPopInvokedWithResult: (didPop, result) async {
          if (didPop) return;
          final shouldPop = await _onPopInvoked();
          if (shouldPop && context.mounted) {
            await widget.controller.pause(saveOnStop: false);
            if (context.mounted) {
              Navigator.of(context).pop();
            }
          }
        },
        child: Stack(
          children: [
            DesktopGameLayout(
              state: state,
              combatLogEntries: _combatLogController.entries,
              speedMultiplier: widget.controller.loop?.speedMultiplier ?? 1,
              onSpeedCycle: () {
                widget.controller.loop?.cycleSpeed();
                setState(() {});
              },
              isPaused:
                  !widget.controller.isRunning && _specialAnimation == null,
              onPauseToggle: () async {
                await widget.controller.togglePause();
                setState(() {});
              },
              specialAnimation: _specialAnimation,
              cheatsEnabled: widget.controller.cheatsEnabled,
              onCheatTask: () => widget.controller.loop?.cheatCompleteTask(),
              onCheatQuest: () => widget.controller.loop?.cheatCompleteQuest(),
              onCheatPlot: () => widget.controller.loop?.cheatCompletePlot(),
              onShowStatistics: () => _showStatisticsDialog(context),
              onShowHelp: () => HelpDialog.show(context),
              onShowSettings: () => _showSettingsScreen(context),
            ),
            ..._buildOverlays(state),
          ],
        ),
      ),
    );
  }

  /// 공통 오버레이 (사망, 승리)
  List<Widget> _buildOverlays(GameState state) {
    return [
      if (state.isDead && state.deathInfo != null)
        DeathOverlay(
          deathInfo: state.deathInfo!,
          traits: state.traits,
          onResurrect: _handleResurrect,
          onAdRevive: _handleAdRevive,
          isPaidUser: IAPService.instance.isAdRemovalPurchased,
        ),
      if (widget.controller.isComplete)
        VictoryOverlay(
          traits: state.traits,
          stats: state.stats,
          progress: state.progress,
          elapsedMs: state.skillSystem.elapsedMs,
          onComplete: _handleVictoryComplete,
        ),
    ];
  }
}
