class Spree::Api::BaseController < Spree::BaseController
  before_filter :check_http_authorization
  before_filter :load_resource
  skip_before_filter :verify_authenticity_token, :if => lambda { admin_token_passed_in_headers }

  respond_to :json

  def index
    respond_with(@collection) do |format|
      format.json { render :json => @collection.to_json(collection_serialization_options) }
    end
  end

  def show
    respond_with(@object) do |format|
      format.json { render :json => @object.to_json(object_serialization_options) }
    end
  end

  def create
    if @object.save
      render :text => "Resource created\n", :status => 201, :location => object_url
    else
      respond_with(@object.errors, :status => 422)
    end
  end

  def update
    if @object.update_attributes(params[object_name])
      render :nothing => true
    else
      respond_with(@object.errors, :status => 422)
    end
  end

  def admin_token_passed_in_headers
    request.headers['HTTP_AUTHORIZATION'].present?
  end

  def access_denied
    render :text => 'access_denied', :status => 401
  end

  # Generic action to handle firing of state events on an object
  def event
    valid_events = model_class.state_machine.events.map(&:name)
    valid_events_for_object = @object ? @object.state_transitions.map(&:event) : []

    if params[:e].blank?
      errors = t('api.errors.missing_event')
    elsif valid_events_for_object.include?(params[:e].to_sym)
      @object.send("#{params[:e]}!")
      errors = nil
    elsif valid_events.include?(params[:e].to_sym)
      errors = t('api.errors.invalid_event_for_object', :events => valid_events_for_object.join(','))
    else
      errors = t('api.errors.invalid_event', :events => valid_events.join(','))
    end

    respond_to do |wants|
      wants.json do
        if errors.blank?
          render :nothing => true
        else
          render :json => errors.to_json, :status => 422
        end
      end
    end
  end

  protected
    def model_class
      "Spree::#{controller_name.classify}".constantize
    end

    def object_name
      controller_name.singularize
    end

    def load_resource
      if member_action?
        @object ||= load_resource_instance
        instance_variable_set("@#{object_name}", @object)
      else
        @collection ||= collection
        instance_variable_set("@#{controller_name}", @collection)
      end
    end

    def load_resource_instance
      if new_actions.include?(params[:action].to_sym)
        build_resource
      elsif params[:id]
        find_resource
      end
    end

    def parent
      nil
    end

    def find_resource
      if parent.present?
        parent.send(controller_name).find(params[:id])
      else
        model_class.includes(eager_load_associations).find(params[:id])
      end
    end

    def build_resource
      if parent.present?
        parent.send(controller_name).build(params[object_name])
      else
        model_class.new(params[object_name])
      end
    end

    def collection
      return @search unless @search.nil?
      params[:search] = {} if params[:search].blank?
      params[:search][:meta_sort] = 'created_at.desc' if params[:search][:meta_sort].blank?

      scope = parent.present? ? parent.send(controller_name) : model_class.scoped

      @search = scope.metasearch(params[:search]).relation.limit(100)
      @search
    end

    def collection_serialization_options
      {}
    end

    def object_serialization_options
      {}
    end

    def eager_load_associations
      nil
    end

    def object_errors
      {:errors => object.errors.full_messages}
    end

    def object_url(object = nil, options = {})
      target = object ? object : @object
      if parent.present?
        send "admin_#{parent[:model_name]}_#{object_name}_url", parent, target, options
      else
        send "admin_#{object_name}_url", target, options
      end
    end

    def collection_actions
      [:index]
    end

    def member_action?
      !collection_actions.include? params[:action].to_sym
    end

    def new_actions
      [:new, :create]
    end

  private
    def check_http_authorization
      if request.headers['HTTP_AUTHORIZATION'].blank?
        render :text => "Access Denied\n", :status => 401
      end
    end
end
