import '../../core/config/app_constants.dart';
import '../entities/pokemon.dart';
import 'game_state.dart';

/// Règles du jeu, pures (aucune IO, aucun widget). Entièrement testable.
class GameEngine {
  const GameEngine();

  /// Nouvelle partie : réinitialise tout sauf le bestScore.
  GameState newGame(GameState s) {
    return s.copyWith(
      lives: AppConstants.startingLives,
      skips: AppConstants.startingSkips,
      hints: AppConstants.startingHints,
      sessionCorrectCount: 0,
      currentScore: 0,
      status: GameStatus.loading,
      isHintUsed: false,
    );
  }

  /// Démarre une manche avec un nouveau Pokémon.
  GameState startRound(GameState s, Pokemon p, {required bool isShiny}) {
    return s.copyWith(
      currentPokemon: p,
      isShiny: isShiny,
      isHintUsed: false,
      status: GameStatus.playing,
    );
  }

  /// Soumet une réponse. Renvoie le nouvel état + la nature du résultat.
  GuessOutcome submitGuess(GameState s, String guess) {
    final pokemon = s.currentPokemon;
    if (pokemon == null || s.status != GameStatus.playing) {
      return GuessOutcome(s, GuessResult.invalid);
    }

    final normalizedGuess = _normalize(guess.trim().toLowerCase());
    final normalizedActual = _normalize(pokemon.name.toLowerCase());

    if (normalizedGuess == normalizedActual) {
      final newSession = s.sessionCorrectCount + 1;
      final gained = s.isShiny ? AppConstants.pointsShiny : AppConstants.pointsNormal;
      final newScore = s.currentScore + gained;
      final hintBonus =
          newSession % AppConstants.hintBonusEvery == 0 ? 1 : 0;
      final skipBonus =
          newSession % AppConstants.skipBonusEvery == 0 ? 1 : 0;

      final newState = s.copyWith(
        currentPokemon: pokemon.copyWith(isCaught: true, isSeen: true),
        currentScore: newScore,
        bestScore: newScore > s.bestScore ? newScore : s.bestScore,
        sessionCorrectCount: newSession,
        hints: s.hints + hintBonus,
        skips: s.skips + skipBonus,
        status: GameStatus.roundWon,
      );
      return GuessOutcome(newState, GuessResult.correct);
    }

    final remainingLives = s.lives - 1;
    if (remainingLives <= 0) {
      return GuessOutcome(
        s.copyWith(lives: 0, status: GameStatus.gameOver),
        GuessResult.gameOver,
      );
    }
    return GuessOutcome(
      s.copyWith(lives: remainingLives),
      GuessResult.wrong,
    );
  }

  /// Consomme un indice si disponible et non déjà utilisé.
  GameState useHint(GameState s) {
    if (s.isHintUsed || s.hints <= 0) return s;
    return s.copyWith(isHintUsed: true, hints: s.hints - 1);
  }

  /// Consomme un skip si disponible.
  GameState useSkip(GameState s) {
    if (s.skips <= 0) return s;
    return s.copyWith(skips: s.skips - 1);
  }

  static String _normalize(String input) {
    const withDia = 'ÀÁÂÃÄÅàáâãäåÒÓÔÕÖØòóôõöøÈÉÊËèéêëÇçÌÍÎÏìíîïÙÚÛÜùúûüÿÑñ';
    const withoutDia = 'AAAAAAaaaaaaOOOOOOooooooEEEEeeeeCcIIIIiiiiUUUUuuuuyNn';
    var output = input;
    for (var i = 0; i < withDia.length; i++) {
      output = output.replaceAll(withDia[i], withoutDia[i]);
    }
    return output;
  }
}
