'use strict';
const bcrypt = require('bcryptjs');
const Data   = require('./Data');

/**
 * Representation of a player's account
 *
 * @property {string} username
 * @property {Array<string>} characters List of character names in this account
 * @property {string} password Hashed password
 * @property {boolean} banned Whether this account is banned or not
 */
class Account {

  /**
   * @param {Object} data Account save data
   */
  constructor(data) {
    this.username   = data.username;
    this.characters = data.characters || [];
    this.password   = data.password;
    this.banned = data.banned || false;
    this.deleted = data.deleted || false;
    // Arbitrary data bundles are free to shove whatever they want in
    // WARNING: values must be JSON.stringify-able
    this.metadata = data.metadata || {};
  }

  /**
   * @return {string}
   */
  getUsername() {
    return this.username;
  }

  /**
   * @param {string} username
   */
  addCharacter(username) {
    this.characters.push({ username: username, deleted : false});
  }

  /**
   * @param {string} name
   * @return {boolean}
   */
  hasCharacter(name) {
    return this.characters.find(c => c.username === name);
  }

  /**
   * @param {string} name Delete one of the chars
   */
  deleteCharacter(name) {
    var picked = this.characters.find(c => c.username === name);
    picked.deleted = true;
    this.save();
  }

  /**
   * @param {string} name Removes the deletion of one of the chars
   */
  undeleteCharacter(name) {
    var picked = this.characters.find(c => c.username === name);
    picked.deleted = false;
    this.save();
  }

  /**
   * @param {string} password Unhashed password. Is hashed inside this function
   */
  setPassword(pass) {
    this.password = this._hashPassword(pass);
    this.save();
  }

  /**
   * @param {string} pass Unhashed password to check against account's password
   * @return {boolean}
   */
  checkPassword(pass) {
    return bcrypt.compareSync(pass, this.password);
  }

  /**
   * @param {function} callback after-save callback
   */
  save(callback) {
    Data.save('account', this.username, this.serialize(), callback);
  }

  /**
   * Set this account to banned
    There is no unban because this can just be done by manually editing the account file
   */
  ban() {
    this.banned = true;
    this.save();
  }

  /**
   * Set this account to deleted
   There is no undelete because this can just be done by manually editing the account file
   */
  deleteAccount() {
    this.characters.forEach(char => {
      this.deleteCharacter(char.username);
    });
    this.deleted = true;
    this.save();
  }

  /**
   * @private
   * @param {string} pass
   * @return {string} Hashed password
   */
  _hashPassword(pass) {
    const salt = bcrypt.genSaltSync(10);
    return bcrypt.hashSync(pass, salt);
  }

  /**
   * Gather data from account object that will be persisted to disk
   *
   * @return {Object}
   */
  serialize() {
    const {
      username,
      characters,
      password,
      metadata,
    } = this;

    return {
      username,
      characters,
      password,
      metadata
    };
  }
}

module.exports = Account;