class Accounting::Invoice < ApplicationRecord
  audited

  attr_accessor :transaction_charges

  enum status: { draft: 0, shared: 1 }

  belongs_to :school
  belongs_to :family, class_name: '::Family'

  has_many :invoice_charges, class_name: 'Accounting::InvoiceCharge',
    foreign_key: :invoice_id, validate: false, dependent: :destroy
  has_many :charges, through: :invoice_charges
  has_many :transaction_details, through: :charges
  has_many :allocations, through: :charges, source: :increase_allocations
  has_many :notification_messages, foreign_key: :associated_id, as: :associated,
    dependent: :destroy, inverse_of: :associated

  after_initialize :set_school, if: :new_record?

  before_save :attach_charges, if: :transaction_charges

  after_save :mark_as_paid_or_unpaid

  after_update :generate_notifications

  validates :due_date, :posted_on, presence: true

  scope :by_family, ->(family) { where(family_id: family) if family }
  scope :by_paid, ->(flag) { where(paid: flag) unless flag.nil? }

  scope :by_status, ->(status) do
    return if status.nil?

    case status
    when 'past_due'
      where(status: :shared, paid: false).where('due_date < ?', Date.current)
    when 'unpaid'
      where(status: :shared, paid: false)
    when 'paid'
      where(status: :shared, paid: true)
    else
      where(status: status)
    end
  end

  def self.last_generated_tracking_number
    order(tracking_number: :desc).limit(1).pluck(:tracking_number).first || 1000
  end

  def total
    charges.map(&:amount).reduce(:+)
  end

  def balance
    transaction_details.reduce(0) { |total, t| total + t.balance(:increase) }
  end

  def mark_as_paid_or_unpaid
    update_column(:paid, charges.all?(&:closed))
  end

  private
    def set_school
      self.school_id = family.school_id if school_id.nil?
    end

    def attach_charges
      self.charges = transaction_charges
    end

    def generate_notifications
      return unless saved_change_to_status?

      if shared?
        Notification::Accounting::InvoiceJob.perform_async(id)
      else
        notification_messages.destroy_all
      end
    end
end
