part of '../../video_player_screen.dart';

extension _VideoPlayerPlaybackPromptMethods on VideoPlayerScreenState {
  void _onVideoCompleted(bool completed, {bool skipAutoPlayCountdown = false}) async {
    // Live TV streams are continuous — ignore transient EOF events while an
    // HLS playlist refreshes or crosses a discontinuity.
    if (widget.isLive) return;
    if (!completed) return;
    // Ignore spurious EOF from the old file during an in-place media-source
    // transition (episode swap, transcode restart, channel switch).
    if (_playbackTransition != _PlaybackTransition.idle) return;
    if (_isResolvingCompletionAdjacency) return;

    // mpv does not flip the `pause` property on EOF, so _onPlayingStateChanged
    // never fires false.  Normalize all playback-dependent state.
    unawaited(_wakelockController.setEnabled(false));
    final duration = player?.state.duration;
    unawaited(
      duration != null && duration.inMilliseconds > 0
          ? _sendStoppedProgressOnce(positionOverride: duration)
          : _sendStoppedProgressOnce(),
    );
    _updateMediaControlsPlaybackState();
    unawaited(DiscordRPCService.instance.pausePlayback());
    unawaited(TraktScrobbleService.instance.pausePlayback());
    if (_autoPipEnabled) {
      unawaited(_updateAutoPipState(isPlaying: false));
    }

    // End-of-video sleep timer takes precedence over autoplay / next-episode
    // dialogs: the user explicitly asked to stop after this item.
    final sleepTimerService = SleepTimerService();
    if (sleepTimerService.isEndOfVideoMode && !_completionLatch.triggered) {
      _completionLatch.latch();
      sleepTimerService.notifyVideoCompleted();
      return;
    }
    if (!_canNavigateMediaItems()) {
      if (!_completionLatch.triggered) _completionLatch.latch();
      return;
    }

    var navigationAction = completionNavigationAction(
      hasNext: _nextEpisode != null,
      adjacentStatus: _nextEpisodeStatus,
    );
    if (navigationAction == CompletionNavigationAction.retryAdjacent) {
      _isResolvingCompletionAdjacency = true;
      try {
        await _loadAdjacentEpisodes();
      } finally {
        _isResolvingCompletionAdjacency = false;
      }
      if (!mounted) return;
      navigationAction = completionNavigationAction(hasNext: _nextEpisode != null, adjacentStatus: _nextEpisodeStatus);
      if (navigationAction == CompletionNavigationAction.retryAdjacent) {
        _completionLatch.latch();
        showGlobalErrorSnackBar(t.messages.errorLoadingSeries);
        return;
      }
    }

    if (navigationAction == CompletionNavigationAction.presentNext &&
        !_showPlayNextDialog &&
        !_showStillWatchingPrompt &&
        !_completionLatch.triggered) {
      _completionLatch.latch();

      // PiP: skip dialog (user can't interact), auto-play immediately
      if (PipService().isPipActive.value) {
        unawaited(_playNext());
        return;
      }

      // Capture keyboard mode before async gap
      final isKeyboardMode = PlatformDetector.isTV() && InputModeTracker.isKeyboardMode(context);

      final settings = await SettingsService.getInstance();
      if (!mounted) return;
      final autoPlayEnabled = settings.read(SettingsService.autoPlayNextEpisode);

      if (skipAutoPlayCountdown && autoPlayEnabled) {
        unawaited(_playNext());
        return;
      }

      _setPlayerState(() {
        _showPlayNextDialog = true;
        _autoPlayCountdown = autoPlayEnabled ? 5 : -1;
      });

      // Auto-focus Play Next button on TV when dialog appears (only in keyboard/TV mode)
      if (isKeyboardMode) {
        WidgetsBinding.instance.addPostFrameCallback((_) {
          if (mounted) {
            _playNextConfirmFocusNode.requestFocus();
          }
        });
      }

      if (autoPlayEnabled) {
        _startAutoPlayTimer();
      }
    } else if (navigationAction == CompletionNavigationAction.exit && !_completionLatch.triggered) {
      _completionLatch.latch();
      unawaited(_handleBackButton());
    }
  }

