$: << File.dirname(__FILE__) + '/../lib'


require 'rubygems'

require 'rack'
require "rails/all"
require 'rack/test'
require 'rails/test_help'
require 'rspec/rails'
require 'active_model'
require 'support/helper_methods'
require 'support/integration_helpers'
require_relative '../lib/make_resourceful'
require_relative '../lib/resourceful/base'

RSpec.configure do |config|
  config.mock_with :mocha
  config.include HelperMethods
  config.include IntegrationHelpers
end

module MetaClass
  def metaclass
    class << self; self; end
  end
end

def should_be_called(&block)
  pstub = stub
  pstub.expects(:call).instance_eval(&(block || proc {}))
  proc { |*args| pstub.call(*args) }
end

def stub_model(name)
  model = Class.new do
    include Resourceful::Serialize::Model

    def self.to_s
      @name
    end

    def initialize(attrs = {})
      attrs.each do |k, v|
        self.stubs(k).returns(v)
      end
    end

    def inspect
      "#<#{self.class.send(:instance_variable_get, '@name')}>"
    end
  end
  model.send(:instance_variable_set, '@name', name)
  model
end

def stub_const(name)
  unless Object.const_defined?(name)
    obj = Object.new
    obj.extend MetaClass
    obj.metaclass.send(:define_method, :to_s) { name.to_s }
    obj.metaclass.send(:alias_method, :inspect, :to_s)
    Object.const_set(name, obj)
  end
  Object.const_get(name)
end

def stub_list(size, name = nil, &block)
  list = Array.new(size) { |i| name ? stub("#{name}_#{i}") : stub }
  list.each(&block) if block
  list
end

module Spec::Matchers
  def have_any(&proc)
    satisfy { |a| a.any?(&proc) }
  end
end

module ControllerMocks
  def mock_kontroller(*to_extend)
    options = to_extend.last.is_a?(Hash) ? to_extend.slice!(-1) : {}
    @kontroller = Class.new
    @kontroller.extend Resourceful::Maker
    to_extend.each(&@kontroller.method(:extend))

    @hidden_actions = Resourceful::ACTIONS.dup
    @kontroller.stubs(:hidden_actions).returns(@hidden_actions)
    @kontroller.stubs(:plural_action?).returns(false)
    @kontroller.stubs(:include)
    @kontroller.stubs(:before_action)
    @kontroller.stubs(:helper_method)
  end

  def mock_controller(*to_extend)
    mock_kontroller
    @controller = @kontroller.new
    to_extend.each(&@controller.method(:extend))
  end

  def mock_builder(inherited = false)
    @builder = stub
    @builder.stubs(:response_for)
    @builder.stubs(:apply)
    @builder.stubs(:instance_eval).yields(@buildercc )
    @builder.stubs(:inherited?).returns(inherited)
    Resourceful::Base.stubs(:made_resourceful).returns([])
    Resourceful::Builder.stubs(:new).returns(@builder)
  end

  def create_builder
    @builder = Resourceful::Builder.new(@kontroller)
    class << @builder
      alias_method :made_resourceful, :instance_eval
    end
  end

  def responses
    @kontroller.resourceful_responses
  end

  def callbacks
    @kontroller.resourceful_callbacks
  end

  def parents
    @kontroller.parents
  end

  # Evaluates the made_resourceful block of mod (a module)
  # in the context of @builder.
  # @builder should be initialized via create_builder.
  def made_resourceful(mod)
    mod.included(@builder)
  end
end

module RailsMocks
  attr_reader :response, :request, :controller, :kontroller

  def included(mod)
    require 'ruby-debug'
    debugger
  end

  def mock_resourceful(options = {}, &block)
    options = {
      :name => "things"
    }.merge options

    init_kontroller options
    init_routes options

    stub_const(options[:name].singularize.camelize)
    kontroller.make_resourceful(&block)

    init_controller options
  end

  def assigns(name)
    controller.instance_variable_get("@#{name}")
  end

  def redirect_to(opts)
    Spec::Rails::Matchers::RedirectTo.new(request, opts)
  end

  def render_template(path)
    Spec::Rails::Matchers::RenderTemplate.new(path.to_s, @controller)
  end

  private

  def init_kontroller(options)
    @kontroller = Class.new ActionController::Base
    @kontroller.extend Resourceful::Maker
    @kontroller.extend MetaClass

    @kontroller.metaclass.send(:define_method, :controller_name) { options[:name] }
    @kontroller.metaclass.send(:define_method, :controller_path) { options[:name] }
    @kontroller.metaclass.send(:define_method, :inspect) { "#{options[:name].camelize}Controller" }
    @kontroller.metaclass.send(:alias_method, :to_s, :inspect)

    @kontroller.send(:define_method, :controller_name) { options[:name] }
    @kontroller.send(:define_method, :controller_path) { options[:name] }
    @kontroller.send(:define_method, :inspect) { "#<#{options[:name].camelize}Controller>" }
    @kontroller.send(:alias_method, :to_s, :inspect)
    @kontroller.send(:include, ControllerMethods)
    @kontroller.send(:view_paths=, [File.join(File.dirname(__FILE__), 'views')])

    @kontroller
  end

  def init_routes(options)
    ActionController::Routing::Routes.clear!
    route_block = options[:routes] || proc { |map| map.resources options[:name] }
    ActionController::Routing::Routes.draw(&route_block)
  end

  def init_controller(options)
    @controller = kontroller.new
    @request = ActionController::TestRequest.new
    @response = ActionController::TestResponse.new

    @controller.request = @request
    @controller.response = @response
    @request.accept = '*/*'
    @request.env['HTTP_REFERER'] = 'http://test.host'

    @controller
  end

  def action_params(action, params = {})
    params.merge case action
                 when :show, :edit, :destroy, {:id => 12}
                 when :update, {:id => 12, :thing => {}}
                 when :create, {:thing => {}}
                 else {}
                 end
  end

  def action_method(action)
    method case action
           when :index, :show, :edit, :new, :get
           when :update, :put
           when :create, :post
           when :destroy, :delete
           end
  end

  module ControllerMethods
    # From rspec-rails ControllerExampleGroup

    def render(options=nil, deprecated_status_or_extra_options=nil, &block)
      if ::Rails::VERSION::STRING >= '2.0.0' && deprecated_status_or_extra_options.nil?
        deprecated_status_or_extra_options = {}
      end

      unless block_given?
        if @template.respond_to?(:finder)
          (class << @template.finder; self; end).class_eval do
            define_method :file_exists? do; true; end
          end
        else
          (class << @template; self; end).class_eval do
            define_method :file_exists? do; true; end
          end
        end
        (class << @template; self; end).class_eval do
          define_method :render_file do |*args|
            @first_render ||= args[0] unless args[0] =~ /^layouts/
            @_first_render ||= args[0] unless args[0] =~ /^layouts/
          end

          define_method :_pick_template do |*args|
            @_first_render ||= args[0] unless args[0] =~ /^layouts/
            PickedTemplate.new
          end
        end
      end

      super(options, deprecated_status_or_extra_options, &block)
    end

    class PickedTemplate
      def render_template(*ignore_args); end
      def render_partial(*ignore_args);  end
    end
  end

end

