'use strict';

const Quest = require('./Quest');
const Logger = require('./Logger');

/**
 * @property {Map} quests
 */
class QuestFactory {
  constructor() {
    this.quests = new Map();
  }

  add(areaName, id, config) {
    const entityRef = this.makeQuestKey(areaName, id);
    config.entityReference = entityRef;
    this.quests.set(entityRef, { id, area: areaName, config });
  }

  set(qid, val) {
    this.quests.set(qid, val);
  }

  /**
   * Get a quest definition. Use `create` if you want an instance of a quest
   * @param {string} qid
   * @return {object}
   */
  get(qid) {
    return this.quests.get(qid);
  }

  /**
   * Check to see if a player can start a given quest based on the quest's
   * prerequisite quests
   * @param {entityReference} questRef
   * @return {boolean}
   */
  canStart(player, questRef) {
    const quest = this.get(questRef);
    if (!quest) {
      throw new Error(`Invalid quest id [${questRef}]`);
    }

    const tracker = player.questTracker;

    if (tracker.completedQuests.has(questRef) && !quest.config.repeatable) {
      return false;
    }

    if (tracker.isActive(questRef)) {
      return false;
    }

    if (!quest.config.requires) {
      return true;
    }

    return quest.config.requires.every(requiresRef => tracker.isComplete(requiresRef));
  }

  /**
   * @param {GameState} GameState
   * @param {entityReference} qid
   * @param {Player}    player
   * @param {Array}     state     current quest state
   * @return {Quest}
   */
  create(GameState, qid, player, state = []) {
    const quest = this.quests.get(qid);
    if (!quest) {
      throw new Error(`Trying to create invalid quest id [${qid}]`);
    }

    const instance = new Quest(GameState, quest.id, quest.config, player);
    instance.state = state;
    for (const goal of quest.config.goals) {
      const goalType = GameState.QuestGoalManager.get(goal.type);
      instance.addGoal(new goalType(instance, goal.config, player));
    }

    instance.on('progress', (progress) => {
      player.emit('questProgress', instance, progress);
      player.save();
    });

    instance.on('start', () => {
      player.emit('questStart', instance);
      instance.emit('progress', instance.getProgress());
    });

    instance.on('turn-in-ready', () => {
      player.emit('questTurnInReady', instance);
    });

    instance.on('complete', () => {
      player.emit('questComplete', instance);
      player.questTracker.complete(instance.entityReference);

      if (!quest.config.rewards) {
        player.save();
        return;
      }

      for (const reward of quest.config.rewards) {
        try {
          const rewardClass = GameState.QuestRewardManager.get(reward.type);

          if (!rewardClass) {
            throw new Error(`Quest [${qid}] has invalid reward type ${reward.type}`);
          }

          rewardClass.reward(GameState, instance, reward.config, player);
          player.emit('questReward', reward);
        } catch (e) {
          Logger.error(e.message);
        }
      }

      player.save();
    });

    return instance;
  }

  /**
   * @param {string} areaName
   * @param {number} id
   * @return {string}
   */
  makeQuestKey(area, id) {
    return area + ':' + id;
  }
}

module.exports = QuestFactory;