class StatisticsController < ApplicationController
  before_action :authenticate_user!

  def global_activity
    members = current_organization.members
    @active_members = members.active

    @total_hours = num_movements = 0
    members.each do |m|
      num_movements += m.account.movements.count
      @total_hours += m.account.movements.map do
        |a| (a.amount > 0) ? a.amount : 0
      end.inject(0, :+)
    end
    @total_hours += current_organization.account.movements.
                    map { |a| (a.amount > 0) ? a.amount : 0 }.inject(0, :+)

    @num_swaps = (num_movements + current_organization.account.movements.count) / 2

    from = params[:from].presence.try(:to_date) || DateTime.now.to_date - 5.month
    to = params[:to].presence.try(:to_date) || DateTime.now.to_date
    num_months = (to.year * 12 + to.month) - (from.year * 12 + from.month) + 1
    date = from

    @months_names = []
    @user_reg_months = []
    @num_swaps_months = []
    @hours_swaps_months = []

    num_months.times do
      @months_names << l(date, format: "%B %Y")
      @user_reg_months << members.by_month(date).count

      swaps_members = members.map { |a| a.account.movements.by_month(date) }
      swaps_organization = current_organization.account.movements.by_month(date)
      sum_swaps = (swaps_members.flatten.count + swaps_organization.count) / 2
      @num_swaps_months << sum_swaps

      sum_hours = 0
      swaps_members.flatten.each do |s|
        sum_hours += (s.amount > 0) ? s.amount : 0
      end
      sum_hours += swaps_organization.map do
        |a| (a.amount > 0) ? a.amount : 0
      end.inject(0, :+)
      sum_hours = sum_hours / 3600.0 if sum_hours > 0
      @hours_swaps_months << sum_hours

      date = date.next_month
    end
  end

  def inactive_users
    @members = current_organization.members.active
  end

  def demographics
    members = current_organization.members
    @age_counts = age_counts(members)
    @gender_counts = gender_counts(members)
  end

  def last_login
    @members = current_organization.members.active.joins(:user).
               order("users.current_sign_in_at ASC NULLS FIRST")
  end

  def without_offers
    @members = current_organization.members.active
  end

  def type_swaps
    offers = current_organization.posts.
             where(type: "Offer").joins(:transfers, transfers: :movements).
             select("posts.tags, posts.category_id, SUM(movements.amount) as
                     sum_of_transfers, COUNT(transfers.id) as
                     count_of_transfers").
             where("movements.amount > 0").
             group("posts.tags, posts.category_id, posts.updated_at")

    @offers = count_offers_by_label(offers).to_a.each { |a| a.flatten!(1) }.
              sort_by(&:last).reverse
  end

  def all_transfers
    @transfers = current_organization.
                 all_transfers_with_accounts.
                 page(params[:page]).
                 per(20)
  end

  protected

  def count_offers_by_label(offers)
    # Cannot use Hash.new([0, 0]) because then counters[key][0] += n
    # will modify directly the "global default" instead of
    # first assigning a new array with the zeroed counters.
    counters = Hash.new { |h, k| h[k] = [0, 0] }

    offers.each do |offer|
      labels_for_offer(offer).each do |labels|
        counters[labels][0] += offer.sum_of_transfers
        counters[labels][1] += offer.count_of_transfers
      end
    end
    add_ratios(counters)

    counters
  end

  def add_ratios(counters)
    total_count = counters.values.map { |_, counts| counts }.sum

    counters.each do |_, v|
      v << v[1].to_f / total_count
    end
  end

  def labels_for_offer(offer)
    tag_labels = offer.tags.presence ||
                 [t("statistics.type_swaps.without_tags")]

    category_label = offer.category.try(:name) ||
                     t("statistics.type_swaps.without_category")

    [category_label].product(tag_labels)
  end

  def age_counts(members)
    members.each_with_object(Hash.new(0)) do |member, counts|
      age = compute_age(member.user_date_of_birth)

      age_label = age_group_labels.detect do |range, _|
        range.include? age
      end.try(:last) || t("statistics.demographics.unknown")

      counts[age_label] += 1
    end
  end

  def compute_age(date_of_birth)
    return unless date_of_birth

    age_in_days = Date.today - date_of_birth
    (age_in_days / 365.26).to_i
  end

  def age_group_labels
    {
      0..17   => "-17",
      18..24  => "18-24",
      25..34  => "25-34",
      35..44  => "35-44",
      45..54  => "45-54",
      55..64  => "55-64",
      65..100 => "65+"
    }
  end

  def gender_counts(members)
    members.each_with_object(Hash.new(0)) do |member, counts|
      gender = member.user_gender
      gender_label = if gender.present?
        t("simple_form.options.user.gender.#{gender}")
      else
        t("statistics.demographics.unknown")
      end
      counts[gender_label] += 1
    end
  end
end
