# == Schema Information
#
# Table name: stats
#
#  id                                    :bigint           not null, primary key
#  administrations_partenaires           :bigint           default(0)
#  dossiers_brouillon                    :bigint           default(0)
#  dossiers_cumulative                   :jsonb            not null
#  dossiers_depose_avant_30_jours        :bigint           default(0)
#  dossiers_deposes_entre_60_et_30_jours :bigint           default(0)
#  dossiers_en_construction              :bigint           default(0)
#  dossiers_en_instruction               :bigint           default(0)
#  dossiers_in_the_last_4_months         :jsonb            not null
#  dossiers_not_brouillon                :bigint           default(0)
#  dossiers_termines                     :bigint           default(0)
#  created_at                            :datetime         not null
#  updated_at                            :datetime         not null
#
class Stat < ApplicationRecord
  class << self
    def update_stats
      states = dossiers_states
      stat = Stat.first || Stat.new

      stat.update(
        dossiers_en_construction: states['en_construction'],
        dossiers_en_instruction: states['en_instruction'],
        dossiers_brouillon: states['brouillon'],
        dossiers_depose_avant_30_jours: states['dossiers_depose_avant_30_jours'],
        dossiers_deposes_entre_60_et_30_jours: states['dossiers_deposes_entre_60_et_30_jours'],
        dossiers_not_brouillon: states['not_brouillon'],
        dossiers_termines: states['termines'],
        dossiers_cumulative: cumulative_hash(Dossier.state_not_brouillon, :en_construction_at),
        dossiers_in_the_last_4_months: last_four_months_hash(Dossier.state_not_brouillon, :en_construction_at),
        administrations_partenaires: AdministrateursProcedure.joins(:procedure).merge(Procedure.publiees_ou_closes).select('distinct administrateur_id').count
      )
    end

    private

    def dossiers_states
      query = <<-EOF
      SELECT
        COUNT(*) FILTER ( WHERE state != 'brouillon' ) AS "not_brouillon",
        COUNT(*) FILTER ( WHERE state != 'brouillon' and en_construction_at BETWEEN :one_month_ago AND :now ) AS "dossiers_depose_avant_30_jours",
        COUNT(*) FILTER ( WHERE state != 'brouillon' and en_construction_at BETWEEN :two_months_ago AND :one_month_ago ) AS "dossiers_deposes_entre_60_et_30_jours",
        COUNT(*) FILTER ( WHERE state = 'brouillon' ) AS "brouillon",
        COUNT(*) FILTER ( WHERE state = 'en_construction' ) AS "en_construction",
        COUNT(*) FILTER ( WHERE state = 'en_instruction' ) AS "en_instruction",
        COUNT(*) FILTER ( WHERE state in ('accepte', 'refuse', 'sans_suite') ) AS "termines"
      FROM dossiers
      WHERE hidden_at IS NULL
      EOF

      sanitized_query = ActiveRecord::Base.sanitize_sql([
        query,
        now: Time.zone.now,
        one_month_ago: 1.month.ago,
        two_months_ago: 2.months.ago
      ])

      Dossier.connection.select_all(sanitized_query).first
    end

    def last_four_months_hash(association, date_attribute)
      min_date = 3.months.ago.beginning_of_month.to_date

      association
        .where(date_attribute => min_date..max_date)
        .group("DATE_TRUNC('month', #{date_attribute})")
        .count
        .to_a
        .sort_by { |a| a[0] }
        .map { |e| [I18n.l(e.first, format: "%B %Y"), e.last] }
    end

    def cumulative_hash(association, date_attribute)
      sum = 0
      association
        .where("#{date_attribute} < ?", max_date)
        .group("DATE_TRUNC('month', #{date_attribute})")
        .count
        .to_a
        .sort_by { |a| a[0] }
        .map { |x, y| { x => (sum += y) } }
        .reduce({}, :merge)
    end

    def max_date
      Time.zone.now.beginning_of_month - 1.second
    end
  end
end
