module Spree
  module Api
    class OrdersController < Spree::Api::BaseController

      skip_before_filter :check_for_user_or_api_key, only: :apply_coupon_code
      skip_before_filter :authenticate_user, only: :apply_coupon_code

      # Dynamically defines our stores checkout steps to ensure we check authorization on each step.
      Order.checkout_steps.keys.each do |step|
        define_method step do
          find_order
          authorize! :update, @order, params[:token]
        end
      end

      def cancel
        find_order
        authorize! :update, @order, params[:token]
        @order.cancel!
        render :show
      end

      def create
        authorize! :create, Order
        @order = Order.build_from_api(current_api_user, order_params)
        respond_with(@order, default_template: :show, status: 201)
      end

      def empty
        find_order
        authorize! :update, @order, order_token
        @order.empty!
        @order.update!
        render text: nil, status: 200
      end

      def index
        authorize! :index, Order
        @orders = Order.ransack(params[:q]).result.page(params[:page]).per(params[:per_page])
        respond_with(@orders)
      end

      def show
        find_order
        authorize! :show, @order, order_token
        method = "before_#{@order.state}"
        send(method) if respond_to?(method, true)
        respond_with(@order)
      end

      def update
        find_order(true)
        authorize! :update, @order, order_token
        # Parsing line items through as an update_attributes call in the API will result in
        # many line items for the same variant_id being created. We must be smarter about this,
        # hence the use of the update_line_items method, defined within order_decorator.rb.
        order_params.delete("line_items_attributes")
        if @order.update_attributes(order_params)

          deal_with_line_items if params[:order][:line_items]

          @order.line_items.reload
          @order.update!
          respond_with(@order, default_template: :show)
        else
          invalid_resource!(@order)
        end
      end
      
      def mine
        if current_api_user.persisted?
          @orders = current_api_user.orders.ransack(params[:q]).result.page(params[:page]).per(params[:per_page])
        else
          render "spree/api/errors/unauthorized", status: :unauthorized
        end
      end

      ##
      # Applies a promotion code to the user's most recent order
      # This is a temporary API method until we move to next Spree release which has this logic already in this commit.
      #
      # https://github.com/spree/spree/commit/72a5b74c47af975fc3492580415a4cdc2dc02c0c 
      #
      # Source references:
      #
      # https://github.com/spree/spree/blob/master/frontend/app/controllers/spree/store_controller.rb#L13
      # https://github.com/spree/spree/blob/2-1-stable/frontend/app/controllers/spree/orders_controller.rb#L100
      def apply_coupon_code
        find_order
        authorize! :update, @order, order_token
        @order.coupon_code = params[:coupon_code]
        @order.save

        # https://github.com/spree/spree/blob/2-1-stable/core/lib/spree/promo/coupon_applicator.rb
        result = Spree::Promo::CouponApplicator.new(@order).apply

        result[:coupon_applied?]  ||= false

        # Move flash.notice fields into success if applied
        # An error message is in result[:error]
        if result[:coupon_applied?] && result[:notice]
          result[:success] = result[:notice]
        end

        # Need to turn hash result into object for RABL
        # https://github.com/nesquena/rabl/wiki/Rendering-hash-objects-in-rabl
        @coupon_result = OpenStruct.new(result)
        render status: @coupon_result.coupon_applied? ? 200 : 422
      end

      private
        def deal_with_line_items
          line_item_attributes = params[:order][:line_items]
          line_item_attributes.each_key do |key|
            # need to call .to_hash to make sure Rails 4's strong parameters don't bite
            line_item_attributes[key] = line_item_attributes[key].slice(*permitted_line_item_attributes).to_hash
          end
          @order.update_line_items(line_item_attributes)
        end

        def order_params
          if params[:order]
            params[:order][:payments_attributes] = params[:order][:payments] if params[:order][:payments]
            params[:order][:shipments_attributes] = params[:order][:shipments] if params[:order][:shipments]
            params[:order][:line_items_attributes] = params[:order][:line_items] if params[:order][:line_items]
            params[:order][:ship_address_attributes] = params[:order][:ship_address] if params[:order][:ship_address]
            params[:order][:bill_address_attributes] = params[:order][:bill_address] if params[:order][:bill_address]

            params.require(:order).permit(permitted_order_attributes)
          else
            {}
          end
        end

        def permitted_order_attributes
          if current_api_user.has_spree_role? "admin"
            super << admin_order_attributes
          else
            super
          end
        end

        def permitted_shipment_attributes
          if current_api_user.has_spree_role? "admin"
            super << admin_shipment_attributes
          else
            super
          end
        end

        def admin_shipment_attributes
          [:shipping_method, :stock_location, :inventory_units => [:variant_id, :sku]]
        end

        def admin_order_attributes
          [:import, :number, :completed_at, :locked_at, :channel]
        end

        def next!(options={})
          if @order.valid? && @order.next
            render :show, status: options[:status] || 200
          else
            render :could_not_transition, status: 422
          end
        end

        def find_order(lock = false)
          @order = Spree::Order.lock(lock).find_by!(number: params[:id])
        end

        def before_delivery
          @order.create_proposed_shipments
        end
    end
  end
end