'use strict';
import DuelController from '../client-server-shared/duel-controller.js';

export default function OnlineDuelSync(
  ioServer,
  mariadbConnector,
  gameId,
  firstPlayer,
  isNewGame,
  gameOptions,
  allHeroesJson
) {
  let io = ioServer;
  let mdb = mariadbConnector;
  let isNew = isNewGame;
  let heroesJson = allHeroesJson;
  let players = new Map();

  // internal method to load game data from DB
  let loadGameData = async function () {
    let gameDetails = {};
    gameDetails.game_data = {};
    try {
      let game = await mdb.getGameById(gameId);
      gameDetails = game[0];
      try {
        gameDetails.game_data = JSON.parse(game[0].game_data);
      } catch (err) {
        console.log('err converting JSON data :>> ', err);
      }
    } catch (err) {
      console.log('Error retreiving game : ', err.message);
    }
    if (!gameDetails.game_data.game) {
      gameDetails.game_data.game = {};
    }

    return gameDetails;
  };

  // Internal method to send message to game room
  let chat = function (name, message) {
    io.to(gameId).emit('message', {
      from: name,
      text: message
    });
  };

  //dataStore will be used by the duel controller to save data and notify
  let dataStore = {
    load() { },
    // Save new game data after statemachine processed it
    save(data) {
      try {
        mdb.updateGameData(gameId, data);
        io.to(gameId).emit('update-game-data', data);
        return true;
      } catch (err) {
        return false;
      }
    },
    getHeroesJson() {
      return heroesJson;
    }
  };

  let messenger = {
    chat(from, message) {
      chat(from, message);
    }
  };

  // Create Duel Controller (game state machine)
  // Give it possibility to store data and notify players with dataStore
  let duelController = new DuelController(dataStore, messenger);

  // Is used to notify duel controller of a player action
  // gives access to method : .endTurn(data)
  let duelCtrlProxy = duelController.duelControllerProxy;

  // Set array of players with first one that joined
  players.set(firstPlayer.playerName, firstPlayer);

  /** @type {import('type/game').TH_GameDataStore} */
  let game_data = {};
  loadGameData().then(gameDetails => {
    game_data = gameDetails.game_data;
    game_data.game.updatePlayerState = 'both';
    // Join 1st player to game room and notify it
    // Also send the game data (might be empty for new game but it is ok)
    firstPlayer
      .getSocket()
      .join(gameId, () => {
        chat(
          firstPlayer.playerName,
          firstPlayer.playerName +
          ' has entered the game, waiting for second player...'
        );
      })
      .emit('update-game-data', game_data)
      // This allows the player to send message to the game room
      .on('chat', message => {
        chat(firstPlayer.playerName, message);
      })
      .on('end-turn', data => {
        duelCtrlProxy.endTurn(data);
      });

    // If not new game, set status to PLAYING right now
    // Otherwise we wait second player before doing so
    if (!isNew) {
      mdb.updateGameStatus(gameId, 'PLAYING');
      // Resume game right away, we don't need second player yet as it might be 1st player turn
      duelController.resumeGame(game_data);
    }
  });

  // Public method to add a second player to the game
  let addPlayer = function (player) {
    let res = true;
    // Doing some checks
    if (players.size === 1) {
      if (players.has(player.playerName)) {
        console.log(
          'ERROR :  this player is already there ! ',
          player.playerName
        );
        return false;
      }
      players.set(player.playerName, player);
      loadGameData().then(gameDetails => {
        game_data = gameDetails.game_data;
        game_data.game.updatePlayerState = 'both';
        // second player is in ! notify the game room and send latest game data (ok if empty)
        player
          .getSocket()
          .join(gameId, () => {
            chat(
              player.playerName,
              player.playerName + ' has entered the game'
            );
          })
          .emit('update-game-data', game_data)
          // Allow 2nd player to chat in the room
          .on('chat', message => {
            chat(player.playerName, message);
          })
          .on('end-turn', data => {
            duelCtrlProxy.endTurn(data);
          });
        // Both players connected to a new game, set status to PLAYING
        if (isNew) {
          isNew = false;
          mdb.updateGameStatus(gameId, 'PLAYING', player.playerName);

          // Start the duel controller
          duelController.startNewGame(
            firstPlayer.playerName,
            player.playerName,
            gameOptions
          );
        }
      });
    } else {
      console.log(
        'Already 2 players, weird :' +
        player.playerName +
        ' was trying to join : ',
        players
      );
      res = false;
    }
    return res;
  };

  // Handle player disconnection / reconnection without impacting other player
  // If no player left, this object will be destroyed by games-manager
  let playerLeft = function (player) {
    let playerLeaving = players.get(player.playerName);
    if (playerLeaving) {
      playerLeaving.getSocket().leave(gameId);

      playerLeaving.getSocket().removeAllListeners('chat');
      playerLeaving.getSocket().removeAllListeners('end-turn');

      chat(player.playerName, player.playerName + ' has left the game');
      players.delete(player.playerName);
    }
  };

  // Public methods to know if there are currently any player tie to this game
  let hasPlayers = function () {
    return players.size > 0;
  };
  let isNewlyCreatedGame = function () {
    return isNew;
  };
  return {
    addPlayer,
    playerLeft,
    hasPlayers,
    isNewlyCreatedGame
  };
}
