require 'active_support/concern'

module Iord
  module Nested
    extend ActiveSupport::Concern

    included do
      #alias_method_chain :collection_url, :nested
      alias_method_chain :resource_url, :nested
      alias_method_chain :edit_resource_url, :nested

      alias_method_chain :set_resource, :nested
      alias_method_chain :build_resource_info, :nested
    end

    def parent_models
      @parent_models ||= default(:parent_models) || []
    end

    def parents_collection_names
      @parents_collection_names ||= default(:parents_collection_names) || []
    end

    def parent_collection_name
      @parent_collection_name ||=
        default(:parent_collection_name) || resource_name_u.pluralize.to_sym
    end

    def parent_resource_name
      @parent_resource_name ||=
        default(:parent_resource_name) || resource_name_u.to_sym
    end

    def index_collection
      @collection = @parent.public_send(parent_collection_name)
    end

    def create_resource
      @resource = resource_class.new resource_params
      if @parent.respond_to? "#{parent_resource_name}=".to_sym
        @parent.public_send "#{parent_resource_name}=".to_sym, @resource
      elsif @parent.respond_to? parent_collection_name
        collection = @parent.public_send parent_collection_name
        collection << @resource
      end
    end

    def resource_url_with_nested(resource = nil)
      resource ||= @resource
      resource = nil unless has_collection?
      self.public_send resource_url_method, *(@parents + [resource])
    end

    def collection_url_with_nested
      self.public_send collection_url_method, *@parents
    end

    def edit_resource_url_with_nested(resource = nil)
      resource ||= @resource
      resource = nil unless has_collection?
      self.public_send "edit_#{resource_url_method}".to_sym, *(@parents + [resource])
    end

    def set_resource_with_nested
      @parents = Array.new
      @parent = nil
      if self.parent_models.empty?
        path = request.path[1..-1].split('/').reject { |x| x =~ /^#{params[:action]}|[a-f0-9]+(\.#{params[:format]})?$/ }
        path = path[0..-2].map { |x| x.singularize.camelize.constantize }
        @parent_models = path
      end
      self.parent_models.each_with_index do |model, index|
        param = "#{model.name.underscore}_id".to_sym
        collection = model
        unless @parents.empty?
          collection = @parent
          collection_name = self.parents_collection_names[index]
          if collection_name.nil?
            collection_name = model.name.underscore.pluralize.to_sym
          end
          collection = @parent.public_send(collection_name)
        end
        @parent = collection.find(params[param])
        @parents << @parent
      end
      return unless self.class.resource_based_actions.include? params[:action].to_sym
      if @parent.respond_to? parent_collection_name
        @resource = @parent.public_send(parent_collection_name).find(params[:id])
      else
        @resource = @parent.public_send(parent_resource_name)
        if @resource.nil?
          @resource = resource_class.new
          @parent.public_send("#{parent_resource_name}=".to_sym, @resource)
        end
      end
    end

    def build_resource_info_with_nested
      return if @parent_path
      build_resource_info_without_nested

      parent_path = self.parent_models.map { |x| x.name.underscore }
      @parent_path = parent_path.join('/')
    end
  end
end

