class Map
  include DataMapper::Resource

  property :id,     Serial
  property :key_repository, String, :default => 'keys'
  
  has n, :roles
  has n, :users
  
  belongs_to :parent, :model => Map, :required => false
  
  after :create do
    self.users += self.parent.users if self.parent
  end
  
  after :key_repository= do |*args|
    unless File.exists?(self.expanded_key_repository_path)
      raise LoadError, "Could not load key repository - #{self.expanded_key_repository_path}"
    end
  end
    
  def self.draw(opts = {})
    map = Map.create(opts)
    yield map if block_given?
    map.read!

    map
  end
    
  def read!
    self.roles.all.each do |role|
      role.update_user_access!
    end
    true
  end
  
  def key_repository(repo = nil)
    if repo
      self.key_repository = repo
    end
    
    @key_repository
  end
  
  def expanded_key_repository_path
    File.join(Fabric.options(:map_root), self.key_repository)
    # self.key_repository
  end
  
  def role(name, *hosts)
    role = self.roles.create(:name => name.to_s)
    hosts.each do |host|
      role.servers.create(:host => host)
    end

    role.save
  end
    
  def user(*names)
    names.each do |name|
      user = self.users.create(:name => name.to_s)
      self.load_keys_from_repository(user)
    end
  end
  
  # This method has become too long again...
  def grant(user_names, role_names, role_conditions = {})
    users = self.users.all
    roles = self.roles.all
    
    users = users.all(:name => user_names) unless user_names == :all
    roles = roles.all(:name => role_names) unless role_names == :all

    if role_conditions.include?(:except)
      roles = roles.all(:name.not => role_conditions[:except])
    end
    
    raise DataMapper::ObjectNotFoundError if users.empty? or roles.empty?
    
    roles.each do |role|
      role.users += users
    end
  end
  
  def namespace(&block)
    self.class.draw(:parent => self, &block)
  end
  
  protected
  def load_keys_from_repository(user)
    path = File.join(self.expanded_key_repository_path, "#{user.name}.pub")
    narrate "Looking for key for #{user.name} in #{path}"
    
    if File.exists?(path)
      user.keys.create(:public_key => File.read(path))
    end
  end
end