# frozen_string_literal: true

require_relative '../api_resource'

module ErpIntegration
  module Fulfil
    module Resources
      class SalesOrder < ApiResource
        self.model_name = 'sale.sale'

        class MissingIdempotencyKey < ErpIntegration::Error
          def initialize
            super(
              '[ERP Integration] Missing the required reference attribute. ' \
                'Setting the reference prevents Fulfil from processing the same sales ' \
                "order twice. For this reason, it's required to provide it as a safeguard."
            )
          end
        end

        # Allows cancelling the entire sales order in Fulfil.
        # @param id [Integer|String] The ID of the to be cancelled order.
        # @return [boolean] Whether the sales order was cancelled successfully or not.
        def cancel(id)
          client.put("model/sale.sale/#{id}/cancel")
          true

        # Fulfil will return an 400 (a.k.a. "Bad Request") status code when a sales order couldn't
        # be cancelled. If a sales order isn't cancellable by design, no exception should be raised.
        #
        # See the Fulfil's documentation for more information:
        # https://developers.fulfil.io/rest_api/model/sale.sale/#cancel-a-sales-order
        rescue ErpIntegration::HttpError::BadRequest
          false
        # Workaround: Fulfil api does not return a json when status code is 200 (a.k.a. "Ok")
        # and faraday is having an error when trying to parse it. Let's skip the parse error
        # and move on.
        rescue Faraday::ParsingError
          true
        end

        # Confirm the order on Fulfil.
        # @param id [Integer|String] The ID of the to be confirmed order.
        # @return [boolean] Whether the sales order was confirmed successfully or not.
        def confirm(id)
          client.put("model/sale.sale/#{id}/confirm")
          true

        # Fulfil will return an 400 (a.k.a. "Bad Request") status code when a sales order couldn't
        # be confirmed.
        rescue ErpIntegration::HttpError::BadRequest
          false
        # Workaround: Fulfil api does not return a json when status code is 200 (a.k.a. "Ok")
        # and faraday is having an error when trying to parse it. Let's skip the parse error
        # and move on.
        rescue Faraday::ParsingError
          true
        end

        # Creates the sales order in one API call in Fulfil.
        #
        # Fulfil allows two ways of creating a new sales order. Through their
        # regular/generic API endpoint just like any other resource or through the
        # sales channel.
        #
        # The benefit of using the Sales Channel route is it allows creating the sales
        #
        # @see https://developers.fulfil.io/guides/creating-orders/#create-order-api
        #
        # The sales order number is used as an idempotency key for Fulfil to prevent
        # sales orders from being processed twice. Due to that it's a required attribute.
        #
        # @see https://developers.fulfil.io/guides/creating-orders/#channel_identifier
        #
        # @param sales_channel [String, Integer] The ID of the sales channel in Fulfil.
        # @param attributes [Hash] The attributes of the sales order
        # @return [ErpIntegration::SalesOrder]
        def create_for(sales_channel, attributes:)
          raise MissingIdempotencyKey if attributes['reference'].blank? && attributes[:reference].blank?

          ErpIntegration::SalesOrder.new(
            client.put("model/sale.channel/#{sales_channel}/create_order", [attributes])
          )
        end

        # Allows duplicating the entire sales order in Fulfil.
        # @param id [Integer|String] The ID of the to be duplicated order.
        # @return [Array|boolean] Whether the sales order was duplicated successfully or not.
        def duplicate(id)
          duplicated_order_id = client.put("model/sale.sale/#{id}/copy").first
          ErpIntegration::SalesOrder.new(id: duplicated_order_id)

        # Fulfil will return an 400 (a.k.a. "Bad Request") status code when a sales order couldn't
        # be duplicated.
        rescue ErpIntegration::HttpError::BadRequest
          false
        end

        # Process the order on Fulfil.
        # @param id [Integer|String] The ID of the to be processed order.
        # @return [boolean] Whether the sales order was processed successfully or not.
        def process(id)
          client.put("model/sale.sale/#{id}/process")
          true

        # Fulfil will return an 400 (a.k.a. "Bad Request") status code when a sales order couldn't
        # be processed.
        rescue ErpIntegration::HttpError::BadRequest
          false
        # Workaround: Fulfil api does not return a json when status code is 200 (a.k.a. "Ok")
        # and faraday is having an error when trying to parse it. Let's skip the parse error
        # and move on.
        rescue Faraday::ParsingError
          true
        end

        # Creates a return order in Fulfil.
        #
        # @param id [Integer, String] The ID of the sales order
        # @param options [Hash] The attributes needed for the return.
        def return!(id, options)
          client.put("model/sale.sale/#{id}/return_order", options)
        rescue ErpIntegration::HttpError::BadRequest
          false
        end
      end
    end
  end
end