# Schema as of Fri Jun 29 21:58:21 EDT 2007 (schema version 45)
#
#  id                  :integer(11)   not null
#  name                :string(255)   
#  type                :string(255)   
#  email               :string(100)   
#  crypted_password    :string(40)    
#  salt                :string(40)    
#  created_at          :datetime      
#  bio                 :text          
#  picture             :string(255)   
#  user_number         :string(255)   
#  year_of_birth       :integer(11)   
#  gender              :integer(11)   default(0)
#  postal_code         :string(255)   
#  login_key           :string(255)   
#  verified            :boolean(1)    
#  login_key_expires_at:datetime      
#  avg_rating          :float         
#  suspended           :boolean(1)    
#  receive_messages_fro:boolean(1)    default(true)
#

# This is an abstract class that gets inherited by Guest and RegisteredUser
class User < ActiveRecord::Base
  has_many :ratings, :foreign_key => :participant_id, :class_name => 'AbstractRating', :dependent => :delete_all
  has_many :comment_ratings, :foreign_key => :participant_id, :class_name => 'CommentRating', :dependent => :delete_all
  
  cattr_accessor :current_user    
  
  # This method is just a stand-in for a real role system.
  #
  # Since this project has simple permission requirements this approach
  # may suffice.
  def role
    self.class.to_s.downcase.to_sym
  end
  
  # change in descendants to include all prermitted activities
  def permitted_activities
    []
  end
  
  def has_permission_to?(do_something)
    permitted = permitted_activities.include? do_something
    logger.debug "checking permission for #{name} (#{role}) to #{do_something} -- " + (permitted ? "premitted" : "forbidden")
    return permitted
  end
  
  # This method is defined here, so that it can be called on any user without having to first check
  # if we record reads for that class
  def record_read_of(what)
    readings.create(:readable => what) if self.respond_to?(:readings)
  end    

  def registered?
    false
  end

  def guest?
    false
  end
  
  # takes the same arguments as AR::find, but is scoped to the current user
  def topics_find(*args)
    self.with_topic_scope do
      Topic.find *args
    end
  end 
  
  def with_topic_scope
    Topic.with_scope(:find => TopicPolicy::topic_scope_for_user(self) ) do
      yield
    end
  end
  
  # Comments the user has made, grouped by Topic.
  #
  # This method does account for private topics(via access passes)
  #
  # Returns an array of [topic, [comments]] pairs.
  def commented_topics_by(user)
    topics = topics_find(:all, :conditions => ['id IN (?)', user.comments.collect(&:topic_id).uniq], :order => 'start_date DESC') # filters out private topics the current user lacks access to
    grouped_comments = user.comments.group_by(&:topic_id)
    topics.collect { |topic| [topic, grouped_comments[topic.id]] }
  end
  
  class << self
    include RuportQueryOverArConnection
    
    def reading_users_report(topic_id)
      report_raw("SELECT DISTINCT users.email, users.created_at, users.id as `user_id`, users.type FROM users INNER JOIN readings ON users.id = readings.participant_id WHERE ((readings.readable_type = 'Topic') AND (readings.readable_id = #{topic_id}))")
    end
    
    def reading_participants_report(topic_id)
      report_raw "SELECT DISTINCT users.email, users.created_at, users.id as `user_id`, users.type FROM users INNER JOIN readings ON users.id = readings.participant_id WHERE ((readings.readable_type = 'Topic') AND (readings.readable_id = #{topic_id}) AND ((users.type = 'Participant')))"
    end
    
    # User metrics include:
    # - Best Rated   :ratings  # how well rated (avg)
    # - Worst Rated  :ratings
    # - Most Rated   :rated    # how many times rated
    # - Most Read    :readings # how many times comments read
    # 
    # Since there is no facility for rating/reading users, these measurements are aggregates of 
    # a users commenting history.
    # 
    # We are measuring how others have responded to messages grouped by author.
    def list_for(listable, options={})
      case listable
      when :readings
        query_helper = QueryHelper.new(options, :base => ['readable_type = "Comment"'], :topic => 'comments.topic_id', :age => 'readings.created_at')
        # scopes to the age of the Reading, not of the User
        # 
        # When sorting in reverse, Users with no comment reading are ignored because
        # without any reading, there is nothing to count, or scope
        counts = Reading.count(:group => 'comments.created_by',           # returns an array of id, count pairs
          :joins => 'INNER JOIN comments ON comments.id = readings.readable_id', # needed for topic condition
          :conditions => query_helper.conditions,
          :limit => 5, 
          :order => "count_all #{query_helper.sort_order}"
        )
        counts.collect{|id, count| [User.find(id), count]}
      
      when :rated # how many times rated
        query_helper = QueryHelper.new(options, :topic => 'comments.topic_id', :age => 'ratings.created_on')
        counts = CommentRating.count(:group => 'comments.created_by', 
          :joins => 'INNER JOIN comments ON comments.id = ratings.subject_id', # needed for topic condition
          :conditions => query_helper.conditions,
          :limit => 5,
          :order => "count_all #{query_helper.sort_order}")
        counts.collect{|id, count| [User.find(id), count]}
      
      when :ratings # how well rated (avg)
        query_helper = QueryHelper.new(options, :base => ['ratings.value != 77'], :topic => 'comments.topic_id', :age => 'ratings.created_on')
        
        counts = CommentRating.average(:value, :group => "comments.created_by", 
          :joins => 'INNER JOIN comments ON comments.id = ratings.subject_id', # needed for topic condition
          :conditions => query_helper.conditions,
          :limit => 5,
          :order => "avg_value #{query_helper.sort_order}")
        counts.collect{|id, count| [User.find(id), count]}
      
      # when :commented_on # most direct replies
      #   qh = QueryHelper.new(options, :base => ['root_id != parent_id AND parent_id != 0'], :topic => 'comments.topic_id', :age => 'comments.created_at')
      #   counts = self.count(:group => :parent_id, 
      #      :conditions => qh.conditions,
      #      :limit => 5, :order => "count_all #{qh.sort_order}"
      #      )
      #   counts.collect{|id, count| [Comment.find(id), count]}
      #   
      # when :word_count 
      #   qh = QueryHelper.new(options, :topic => 'comments.topic_id', :age => 'comments.created_at')
      #   records = self.find(:all,
      #      :conditions => qh.conditions,
      #      :limit => 5, :order => "word_count #{qh.sort_order}"
      #      )
      #   records.collect{|rec| [rec, rec.word_count]}
      else
        raise ArgumentError, "unrecognized list '#{listable}' for #{self}"
      end # case
      
    end
  end
end
