module Spree
  module Api
    ProductsController.class_eval do
      # Push your product with interface name and id. When the product doesn't exist, it gets created, otherwise it gets updated.
      # Noteable attributes:
      # - interface_name: string that identifies your system
      # - id: the id in your system
      def push
        authorize! :push, Product
        params[:product][:available_on] ||= Time.now
        set_up_shipping_category
        # check if interfacing attributes are available
        unless params[:interface_name] && params[:id]
          render :text => { :exception => 'interface_name and/or id params missing' }.to_json, :status => 422 and return
        end
        ia = Spree::InterfaceAttribute.where(interfaceable_type: Spree::Product, interface_name: params[:interface_name], name: 'id', value: params[:id]).first
        if ia
          @product = ia.interfaceable
          @product.update_attributes(product_params)
        else
          @product = Spree::Product.new(product_params)
        end

        if @product.save
          variants_params.each do |variant_attribute|
            # make sure the product is assigned before the options=
            @product.variants.create({ product: @product }.merge(variant_attribute))
          end

          option_types_params.each do |name|
            option_type = OptionType.where(name: name).first_or_initialize do |option_type|
              option_type.presentation = name
              option_type.save!
            end

            @product.option_types << option_type unless @product.option_types.include?(option_type)
          end

          # update product properties ([{'name': '', 'value': },{'name': '', 'value': }])
          Spree::ProductPropertyImporter.import(@product,properties_params)
          # update product taxon
          Spree::ProductTaxonImporter.import(@product,params[:product][:categ_id])

          Spree::InterfaceAttribute.find_or_create_by(interfaceable: @product, interface_name: params[:interface_name], name: 'id', value: params[:id])
          # schedule update of images
          RetrieveImagesFromThrWorker.perform_async(@product.id, images_params)

          respond_with(@product, :status => 201, :default_template => :show, :location => api_products_url)
        else
          invalid_resource!(@product)
        end
      end

      # Push products in bulk. When pushing a lot of products at once, it is wiser to make one big call to avoid the overhead of multiple single product calls.
      # This method expects an array of products. 
      def push_bulk
        authorize! :push_bulk, Product
        unless params[:interface_name] && params[:products]
          render :text => { :exception => 'interface_name and/or products param missing' }.to_json, :status => 422 and return
        end
        
        params[:products].each do |product_parameters|
          UpdateProductWorker.perform_async(params[:interface_name], product_parameters)
        end
        render :text => { :message => "Product bulk push successful. This message doesn't mean that everything went fine, only that everything is processed." }.to_json, :status => 201
      end

      # Retract the product from the site.
      # Noteable attributes:
      # - interface_name: string that identifies your system
      # - id: the id in your system
      def retract
        ia = Spree::InterfaceAttribute.where(interfaceable_type: Spree::Product, interface_name: params[:interface_name], name: 'id', value: params[:id]).first
        unless ia
          raise ActiveRecord::RecordNotFound
        end
        @product = find_product(ia.interfaceable_id)
        authorize! :destroy, @product
        @product.destroy
        respond_with(@product, :status => 204, :location => api_products_url)
      end

      # Receive stock for a product
      # Noteable attributes:
      # - interface_name: string that identifies your system
      # - id: the id in your system
      # - quantity: the quantity you whish to receive
      def receive
        authorize! :receive, Product
        render :text => { :exception => 'quantity parameter missing' }.to_json, :status => 422 and return unless params[:stock_movement][:quantity]
        ia = Spree::InterfaceAttribute.where(interfaceable_type: Spree::Product, interface_name: params[:interface_name], name: 'id', value: params[:id]).first
        unless ia
          raise ActiveRecord::RecordNotFound
        end
        product = find_product(ia.interfaceable_id)
        # important to note is that this limits you to only have 1 stock location
        params[:stock_movement][:stock_item] = product.stock_items.first
        @stock_movement = Spree::StockMovement.new(quantity: params[:stock_movement][:quantity], stock_item: params[:stock_movement][:stock_item])
        if @stock_movement.save
          render :text => { :message => "Quantity changed by #{@stock_movement.quantity}. Total quantity now #{product.total_on_hand}." }.to_json, :status => 201
        else
          invalid_resource!(@stock_movement)
        end
      end

      private

        def images_params
          params[:product].delete(:images) || []
        end

        def properties_params
          params[:product].delete(:properties) || []
        end
    end
  end
end