class Pdf::Accounting::SchoolBalanceService < Pdf::NewApplicationService
  def initialize(school, params={})
    @school = school
    @statement_type = params['type'].to_sym
    @subcategory_ids = params['category_ids']
    @group_by = params['group_by'].to_sym
  end

  private
    def header
      content = ActionController::Base.new.render_to_string(
        partial: 'pdf/headers/school_info.html.haml',
        locals: {
          school: @school,
          side_content_partial: 'pdf/accounting/school_balance_header.html.haml',
          side_content: { date: Date.current }
        }
      )
      { content: content, spacing: 25 }
    end

    def body
      ActionController::Base.new.render_to_string(
        partial: 'pdf/accounting/school_balance.html.haml',
        locals: set_local_variables
      )
    end

    def set_local_variables
      set_summary_and_or_detail
      {}.tap do |props|
        props[:statement_type] = @statement_type
        props[:subcategories] = subcategories

        props[:total_by_subcategory] = @total_by_subcategory if [
          :summary,
          :both
        ].include?(@statement_type)

        if [:details, :both].include?(@statement_type)
          props[:group_by] = @group_by
          props[:families] = families

          props[:group_by_totals] = if @group_by == :family
            @subcategory_total_by_family
          else
            @family_total_by_subcategory
          end
        end
      end
    end

    def set_summary_and_or_detail
      set_defaults
      increase_details.each { |d| calculate_totals(d, :increase) }
      decrease_details.each { |d| calculate_totals(d, :decrease) }
    end

    def calculate_totals(detail, type)
      label = type == :increase ? :outstanding : :unallocated
      balance = detail.balance(type)
      family_id = detail.accounting_transaction.family_id

      @total_by_subcategory[label][detail.subcategory_id] += balance
      @total_by_subcategory[:totals][label] += balance

      if @group_by == :family
        @subcategory_total_by_family[label][family_id][detail.subcategory_id] += balance
        @subcategory_total_by_family[label][family_id][:total] += balance
      else
        @family_total_by_subcategory[label][detail.subcategory_id][family_id] += balance
        @family_total_by_subcategory[label][detail.subcategory_id][:total] += balance
      end
    end

    def increase_details
      @increase_details ||= @school.accounting_transaction_details
        .includes(:increase_allocations, :accounting_transaction)
        .with_subcategory(@subcategory_ids)
        .without_void(true)
        .opened
        .only_increases
    end

    def decrease_details
      @decrease_details ||= @school.accounting_transaction_details
        .includes(:decrease_allocations, :accounting_transaction)
        .with_subcategory(@subcategory_ids)
        .without_void(true)
        .opened
        .only_decreases
    end

    def subcategories
      @subcategories ||= @school.accounting_subcategories.where(id: @subcategory_ids).ordered
    end

    def families
      @families ||= @school.families.ordered
    end

    def subcategory_defaults(by_family=false)
      subcategories.map do |subcategory|
        value = by_family ? family_defaults.merge(total: 0) : 0
        [subcategory.id, value]
      end.to_h
    end

    def family_defaults(by_subcategory=false)
      families.map do |family|
        value = by_subcategory ? subcategory_defaults.merge(total: 0) : 0
        [family.id, value]
      end.to_h
    end

    def set_defaults
      @total_by_subcategory = {
        outstanding: subcategory_defaults,
        unallocated: subcategory_defaults,
        totals: { outstanding: 0, unallocated: 0 }
      }

      @subcategory_total_by_family = {
        outstanding: family_defaults(true),
        unallocated: family_defaults(true),
        totals: { outstanding: 0, unallocated: 0 }
      }

      @family_total_by_subcategory = {
        outstanding: subcategory_defaults(true),
        unallocated: subcategory_defaults(true),
        totals: { outstanding: 0, unallocated: 0 }
      }
    end
end
