class Payment < InvoicingRecord
  belongs_to :billing_company, optional: true
  belongs_to :payment_type, optional: true
  has_many :payment_lines, dependent: :destroy
  has_many :billings, through: :payment_lines
  has_many :billing_lines, through: :billings
  has_many :payment_amounts

  belongs_to :cancel_payment, optional: true, class_name: 'Payment', foreign_key: 'cancel_payment_id'
  has_one :payment, foreign_key: 'cancel_payment_id'

  validates :billing_company_id, :payment_type_id, :payment_date, presence: true
  validates_presence_of :payment_lines

  before_save :preserve_changes, if: -> { closed? && !hash_string_valid? }
  after_create :create_payment_amounts

  accepts_nested_attributes_for :payment_lines, reject_if: -> (attributes) { attributes[:amount].to_f == 0 }, allow_destroy: true

  def cancel_payment?
    (cancel_payment || payment).present?
  end

  def execute_cancel_payment
    canceled_payment = create_cancel_payment!(cancel_payment_params)
    destroy_provision_statements
    save!
    canceled_payment
  end

  def create_payment_amounts
    payment_ratio = total / billings.sum(&:total_ttc)
    payment_ratio = 0 if payment_ratio.nan?
    billing_lines.order(:vat).group_by(&:vat).each do |vat_rate, lines|
      payment_amounts.create(vat_rate: vat_rate, vat: lines.sum(&:vat_amount) * payment_ratio, total_ht: lines.sum(&:total_ht) * payment_ratio)
    end
  end

  def closed?
    payment_lines.any?(&:closed?)
  end

  def hash_string_valid?
    payment_lines.all?(&:hash_string_valid?)
  end

  private

  def cancel_payment_params
    attr = attributes.slice(*(Payment.column_names - %w[id created_at updated_at]))
    attr['total'] = -attr['total']
    attr['payment_lines_attributes'] = payment_lines.map do |line|
      line_attr = line.attributes.slice(*(PaymentLine.column_names - %w[id payment_id created_at updated_at]))
      line_attr['amount'] = -line_attr['amount']
      line_attr
    end
    attr
  end

  def destroy_provision_statements
    Statement.joins(:statement_lines).where(status: 'pending', inv_statement_lines: { billing_line_id: billing_line_ids }).destroy_all
  end

  def preserve_changes
    throw :abort
  end
end
