module PassiveRecord
  module ClassMethods
    include PassiveRecord::Core
    include PassiveRecord::Associations
    include PassiveRecord::Hooks

    include Enumerable
    extend Forwardable

    # from http://stackoverflow.com/a/2393750/90042
    def descendants
      ObjectSpace.each_object(Class).select { |klass| klass < self }
    end

    def all
      instances_by_id.values
    end
    def_delegators :all, :each, :last

    def find(id_or_ids)
      if id_or_ids.is_a?(Array)
        find_by_ids(id_or_ids)
      else
        find_by_id(id_or_ids)
      end
    end

    def find_by(conditions)
      if conditions.is_a?(Hash)
        where(conditions).first
      else # assume we have an identifier/identifiers
        find(conditions)
      end
    end

    def find_all_by(conditions)
      where(conditions).all
    end

    def where(conditions={})
      Query.new(self, conditions)
    end

    def create(attrs={})
      instance = new

      instance.singleton_class.class_eval { attr_accessor :id }

      instance_id = attrs.delete(:id) { SecureRandom.uuid }
      instance.send(:id=, instance_id)

      register(instance)

      before_create_hooks.each do |hook|
        hook.run(instance)
      end

      attrs.each do |(k,v)|
        instance.send("#{k}=", v)
      end

      after_create_hooks.each do |hook|
        hook.run(instance)
      end

      instance
    end

    def destroy(id)
      @instances.reject! {|k,_| id == k }
    end

    def destroy_all
      @instances = {}
    end

    protected
    def find_by_id(id_to_find)
      instances_by_id[id_to_find]
    end

    def find_by_ids(ids)
      instances_by_id.values_at(*ids)
    end

    private
    def instances_by_id
      @instances ||= {}
    end

    def register(model)
      id = model.id
      instances_by_id[id] = model
      self
    end
  end
end