  void _startAutoPlayTimer() {
    if (!_canNavigateMediaItems()) return;
    _autoPlayTimer?.cancel();
    _autoPlayTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (!mounted) {
        timer.cancel();
        return;
      }
      _setPlayerState(() {
        _autoPlayCountdown--;
      });
      if (_autoPlayCountdown <= 0) {
        timer.cancel();
        _playNext();
      }
    });
  }

  void _cancelAutoPlay() {
    _autoPlayTimer?.cancel();
    _unfocusPlayNextPrompt();
    _progressTracker?.resumeAfterStoppedReport();
    // Keep the latch set while playback is still parked at EOF, so duplicate
    // completed signals cannot re-open this prompt. It is re-armed once playback
    // seeks back clear of the end region (see the position listener) or new media loads.
    _setPlayerState(() {
      _showPlayNextDialog = false;
    });
  }

  void _dismissPlaybackPromptForBack() {
    if (_showPlayNextDialog) {
      _cancelAutoPlay();
      return;
    }
    if (_showStillWatchingPrompt) {
      _dismissStillWatching();
    }
  }

  /// Re-arm the end-of-video latch so Play Next can fire again. Callers
  /// decide *when* it is safe to re-arm (media reloaded, or playback moved
  /// back out of the end region); the latch itself refuses while a prompt
  /// or countdown is active.
  void _rearmCompletionLatch() {
    _completionLatch.rearmIfClear(
      promptVisible: _showPlayNextDialog,
      countdownActive: _autoPlayTimer?.isActive == true,
    );
  }

  void _showStillWatchingDialog() {
    // Don't show if auto-play dialog is already visible
    if (_showPlayNextDialog) return;

    final isKeyboardMode = PlatformDetector.isTV() && InputModeTracker.isKeyboardMode(context);

    _setPlayerState(() {
      _showStillWatchingPrompt = true;
      _stillWatchingCountdown = 30;
    });

    if (isKeyboardMode) {
      WidgetsBinding.instance.addPostFrameCallback((_) {
        if (mounted) _stillWatchingContinueFocusNode.requestFocus();
      });
    }

    _stillWatchingTimer?.cancel();
    _stillWatchingTimer = Timer.periodic(const Duration(seconds: 1), (timer) {
      if (!mounted) {
        timer.cancel();
        return;
      }
      _setPlayerState(() {
        _stillWatchingCountdown--;
      });
      if (_stillWatchingCountdown <= 0) {
        timer.cancel();
        _onStillWatchingTimeout();
      }
    });
  }

  void _onStillWatchingTimeout() {
    _unfocusStillWatchingPrompt();
    final currentPlayer = player;
    if (currentPlayer != null) unawaited(_pauseWithPlaybackIntent(currentPlayer));
    _setPlayerState(() {
      _showStillWatchingPrompt = false;
    });
  }

  void _onStillWatchingContinue() {
    _stillWatchingTimer?.cancel();
    _unfocusStillWatchingPrompt();
    SleepTimerService().restartTimer();
    _setPlayerState(() {
      _showStillWatchingPrompt = false;
    });
  }

  void _onStillWatchingPause() {
    _stillWatchingTimer?.cancel();
    _unfocusStillWatchingPrompt();
    final currentPlayer = player;
    if (currentPlayer != null) unawaited(_pauseWithPlaybackIntent(currentPlayer));
    _setPlayerState(() {
      _showStillWatchingPrompt = false;
    });
  }

  void _dismissStillWatching() {
    _stillWatchingTimer?.cancel();
    if (_showStillWatchingPrompt) {
      _unfocusStillWatchingPrompt();
      _setPlayerState(() {
        _showStillWatchingPrompt = false;
      });
    }
  }

  void _unfocusPlayNextPrompt() {
    _playNextCancelFocusNode.unfocus();
    _playNextConfirmFocusNode.unfocus();
  }

  void _unfocusStillWatchingPrompt() {
    _stillWatchingPauseFocusNode.unfocus();
    _stillWatchingContinueFocusNode.unfocus();
  }
}
