'use strict';
import mariadb from 'mariadb';

const JOJOAPPS_SERVER_DB = process.env.DB;
const JOJOAPPS_SERVER_DB_PORT = process.env.DB_PORT | 0;
const JOJOAPPS_SERVER_DB_SOCKET = process.env.DB_SOCKET;
const DATABASE_NAME = process.env.DB_NAME;
const DATABASE_USER = process.env.DB_USER;
const DATABASE_PASS = process.env.DB_PASS;

export default class MariadbConnector {
  constructor() {

    let mariaDbConfig = {
      host: JOJOAPPS_SERVER_DB,
      user: DATABASE_USER,
      password: DATABASE_PASS,
      database: DATABASE_NAME,
      connectionLimit: 5
    };

    if (JOJOAPPS_SERVER_DB_PORT === 0) {
      mariaDbConfig.socketPath = JOJOAPPS_SERVER_DB_SOCKET;
    } else {
      mariaDbConfig.port = JOJOAPPS_SERVER_DB_PORT;
    }

    this.pool = mariadb.createPool(mariaDbConfig);
  }

  async getUsernames() {
    let conn;
    let players = [];
    try {
      conn = await this.pool.getConnection();

      const rows_players = await conn.query('SELECT username FROM players');
      for (const { username: u } of rows_players) {
        players.push(u);
      }
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
    return players;
  }

  async addPlayer(username) {
    let conn;
    try {
      conn = await this.pool.getConnection();
      const val = await conn.query(
        'INSERT INTO players value (?, NOW(),NULL)',
        [username]
      );

      return val;
    } catch (err) {
      console.log('error adding player : ', err);
      throw err;
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
  }
  async removePlayer(username) {
    let conn;
    try {
      conn = await this.pool.getConnection();
      const val = await conn.query('DELETE FROM players WHERE username=?', [
        username
      ]);
      return val;
    } catch (err) {
      console.log('error removing player : ', err);
      throw err;
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
  }

  async addNewGame(game) {
    let conn;
    try {
      conn = await this.pool.getConnection();
      const val = await conn.query(
        'INSERT INTO games values (NULL,?,?,?,?,?,?,NOW())',
        [
          game.player1,
          game.player2,
          game.deck,
          convertAdvRulesToString(game.advRules),
          game.status,
          game.data
        ]
      );

      return val.insertId;
    } catch (err) {
      console.log('error adding game : ', err);
      throw err;
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
  }

  removeGameById(gameId) {
    return this.removeGame('DELETE FROM games WHERE id=?', [gameId]);
  }
  removeGamesByPlayer1(player1Name) {
    return this.removeGame('DELETE FROM games WHERE player1=?', [player1Name]);
  }
  removeGamesByPlayerAny(playerName) {
    return this.removeGame('DELETE FROM games WHERE player1=? OR player2=?', [
      playerName,
      playerName
    ]);
  }
  removeGamesByStatus(status) {
    return this.removeGame('DELETE FROM games WHERE status=?', [status]);
  }
  removeGamesByDays(days) {
    return this.removeGame(
      'DELETE FROM games WHERE  datediff(NOW(),last_played) > ?',
      [days]
    );
  }
  removeFinishedGamesByDays(days) {
    return this.removeGame(
      "DELETE FROM games WHERE status='FINISHED' AND datediff(NOW(),last_played) > ?",
      [days]
    );
  }
  removeCreatedGamesByPlayer1(player1Name) {
    return this.removeGame(
      "DELETE FROM games WHERE player1=? AND status='CREATED'",
      [player1Name]
    );
  }

  async removeGame(queryStr, queryArgs) {
    let conn;
    try {
      conn = await this.pool.getConnection();
      const val = await conn.query(queryStr, queryArgs);
      return val;
    } catch (err) {
      console.log('error removing game : ', err);
      throw err;
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
  }

  async getJoinableGamesForPlayer(username) {
    let conn;
    let games = [];
    try {
      conn = await this.pool.getConnection();
      const res = await conn.query(
        "SELECT * FROM games WHERE (status = 'CREATED' OR status = 'PAUSED' OR status = 'PLAYING')  AND(player1 = ? OR player2 = '' OR player2 = ?)",
        [username, username]
      );
      for (const { id, player1, player2, deck, adv_rules } of res) {
        games.push({ id, player1, player2, deck, adv_rules });
      }
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
    return games;
  }
  async getGameById(gameId) {
    let conn;
    let game = {};
    try {
      conn = await this.pool.getConnection();
      game = await conn.query('SELECT * FROM games WHERE id=?', gameId);
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
    return game;
  }
  async updateGameStatus(id, status, player2 = null) {
    let conn;
    let result = {};
    try {
      conn = await this.pool.getConnection();
      if (player2) {
        result = await conn.query(
          'UPDATE games SET player2=?, status = ?, last_played = NOW() WHERE games.id =?',
          [player2, status, id]
        );
      } else {
        result = await conn.query(
          'UPDATE games SET status = ?, last_played = NOW() WHERE games.id =?',
          [status, id]
        );
      }
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
    return result;
  }
  async updateGameData(id, data) {
    let conn;
    let result = {};
    let dataJson = JSON.stringify(data);
    try {
      conn = await this.pool.getConnection();
      result = await conn.query(
        'UPDATE games SET game_data = ?, last_played = NOW() WHERE games.id =?',
        [dataJson, id]
      );
    } finally {
      if (conn && conn !== null) {
        conn.end();
      }
    }
    return result;
  }
}
let convertAdvRulesToString = function (advRulesArray) {
  let advRulestStr = '';
  let last = advRulesArray.length - 1;
  advRulesArray.forEach((rule, index) => {
    advRulestStr += rule;
    if (index < last) {
      advRulestStr += ',';
    }
  });
  return advRulestStr;
};
