module Comee
  module Core
    class Invoice < ApplicationRecord
      ADVANCE = "Advance".freeze
      DELIVERY = "Delivery".freeze
      DIRECT = "Direct".freeze

      INVOICE_TYPES = [ADVANCE, DELIVERY, DIRECT].freeze

      before_validation :generate_invoice_no, if: proc { |inv| inv.invoice_no.nil? }
      before_save :calculate_due_date, unless: proc { |inv| inv.date_issued.nil? }

      belongs_to :sales_order
      belongs_to :pod, optional: true
      belongs_to :invoice_address,
                 -> { where(address_type: ClientAddress::INVOICING_ADDRESS) },
                 class_name: "Comee::Core::ClientAddress"
      has_many :invoice_items
      has_many :additional_items
      has_many :payments

      has_noticed_notifications model_name: "Comee::Core::Notification"

      enum :status, {draft: 0, approved: 1, issued: 2}
      enum :payment_status, {not_paid: 0, partially_paid: 1, fully_paid: 2, overpaid: 3}

      validates :invoice_no, uniqueness: {case_sensitive: false}
      validates :status, :payment_status, presence: true
      validates :total_price, numericality: {greater_than_or_equal_to: 0, allow_nil: true}
      validates :amount_paid, numericality: {greater_than_or_equal_to: 0, allow_nil: true}

      validates :pct_advanced,
                presence: true,
                numericality: {greater_than: 0, less_than_or_equal_to: 100},
                if: -> { advance? }
      validates :pct_advanced,
                absence: true,
                if: -> { delivery? || direct? }
      validates :pod_id,
                presence: true,
                if: -> { delivery? }
      validates :pod_id,
                absence: true,
                if: -> { advance? }

      delegate(:order_number, to: :sales_order, prefix: false)
      delegate(:reference_no, to: :pod, prefix: true, allow_nil: true)

      def delivery?
        invoice_type == DELIVERY
      end

      def advance?
        invoice_type == ADVANCE
      end

      def direct?
        invoice_type == DIRECT
      end

      def calculate_total_price
        self.total_price = calculate_total
      end

      def calculate_vat
        return unless sales_order.client.tax_code == "Inland"

        self.vat = (calculate_total * 0.19).round(2)
      end

      def calculate_due_date
        self.due_date = date_issued + 30.days
      end

      def calculate_total
        items = InvoiceItem.where(invoice_id: id)
        additionals = AdditionalItem.where(invoice_id: id)
        total = (items.sum(:total_price) + additionals.sum(:total_price))
        total *= (pct_advanced / 100) if advance?
        total.round(2)
      end

      def calculate_amount_paid
        payments = Payment.where(invoice_id: id)
        payments.sum(:amount)
      end

      def self.ransackable_attributes(_auth_object = nil)
        %w[
          id
          sales_order_id
          invoice_no
          invoice_type
          date_issued
          ship_name
          delivery_date
          voyage_no
          status
          payment_status
        ]
      end

      def self.ransackable_associations(_auth_object = nil)
        ["sales_order"]
      end

      def generate_invoice_no
        year = Date.current.year.to_s
        self.invoice_no = Util.generate_number("Invoice", "invoice_no", year, "-", 100001)
      end
    end
  end
end