module JamRuby
  class MusicSession < ActiveRecord::Base

    self.primary_key = 'id'

    attr_accessor :legal_terms, :skip_genre_validation
    attr_accessible :creator, :description, :musician_access, :approval_required, :fan_chat, :fan_access, :genres

    belongs_to :creator, :inverse_of => :music_sessions, :class_name => "JamRuby::User", :foreign_key => "user_id"

    has_many :connections, :class_name => "JamRuby::Connection"
    has_many :users, :through => :connections, :class_name => "JamRuby::User"
    has_and_belongs_to_many :genres, :class_name => "::JamRuby::Genre", :join_table => "genres_music_sessions"
    has_many :join_requests, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::JoinRequest"
    has_many :invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::Invitation"
    has_many :invited_musicians, :through => :invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver

    has_many :fan_invitations, :foreign_key => "music_session_id", :inverse_of => :music_session, :class_name => "JamRuby::FanInvitation"
    has_many :invited_fans, :through => :fan_invitations, :class_name => "JamRuby::User", :foreign_key => "receiver_id", :source => :receiver
    has_one :recording, :class_name => "JamRuby::Recording", :inverse_of => :music_session

    belongs_to :band, :inverse_of => :music_sessions, :class_name => "JamRuby::Band", :foreign_key => "band_id"

    after_save :require_at_least_one_genre, :limit_max_genres

    after_destroy do |obj|
      JamRuby::MusicSessionHistory.removed_music_session(obj.id)
    end

    validates :description, :presence => true, :no_profanity => true
    validates :fan_chat, :inclusion => {:in => [true, false]}
    validates :fan_access, :inclusion => {:in => [true, false]}
    validates :approval_required, :inclusion => {:in => [true, false]}
    validates :musician_access, :inclusion => {:in => [true, false]}
    validates :legal_terms, :inclusion => {:in => [true]}, :on => :create
    validates :creator, :presence => true
    validate :creator_is_musician

    def creator_is_musician
      unless creator.musician?
        errors.add(:creator, "creator must be a musician")
      end
    end

    # This is a little confusing. You can specify *BOTH* friends_only and my_bands_only to be true
    # If so, then it's an OR condition. If both are false, you can get sessions with anyone.
    def self.index(current_user, participants = nil, genres = nil, friends_only = false, my_bands_only = false, keyword = nil)

      query = MusicSession
        .joins(
          %Q{
            INNER JOIN
              connections
            ON
              music_sessions.id = connections.music_session_id
          }
        )
        .joins(
          %Q{
            LEFT OUTER JOIN 
              friendships 
            ON 
              connections.user_id = friendships.user_id 
            AND 
              friendships.friend_id = '#{current_user.id}'
          }
        )
        .joins(
          %Q{
            LEFT OUTER JOIN
              invitations
            ON
              invitations.music_session_id = music_sessions.id
            AND
              invitations.receiver_id = '#{current_user.id}'
          }
        )
        .group(
          %Q{
            music_sessions.id
          }
        )
        .order(
          %Q{
            SUM(CASE WHEN invitations.id IS NULL THEN 0 ELSE 1 END) DESC, 
            SUM(CASE WHEN friendships.user_id IS NULL THEN 0 ELSE 1 END) DESC, 
            music_sessions.created_at DESC
          }
        )
        .where(
          %Q{
              musician_access = true
            OR
              invitations.id IS NOT NULL
          }
        )

      query = query.where("music_sessions.description like '%#{keyword}%'") unless keyword.nil?
      query = query.where("connections.user_id" => participants.split(',')) unless participants.nil?
      query = query.joins(:genres).where("genres.id" => genres.split(',')) unless genres.nil?

      if my_bands_only
        query = query.joins(
          %Q{
            LEFT OUTER JOIN
              bands_musicians
            ON
              bands_musicians.user_id = '#{current_user.id}'
          }
        )
      end

      if my_bands_only || friends_only
        query = query.where(
          %Q{
              #{friends_only ? "friendships.user_id IS NOT NULL" : "false"}
            OR
              #{my_bands_only ? "bands_musicians.band_id = music_sessions.band_id" : "false"}
          }
        )
      end

      return query
    end

    # Verifies that the specified user can join this music session
    def can_join? user, as_musician
      if as_musician
        if !user.musician
          return false # "a fan can not join a music session as a musician"
          raise PermissionError, "a fan can not join a music session as a musician"
        end
        
        if self.musician_access
          if self.approval_required
            return self.invited_musicians.exists?(user)
          else
            return true
          end

        else
          # the creator can always join, and the invited users can join
          return self.creator == user || self.invited_musicians.exists?(user)
        end
      else
        # it's a fan, and the only way a fan can join is if fan_access is true
        return self.fan_access
      end

    end

    # Verifies that the specified user can see this music session
    def can_see? user
      if self.musician_access
        return true
      else
        # the creator can always see, and the invited users can see it too
        return self.creator == user || self.invited_musicians.exists?(user)
      end
    end

    # Verifies that the specified user can delete this music session
    def can_delete? user
      # the creator can delete
      return self.creator == user
    end

    def access? user
      return self.users.exists? user
    end
    
    def to_s
      return description
    end

    private

    def require_at_least_one_genre
      unless skip_genre_validation
        if self.genres.count < Limits::MIN_GENRES_PER_RECORDING
          errors.add(:genres, ValidationMessages::GENRE_MINIMUM_NOT_MET)
        end
      end
    end

    def limit_max_genres
      unless skip_genre_validation
        if self.genres.count > Limits::MAX_GENRES_PER_RECORDING
          errors.add(:genres, ValidationMessages::GENRE_LIMIT_EXCEEDED)
        end
      end
    end
  end
end
