module JamRuby
  class Notification < ActiveRecord::Base

    self.primary_key = 'id'

    default_scope order('created_at DESC')

    belongs_to :target_user, :class_name => "JamRuby::User", :foreign_key => "target_user_id"
    belongs_to :source_user, :class_name => "JamRuby::User", :foreign_key => "source_user_id"
    belongs_to :band, :class_name => "JamRuby::Band", :foreign_key => "band_id"
    belongs_to :session, :class_name => "JamRuby::MusicSession", :foreign_key => "session_id"
    belongs_to :recording, :class_name => "JamRuby::Recording", :foreign_key => "recording_id"

    def index(user_id)
      results = Notification.where(:target_user_id => user_id).limit(50)
      return results
    end

    def photo_url
      unless self.source_user.nil?
        self.source_user.photo_url
      end
    end

    # used for persisted notifications
    def formatted_msg
      target_user, source_user, band, session, recording, invitation, join_request = nil

      unless self.target_user_id.nil?
        target_user = User.find(self.target_user_id)
      end

      unless self.source_user_id.nil?
        source_user = User.find(self.source_user_id)
      end

      unless self.band_id.nil?
        band = Band.find(self.band_id)
      end

      unless self.session_id.nil?
        session = MusicSession.find(self.session_id)
      end

      unless self.recording_id.nil?
        recording = Recording.find(self.recording_id)
      end

      unless self.invitation_id.nil?
        invitation = Invitation.find(self.invitation_id)
      end

      unless self.join_request_id.nil?
        join_request = JoinRequest.find(self.join_request_id)
      end

      return self.class.format_msg(self.description, source_user)
    end

    # TODO: MAKE ALL METHODS BELOW ASYNC SO THE CLIENT DOESN'T BLOCK ON NOTIFICATION LOGIC
    # TODO: ADD TESTS FOR THIS CLASS

    class << self

      @@mq_router = MQRouter.new
      @@message_factory = MessageFactory.new

      def delete_all(session_id)
        Notification.delete_all "(session_id = '#{session_id}')"
      end

      ################### HELPERS ###################
      def retrieve_friends(connection, user_id)
        friend_ids = []
        connection.exec("SELECT f.friend_id as friend_id FROM friendships f WHERE f.user_id = $1", [user_id]) do |friend_results|
          friend_results.each do |friend_result|
            friend_ids.push(friend_result['friend_id'])
          end
        end
        return friend_ids
      end

      def retrieve_followers(connection, user_id)
        follower_ids = []
        connection.exec("SELECT uf.follower_id as friend_id FROM users_followers uf WHERE uf.user_id = $1", [user_id]) do |follower_results|
          follower_results.each do |follower_result|
            follower_ids.push(follower_result['follower_id'])
          end
        end
        return follower_ids
      end

      def retrieve_friends_and_followers(connection, user_id)
        ids = retrieve_friends(connection, user_id)
        ids.concat(retrieve_followers(connection, user_id))
        ids.uniq! {|id| id}
        return ids
      end

      def retrieve_friends_and_followers_not_in_session(connection, user_id, session_id)
        ids = retrieve_friends_and_followers(connection, user_id)
        connection.exec("SELECT c.user_id as musician_id FROM connections c WHERE c.music_session_id = $1", [session_id]) do |musicians|
          musicians.each do |musician_result|
            # remove users who are in the session
            ids.reject! {|item| item == musician_result['musician_id']}
          end
        end
        return ids
      end

      def format_msg(description, user)
        case description
          when NotificationTypes::FRIEND_UPDATE
            return "#{user.name} is now "

          when NotificationTypes::FRIEND_REQUEST
            return "#{user.name} has sent you a friend request."

          when NotificationTypes::FRIEND_REQUEST_ACCEPTED
            return "#{user.name} has accepted your friend request."

          when NotificationTypes::FRIEND_SESSION_JOIN
            return "#{user.name} has joined the session."

          when NotificationTypes::MUSICIAN_SESSION_JOIN
            return "#{user.name} has joined the session."

          when NotificationTypes::MUSICIAN_SESSION_DEPART
            return "#{user.name} has left the session."

          # when "social_media_friend_joined"
          # when "join_request_approved"
          # when "join_request_rejected"
          # when "session_invitation"
          # when "band_invitation"
          # when "band_invitation_accepted"
          # when "recording_available"
          else
            return ""
        end
      end

      ################### FRIEND UPDATE ###################
      def send_friend_update(user_id, online, connection)

        # (1) get all of this user's friends
        friend_ids = retrieve_friends(connection, user_id)

        if friend_ids.length > 0
          user = User.find(user_id)

          # (2) create notification
          online_msg = online ? "online." : "offline."
          notification_msg = format_msg(NotificationTypes::FRIEND_UPDATE, user) + online_msg
          msg = @@message_factory.friend_update(user_id, user.name, user.photo_url, online, notification_msg)

          # (3) send notification
          @@mq_router.publish_to_friends(friend_ids, msg, user_id)
        end
      end

      ################### FRIEND REQUEST ###################
      def send_friend_request(friend_request_id, user_id, friend_id)
        user = User.find(user_id)

        # (1) save to database
        notification = Notification.new
        notification.description = NotificationTypes::FRIEND_REQUEST
        notification.source_user_id = user_id
        notification.target_user_id = friend_id
        notification.friend_request_id = friend_request_id
        notification.save

        # (2) create notification
        notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST, user)
        msg = @@message_factory.friend_request(friend_request_id, user_id, user.name, user.photo_url, friend_id, notification_msg, notification.id, notification.created_at.to_s)

        # (3) send notification
        @@mq_router.publish_to_user(friend_id, msg)
      end

      ############### FRIEND REQUEST ACCEPTED ###############
      def send_friend_request_accepted(user_id, friend_id)
        friend = User.find(friend_id)

        # (1) save to database
        notification = Notification.new
        notification.description = NotificationTypes::FRIEND_REQUEST_ACCEPTED
        notification.source_user_id = friend_id
        notification.target_user_id = user_id
        notification.save

        # (2) create notification
        notification_msg = format_msg(NotificationTypes::FRIEND_REQUEST_ACCEPTED, friend)
        msg = @@message_factory.friend_request_accepted(friend_id, friend.name, friend.photo_url, user_id, notification_msg, notification.id, notification.created_at.to_s)

        # (3) send notification
        @@mq_router.publish_to_user(user_id, msg)
      end

      ################## SESSION INVITATION ##################
      def send_session_invitation(receiver_id, invitation_id)

        # (1) save to database
        notification = Notification.new
        notification.description = NotificationTypes::SESSION_INVITATION
        notification.target_user_id = receiver_id
        notification.save

        # (2) create notification
        msg = @@message_factory.session_invitation(receiver_id, invitation_id)
        
        # (3) send notification
        @@mq_router.publish_to_user(receiver_id, msg)
      end

      def send_musician_session_join(music_session, connection, user)

        # (1) create notification
        msg = @@message_factory.musician_session_join(music_session.id, user.id, user.name, user.photo_url)

        # (2) send notification
        @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => connection.client_id})
      end

      def send_musician_session_depart(music_session, client_id, user)

        # (1) create notification
        msg = @@message_factory.musician_session_depart(music_session.id, user.id, user.name, user.photo_url)

        # (2) send notification
        @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
      end

      def send_musician_session_fresh(music_session, client_id, user)

        # (1) create notification
        msg = @@message_factory.musician_session_fresh(music_session.id, user.id, user.name, user.photo_url)

        # (2) send notification
        @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
      end

      def send_musician_session_stale(music_session, client_id, user)

        # (1) create notification
        msg = @@message_factory.musician_session_stale(music_session.id, user.id, user.name, user.photo_url)

        # (2) send notification
        @@mq_router.server_publish_to_session(music_session, msg, sender = {:client_id => client_id})
      end

      def send_friend_session_join(db_conn, connection, user)
        ids = retrieve_friends_and_followers_not_in_session(db_conn, user.id, connection.music_session.id)

        if ids.length > 0  
          # (1) save to database

          # (2) create notification
          msg = @@message_factory.friend_session_join(connection.music_session.id, user.id, user.name, user.photo_url)

          # (3) send notification
          @@mq_router.publish_to_friends(ids, msg, sender = {:client_id => connection.client_id})
        end
      end

      def send_join_request(music_session, join_request, sender, text)

        # (1) save to database

        # (2) create notification
        msg = @@message_factory.join_request(music_session.id, join_request.id, sender.name, text)

        # (3) send notification
        @@mq_router.server_publish_to_session(music_session, msg)
      end

    end
  end
end