import 'dart:async';

import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';

import 'package:memory/models/game/game.dart';
import 'package:memory/models/settings/settings_game.dart';
import 'package:memory/models/settings/settings_global.dart';

part 'game_state.dart';

class GameCubit extends HydratedCubit<GameState> {
  GameCubit()
      : super(GameState(
          currentGame: Game.createNull(),
        ));

  void updateState(Game game) {
    emit(GameState(
      currentGame: game,
    ));
  }

  void refresh() {
    final Game game = Game(
      // Settings
      gameSettings: state.currentGame.gameSettings,
      globalSettings: state.currentGame.globalSettings,
      // State
      isRunning: state.currentGame.isRunning,
      isStarted: state.currentGame.isStarted,
      isFinished: state.currentGame.isFinished,
      animationInProgress: state.currentGame.animationInProgress,
      shufflingInProgress: state.currentGame.shufflingInProgress,
      // Base data
      board: state.currentGame.board,
      // Game data
      guessesCount: state.currentGame.guessesCount,
      pairsFound: state.currentGame.pairsFound,
    );
    game.dump();

    updateState(game);
  }

  void startNewGame({
    required GameSettings gameSettings,
    required GlobalSettings globalSettings,
  }) {
    final Game newGame = Game.createNew(
      // Settings
      gameSettings: gameSettings,
      globalSettings: globalSettings,
    );

    newGame.dump();

    updateState(newGame);
    refresh();
  }

  void quitGame() {
    state.currentGame.isRunning = false;
    refresh();
  }

  void resumeSavedGame() {
    state.currentGame.isRunning = true;
    refresh();
  }

  void deleteSavedGame() {
    state.currentGame.isRunning = false;
    state.currentGame.isFinished = true;
    refresh();
  }

  void allowInteractions(bool active) {
    state.currentGame.animationInProgress = !active;
    refresh();
  }

  void unselectAllTiles() {
    for (int i = 0; i < state.currentGame.board.tiles.length; i++) {
      state.currentGame.board.tiles[i].selected = false;
    }
    refresh();
  }

  void markTilesAsPaired(List<int> tilesIndexes) {
    for (int i = 0; i < tilesIndexes.length; i++) {
      state.currentGame.board.tiles[tilesIndexes[i]].paired = true;
    }
    refresh();
  }

  void tapOnTile(int tileIndex) {
    // Already selected? -> skip
    if (state.currentGame.board.tiles[tileIndex].selected) {
      return;
    }

    // Already paired? -> skip
    if (state.currentGame.board.tiles[tileIndex].paired) {
      return;
    }

    // Flip selected tile
    state.currentGame.board.tiles[tileIndex].selected = true;
    refresh();

    // Is first tile selected?
    List<int> selectedTilesIndexes = [];
    for (int i = 0; i < state.currentGame.board.tiles.length; i++) {
      if (state.currentGame.board.tiles[i].selected) {
        selectedTilesIndexes.add(i);
      }
    }

    if (selectedTilesIndexes.length >= 2) {
      allowInteractions(false);

      // does all selected tiles have same value?
      bool hasSameValue = true;
      for (int i = 1; i < selectedTilesIndexes.length; i++) {
        if (state.currentGame.board.tiles[selectedTilesIndexes[i]].value !=
            state.currentGame.board.tiles[selectedTilesIndexes[i - 1]].value) {
          hasSameValue = false;
        }
      }

      state.currentGame.guessesCount++;
      refresh();

      // timer + check pair + unselect
      Timer(const Duration(seconds: 2), () {
        if (hasSameValue) {
          markTilesAsPaired(selectedTilesIndexes);

          final int itemValue = state.currentGame.board.tiles[selectedTilesIndexes[0]].value;
          state.currentGame.pairsFound.add(itemValue);
          refresh();
        }

        state.currentGame.isFinished = state.currentGame.gameWon;
        refresh();

        unselectAllTiles();
        allowInteractions(true);
      });
    }
  }

  @override
  GameState? fromJson(Map<String, dynamic> json) {
    final Game currentGame = json['currentGame'] as Game;

    return GameState(
      currentGame: currentGame,
    );
  }

  @override
  Map<String, dynamic>? toJson(GameState state) {
    return <String, dynamic>{
      'currentGame': state.currentGame.toJson(),
    };
  }
}