'use strict';
import PlayerId from './players/player-id';
import DuelController from './game-server/duel-controller';
import MariadbConnector from './db/mariadb-connector';
import ServerToolListner from './tools/server-tool-listener';

function Server() {
  console.log('Starting 12 heroes server');

  const io = require('socket.io');
  const server = io.listen(2610);

  let mariadbConn = new MariadbConnector();

  let authorizedPlayers = new Map();
  let connectedPlayers = new Map();
  let authorizedPlayersNames = new Set();

  // let players = [];

  // let addNewPlayer = function (playerSocket, playerName) {
  //   console.log('players length : ' + players.length + ' : ' + players);
  //   if (players.length < 2) {
  //     let newPlayer = new PlayerId(playerSocket, playerName);
  //     console.log('push player : ' + newPlayer);
  //     players.push(newPlayer);
  //   }
  //   if (players.length === 2) {
  //     let duelController = new DuelController(players[0], players[1]);
  //   }
  // };

  server.on('connection', function(socket) {
    console.log('A player connected with id : ' + socket.id);

    socket.on('disconnect', reason => {
      try {
        removeAllGamesCreatedByPlayer(connectedPlayers.get(socket.id));
      } catch (err) {
        console.log('Error removing games : ' + err.message);
      }
      forceClientsReloadGames();
      if (
        reason === 'client namespace disconnect' ||
        reason === 'server namespace disconnect'
      ) {
        console.log('A player disconnected with id : ' + socket.id);
        let playerName = connectedPlayers.get(socket.id);
        if (playerName) {
          connectedPlayers.delete(socket.id);
          authorizedPlayers.get(playerName).setConnected(false);
          authorizedPlayers.get(playerName).setSocket(null);
          console.log(playerName + ' disconnected');
        }
      } else {
        console.log('disconnected unexpectidly with reason : ' + reason);
      }
    });
    socket.on('connect', () => {
      console.log('Connected to server');
    });
    socket.on('auth', async (playerName, callback) => {
      console.log(' Received auth message, player name : ' + playerName);
      await updatePlayersFromDb();

      let response = {};
      let kickout = false;
      if (!authorizedPlayersNames.has(playerName)) {
        response = {
          res: 'ko',
          message: playerName + ' Not found'
        };
        kickout = true;
      }
      // If the player is already connected and active
      else if (
        authorizedPlayers.get(playerName).isConnected() &&
        authorizedPlayers.get(playerName).getSocket().connected
      ) {
        response = {
          res: 'ko',
          message: playerName + ' already connected'
        };
        kickout = true;
      } else {
        // Player marked as connected but socket is down, clean it
        if (authorizedPlayers.get(playerName).isConnected()) {
          authorizedPlayers
            .get(playerName)
            .getSocket()
            .disconnect(true);
        } else {
          // In case server did not detect disconnection and did not clean the pending created games
          try {
            removeAllGamesCreatedByPlayer(playerName);
          } catch (err) {
            console.log('Error removing games : ' + err.message);
          }
        }
        response = {
          res: 'ok',
          message: playerName + ' connected'
        };
        authorizedPlayers.get(playerName).setConnected(true);
        authorizedPlayers.get(playerName).setSocket(socket);
        connectedPlayers.set(socket.id, playerName);
      }

      callback(response);
      if (kickout === true) {
        setTimeout(() => {
          socket.disconnect(true);
        }, 600);
      }
    });

    socket.on('games-list', async (playerName, callback) => {
      console.log(' Received games-list message, player name : ' + playerName);
      let response = {};
      try {
        let games = await getJoinableGames(playerName);
        response = {
          res: 'ok',
          message: games
        };
      } catch (error) {
        response = {
          res: 'ko',
          message: 'Error from server'
        };
      }
      callback(response);
    });

    socket.on('create-game', async (game, callback) => {
      let response = {};
      try {
        let id = await addGameDb(game);
        response = {
          res: 'ok',
          message: id
        };
        console.log('Force all clients to reload their games');
        forceClientsReloadGames();
      } catch (error) {
        response = {
          res: 'ko',
          message: 'Error from server'
        };
      }
      callback(response);
    });

    socket.on('remove-created-game', async (username, callback) => {
      try {
        removeAllGamesCreatedByPlayer(username);
      } catch (err) {
        console.log('Error removing games : ' + err.message);
      }
      callback(true);
      forceClientsReloadGames();
    });
  });

  let updatePlayersFromDb = async function() {
    try {
      let usernames = await mariadbConn.getUsernames();
      authorizedPlayersNames = new Set(usernames);
      console.log('authorizedPlayersNames update : ', authorizedPlayersNames);
      authorizedPlayersNames.forEach(name => {
        if (!authorizedPlayers.has(name)) {
          console.log('New username authorized :', name);
          authorizedPlayers.set(name, new PlayerId(name));
        }
      });
      return 'ok';
    } catch (error) {
      console.log('Error getting usernames from DB : ', error);
      return error.Error;
    }
  };

  let getCurrentListAuthorizedPlayers = function() {
    return [...authorizedPlayersNames];
  };
  let addPlayerDb = async function(username) {
    console.log('add player in db : ' + username);
    try {
      let response = await mariadbConn.addPlayer(username);
      authorizedPlayersNames.add(username);
      authorizedPlayers.set(username, new PlayerId(username));
      return 'OK ' + JSON.stringify(response);
    } catch (err) {
      return 'KO ' + err.message;
    }
  };
  let removePlayerDb = async function(username) {
    console.log('remove player in db : ' + username);
    try {
      let response = await mariadbConn.removePlayer(username);
      authorizedPlayersNames.delete(username);
      authorizedPlayers.delete(username);
      return 'OK ' + JSON.stringify(response);
    } catch (err) {
      return 'KO ' + err.message;
    }
  };

  let addGameDb = async function(game) {
    try {
      let response = await mariadbConn.addNewGame(game);
      return response;
    } catch (err) {
      console.log('KO ' + err.message);
      throw err;
    }
  };

  let removeGameDb = async function(method, ...args) {
    try {
      let response;
      switch (method) {
        case 'byId':
          response = await mariadbConn.removeGameById(args[0]);
          break;
        case 'byPlayer1':
          response = await mariadbConn.removeGamesByPlayer1(args[0]);
          break;
        case 'byPlayerAny':
          response = await mariadbConn.removeGamesByPlayerAny(args[0]);
          break;
        case 'byStatus':
          response = await mariadbConn.removeGamesByStatus(args[0]);
          break;
        case 'byDays':
          response = await mariadbConn.removeGamesByDays(args[0]);
          break;
        case 'finishedByDays':
          response = await mariadbConn.removeFinishedGamesByDays(args[0]);
          break;

        default:
          return 'KO : Unknown method to remove game from DB : ' + method;
      }
      return 'OK ' + JSON.stringify(response);
    } catch (err) {
      return 'KO ' + err.message;
    }
  };

  let getJoinableGames = async function(username) {
    try {
      let games = await mariadbConn.getJoinableGamesForPlayer(username);
      return games;
    } catch (err) {
      console.log('KO : not able to get games : ' + err);
      throw err;
    }
  };

  let removeAllGamesCreatedByPlayer = async function(playerName) {
    let result = await mariadbConn.removeCreatedGamesByPlayer1(playerName);
    return result;
  };

  let forceClientsReloadGames = function() {
    server.emit('reload-games-list');
  };

  return {
    updatePlayersFromDb,
    getCurrentListAuthorizedPlayers,
    addPlayerDb,
    removePlayerDb,
    addGameDb,
    removeGameDb,
    getJoinableGames
  };
}

let server = new Server();

let serverToolListner = new ServerToolListner(server);
serverToolListner.listen(1664);