module SpreeAvataxOfficial
  module Transactions
    class RefundService < SpreeAvataxOfficial::Base
      def call(refundable:)
        if full_refund?(refundable)
          create_full_refund(refundable)
        else
          create_partial_refund(refundable)
        end
      end

      private

      def full_refund?(refundable)
        refundable.order_inventory_units == inventory_units(refundable)
      end

      def refundable_class(refundable)
        @refundable_class ||= refundable.class.name.demodulize
      end

      def inventory_units(refundable)
        @inventory_units ||= case refundable_class(refundable)
                             when 'ReturnAuthorization'
                               refundable.inventory_units
                             when 'Refund'
                               refundable.reimbursement.return_items.map(&:inventory_unit)
                             end
      end

      def create_partial_refund(refundable)
        SpreeAvataxOfficial::Transactions::PartialRefundService.call(
          refundable_params(refundable).merge(
            refund_items: refund_items(refundable)
          )
        )
      end

      def refund_items(refundable)
        inventory_units(refundable).group_by(&:line_item).reduce({}) do |ids, (line_item, units)|
          ids.merge!(line_item => [line_item_quantity(units), -1 * line_item_amount(refundable, units)])
        end
      end

      def refundable_params(refundable)
        {
          order:            refundable.order,
          transaction_code: refund_transaction_code(refundable.order_number, refundable.id)
        }
      end

      def line_item_quantity(units)
        return units.count unless units.first.respond_to?(:quantity)

        units.sum(&:quantity)
      end

      def line_item_amount(refundable, units)
        case refundable_class(refundable)
        when 'ReturnAuthorization'
          amount_per_unit(refundable, units) * units.count
        when 'Refund'
          items(refundable, units).sum(&:pre_tax_amount)
        end
      end

      def amount_per_unit(refundable, units)
        items(refundable, units).sum(&:pre_tax_amount) / items(refundable, units).sum(&:quantity)
      end

      def items(refundable, units)
        case refundable_class(refundable)
        when 'ReturnAuthorization'
          units.map(&:line_item).uniq
        when 'Refund'
          units.flat_map(&:return_items).uniq
        end
      end

      def create_full_refund(refundable)
        SpreeAvataxOfficial::Transactions::FullRefundService.call(
          refundable_params(refundable)
        )
      end
    end
  end
end
