module DataMapper
  module Associations
    class RelationshipChain < Relationship
      OPTIONS = [
        :repository_name, :near_relationship_name, :remote_relationship_name,
        :child_model, :parent_model, :parent_key, :child_key,
        :min, :max
      ]

      undef_method :get_parent
      undef_method :attach_parent

      # @api private
      def child_model
        near_relationship.child_model
      end

      # @api private
      def get_children(parent, options = {}, finder = :all, *args)
        query = @query.merge(options).merge(child_key.to_query(parent_key.get(parent)))

        query[:links]  = links
        query[:unique] = true

        with_repository(parent) do
          results = grandchild_model.send(finder, *(args << query))
          # FIXME: remove the need for the uniq.freeze
          finder == :all ? (@mutable ? results : results.freeze) : results
        end
      end

      private

      # @api private
      def initialize(options)
        if (missing_options = OPTIONS - [ :min, :max ] - options.keys ).any?
          raise ArgumentError, "The options #{missing_options * ', '} are required", caller
        end

        @repository_name          = options.fetch(:repository_name)
        @near_relationship_name   = options.fetch(:near_relationship_name)
        @remote_relationship_name = options.fetch(:remote_relationship_name)
        @child_model              = options.fetch(:child_model)
        @parent_model             = options.fetch(:parent_model)
        @parent_properties        = options.fetch(:parent_key)
        @child_properties         = options.fetch(:child_key)
        @mutable                  = options.delete(:mutable) || false

        @name        = near_relationship.name
        @query       = options.reject{ |key,val| OPTIONS.include?(key) }
        @extra_links = []
        @options     = options
      end

      # @api private
      def near_relationship
        parent_model.relationships[@near_relationship_name]
      end

      # @api private
      def links
        if remote_relationship.kind_of?(RelationshipChain)
          remote_relationship.instance_eval { links } + [remote_relationship.instance_eval { near_relationship } ]
        else
          [ remote_relationship ]
        end
      end

      # @api private
      def remote_relationship
        near_relationship.child_model.relationships[@remote_relationship_name] ||
          near_relationship.child_model.relationships[@remote_relationship_name.to_s.singularize.to_sym]
      end

      # @api private
      def grandchild_model
        Class === @child_model ? @child_model : (Class === @parent_model ? @parent_model.find_const(@child_model) : Object.find_const(@child_model))
      end
    end # class Relationship
  end # module Associations
end # module DataMapper
