class EasyEarnedValue < ActiveRecord::Base

  belongs_to :project
  belongs_to :baseline, class_name: "Project", foreign_key: "baseline_id"
  has_many :data, dependent: :destroy, class_name: 'EasyEarnedValueData', foreign_key: 'easy_earned_value_id'

  validates :project, :baseline, :name, :type, presence: true

  before_save :check_project_default

  scope :for_reloading, lambda {
    eager_load(:project).preload(:data).where(projects: { status: Project::STATUS_ACTIVE })
  }

  def check_project_default
    if project_default? && project_default_changed?
      EasyEarnedValue.where(project_id: project_id).update_all(project_default: false)
    end
  end

  def type_name
    self.class.type_name
  end

  def reload_planned
    raise NotImplementedError
  end

  def reload_actual(date)
    raise NotImplementedError
  end

  def other_values
    # Get last data containing every values
    last_values = data.order(:date).reverse_order.where.not(ac: nil, ev: nil, pv: nil).first

    unless last_values
      return {}
    end

    {
      date: last_values.date,
      sv: last_values.ev - last_values.pv,
      spi: last_values.ev / last_values.pv,
      cv: last_values.ev - last_values.ac,
      cpi: last_values.ev / last_values.ac
    }
  end

end

class EasyEarnedValue::EstimatedHours < EasyEarnedValue

  def self.type_name
    I18n.t('easy_earned_values.types.estimated_hours.name')
  end

  # TODO: Make it better. This method is expensive for SQL queries.
  def reload_planned
    return unless baseline

    date_changes = Hash.new { |hash, key| hash[key] = 0 }

    project_ids = baseline.self_and_descendants.pluck(:id)
    issues = Issue.where(project_id: project_ids).where.not(start_date: nil, due_date: nil, estimated_hours: nil).to_a

    unless issues.any?
      self.planned_loaded = true
      save
      return
    end
    issues.each do |issue|
      # If dates are equal - duration is 0 but for this formula desirable 1
      estimated_per_day = issue.estimated_hours.to_f / (issue.duration.to_f + 1)

      issue.start_date.upto(issue.due_date).each do |date|
        date_changes[date] += estimated_per_day
      end
    end

    planned_values = {}
    value = 0

    # Values in all range (cumulative)
    from, to = issues.flat_map{|i| [i.start_date, i.due_date] }.minmax
    from.upto(to).each do |date|
      value += date_changes[date]
      planned_values[date] = value
    end

    # Delete previouse (just in case)
    data.update_all(pv: nil)

    # Create or update
    planned_values.each do |date, pv|
      planned = data.find_or_initialize_by(date: date)
      planned.pv = pv
      planned.save
    end

    # Mark as loaded
    self.planned_loaded = true
    save
  end

  def reload_actual(date)
    project_ids = project.self_and_descendants.pluck(:id)
    issue_ids = Issue.where(project_id: project_ids).pluck(:id)

    # Can't user just project because of differnt entity types
    # Can't user just issues because of moving
    ac = TimeEntry.where(project_id: project_ids, issue_id: issue_ids).sum(:hours)
    ev = Issue.where(id: issue_ids, project_id: project_ids).
               where.not(estimated_hours: nil, done_ratio: nil).
               pluck('SUM(estimated_hours * done_ratio / 100)').first

    actual = data.find{|d| d.date == date } || data.build(date: date)
    actual.ac = ac
    actual.ev = ev
    actual.save
  end

end
