module Hobo
  module Model
    # FIXME: should be FindByBelongsTo maybe
    module FindFor

    def self.included(base)
      base.alias_method_chain :method_missing, :find_for
    end

    def method_missing_with_find_for(name, *args, &block)
      if name.to_s =~ /(.*)_by_(.*)/
        # name matches the general form

        collection_name = $1.to_sym
        anchor_association_name = $2.to_sym
        if (refl = self.class.reflections[collection_name]) && refl.macro == :has_many
          # the association name matches (e.g. comment_for_...)

          if (anchor_refl = refl.klass.reflections[anchor_association_name]) && anchor_refl.macro == :belongs_to
            # the whole thing matches (e.g. comment_for_user)


            #self.class.class_eval %{
            #  def #{name}(anchor)
            #    result = if #{collection_name}.loaded?
            #               #{collection_name}.detect { |x| x.#{anchor_association_name}_is?(anchor) }
            #             else
            #               #{collection_name}.#{anchor_association_name}_is(anchor).first
            #             end
            #    result ||= #{collection_name}.new(:#{anchor_association_name} => anchor)
            #    result.origin = self
            #    result.origin_attribute = "#{name}.'#{anchor_id_expr}'"
            #    result
            #  end
            #}

            self.class.class_eval %{
              def #{name}
                Hobo::Model::FindFor::Finder.new(self, '#{name}', :#{collection_name}, :#{anchor_association_name})
              end
            }

            return send(name, *args)
          end
        end
      end

      method_missing_without_find_for(name, *args, &block)
    end

    class Finder

      def initialize(owner, name, collection, anchor_association)
        @association = owner.send(collection)
        @anchor_reflection = @association.member_class.reflections[anchor_association]
        @name = name
      end

      def origin
        @association.proxy_owner
      end

      def origin_attribute
        @name
      end

      def [](anchor_or_id)
        anchor = if anchor_or_id.is_a?(String)
                   id, klass = anchor_or_id.split(':')
                   (klass.constantize || @anchor_reflection.klass).find(id)
                 else
                   anchor_or_id
                 end
        result = if @association.loaded?
                   @association.detect { |x| x.send("#{@anchor_reflection.name}_is?", anchor) }
                else
                   @association.send("#{@anchor_reflection.name}_is", anchor).first
                end
        result ||= @association.new(@anchor_reflection.name => anchor)


        result.origin = self
        result.origin_attribute = if @anchor_reflection.options[:polymorphic]
                                    "#{anchor.id}:#{anchor.class.name}"
                                  else
                                    "#{anchor.id}"
                                  end
        result
      end

    end

  end
  end
end
