'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) => {
      if (reason === 'client 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
        if (authorizedPlayers.get(playerName).isConnected()) {
          authorizedPlayers.get(playerName).getSocket().disconnect(true);
        }
        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 games = await addGameDb(game);
        response = {
          res: "ok",
          message: games
        };
      } catch (error) {
        response = {
          res: "ko",
          message: 'Error from server'
        };
      }
      callback(response);
    });

  });

  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 'OK ' + JSON.stringify(response);
    } catch (err) {
      return 'KO ' + err.message;
    }
  };

  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;
    }
  };

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

}

let server = new Server();

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