class User < ApplicationRecord
  has_many :goals, dependent: :destroy
  has_many :missions, dependent: :destroy
  belongs_to :level, optional: true
  attr_accessor :remember_token

  VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
  has_secure_password

  validates :name, presence: true, length: {maximum: 64}
  validates :balance, numericality: { greater_than_or_equal_to: 0 }
  validates :email, presence: true, length: {maximum: 255},
            format: {with: VALID_EMAIL_REGEX},
            uniqueness: {case_sensitive: false}
  # allow_nil because has_secure_password will catch it beforehand anyways
  validates :password, presence: true, length: {minimum: 6}, allow_nil: true

  def User.digest(string)
    cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST
                                                : BCrypt::Engine.cost
    BCrypt::Password.create(string, cost: cost)
  end

  # returns: random token
  def User.new_token
    SecureRandom.urlsafe_base64
  end

  def remember
    self.remember_token = User.new_token
    update_attribute(:remember_digest, User.digest(self.remember_token))
  end

  def authenticated?(remember_token)
    return false if remember_digest.nil?
    BCrypt::Password.new(remember_digest).is_password?(remember_token)
  end

  def forget
    update_attribute(:remember_digest, nil)
  end

  def get_coins amount
    self.balance += amount
    self.coins_earned += amount
    @completed_levels = []
    while !self.level.nil? && self.balance >= self.level.cum_value
      @completed_levels.append self.level
      self.level = Level.find_by_id(self.level.id + 1)
    end
    logger.error "@completed_levels = #{@completed_levels}"
  end

  def level_progress
    level_start = level.cum_value - level.value
    ratio = (self.balance - level_start).to_f / level.value
    ratio = 1.0 if ratio > 1.0
    (100 * ratio).to_i
  end

  def check_missions
    @completed_missions = {}
    self.missions.each do |mission|
      if mission.complete?(self)
        self.get_coins mission.value
        logger.error "Unable to delete mission #{mission.id}" unless mission.destroy
        self.missions_completed += 1
        replacement = Mission.generate_mission_for(self, mission.position)
        logger.error "Unable to save mission #{mission.id}" unless replacement.save
        @completed_missions[mission.position] = mission
      end
    end
  end

  def remove_stale_missions(task_id)
    @stale_missions = {}
    self.missions.each do |mission|
      if mission.task_dependency == task_id
        @stale_missions[mission.position] = mission
        logger.error "Unable to delete mission #{mission.id}" unless mission.destroy
        replacement = Mission.generate_mission_for(self, mission.position)
        logger.error "Unable to save mission #{mission.id}" unless replacement.save
      end
    end
  end

  def completed_levels
    @completed_levels = [] if @completed_levels.nil?
    @completed_levels
  end

  def completed_missions
    @completed_missions = {} if @completed_missions.nil?
    @completed_missions
  end

  def stale_missions
    @stale_missions = {} if @stale_missions.nil?
    @stale_missions
  end

  def tasks
    self.goals.map { |goal| goal.tasks }.flatten
  end
end
