class Accounting::Decrease < Accounting::Transaction
  include Validatable
  include Formattable

  enum action: [:credit, :payment, :transfer, :void]

  has_many :allocations, through: :transaction_details, source: :decrease_allocations

  after_initialize :create_transaction_associations, if: :new_record?,
    unless: :transfer?

  after_initialize :create_payment_association, if: :payment?

  after_create :generate_notifications, if: :payment?

  after_destroy :delete_notifications, if: :payment?

  validates :family, presence: true, if: :payment?

  validate :subcategory_required
  validate :student_required, if: :credit?
  validate :payment_response, if: :payment?
  validate :check_totals, if: :new_record?

  def process_payment(params, options={})
    options[:amount] = amount

    payment_detail.payment = params[:payment]
    payment_detail.details =
      case payment_detail.payment.to_sym
      when :check
        params[:check]
      when :paya_credit_card
        params[:paya_credit_card]
      when :payjunction_ach
        ach_params = params[:ach].merge(options)
        process_payment_service(ach_params, :ach)
      when :payjunction_credit_card
        credit_card_params = params[:credit_card].merge(options)
        process_payment_service(credit_card_params, :credit_card)
      else
        {}
      end.merge(distribution: payment_distribution).to_json
  end

  def build_details(subcategory, amount, params, split)
    return transaction_details.build(subcategory: subcategory, amount: amount) unless split

    family.students.each do |student|
      data = params.find { |f| f[:student_id] == student.id }
      next if data.nil? || data[:applied].to_f.zero?

      transaction_details.build(subcategory: subcategory, student: student, amount: data[:applied])
    end
  end

  def autopay_defaults
    @autopay_defaults ||= hash_default(family.school.accounting_subcategories)
  end

  private
    def payment_response
      return if payment_detail&.details.nil?

      response = JSON.parse(payment_detail.details)
      if response['errors']
        response['errors'].each { |e| errors.add(:base, e['message']) }
      elsif response['status'] == 'DECLINED'
        errors.add(:base, response['response']['message'])
      end
    end

    def create_payment_association
      return unless new_record?

      build_payment_detail(payment: :cash)
    end

    def process_payment_service(params, type)
      contact = family.contacts.first.payjunction_contact
      Payments::Payjunction::ProcessPaymentService.call(school, params, contact, type)
    end

    def payment_distribution
      sorted_details = transaction_details.sort_by { |d| d.subcategory.decorate.title }
      sorted_details.group_by(&:subcategory).map do |subcategory, details|
        students = []
        total = 0
        is_split = false

        details.each do |detail|
          students << detail.decorate.student_line_amount
          total += detail.amount
          is_split = true if detail.student_id
        end

        {
          subcategory_name: subcategory.decorate.title,
          students: students,
          total: total,
          is_split: is_split
        }
      end
    end

    def generate_notifications
      Notification::Accounting::PaymentJob.perform_async(id)
    end

    def delete_notifications
      notification_messages.destroy_all
    end
end
