module JamRuby
  class RsvpRequest < ActiveRecord::Base

    belongs_to :user, :class_name => "JamRuby::User"
    belongs_to :music_session, :class_name => "JamRuby::MusicSession"
    has_many :rsvp_requests_rsvp_slots, :class_name => "JamRuby::RsvpRequestRsvpSlot", :foreign_key => "rsvp_request_id"
    has_many :rsvp_slots, :class_name => "JamRuby::RsvpSlot", :through => :rsvp_requests_rsvp_slots

    validates :user, presence: true
    validates :canceled, :inclusion => {:in => [nil, true, false]}
    validate :creator_rsvp_cancel
    before_save :cancel_calendar

    # pulls all instruments from the associated rsvp_slots
    def instrument_list
      rsvp_slots.map(&:instrument)
    end

    def self.index(music_session, user=nil, options={})
      query = RsvpRequest
                .includes(:user)
                .joins(
                  %Q{
                    INNER JOIN
                      rsvp_requests_rsvp_slots rrrs
                    ON
                      rrrs.rsvp_request_id = rsvp_requests.id
                    INNER JOIN
                      rsvp_slots rs
                    ON rs.id = rrrs.rsvp_slot_id
                  }
                )
                .where(
                  %Q{
                    rs.music_session_id = '#{music_session.id}'
                  }
                )
                .order('rsvp_requests.created_at DESC')

      if options[:status] == 'approved'
        query = query.where("rrrs.chosen = true AND canceled != TRUE")
      elsif options[:status] == 'pending'
        query = query.where("rrrs.chosen is null AND canceled != TRUE")
      end

      query = query.where("rsvp_requests.user_id = ?", user.id) unless user.nil?
      return query.uniq
    end

    def self.create(params, user)
      music_session = MusicSession.find_by_id(params[:session_id])

      # verify music session exists
      if music_session.nil?
        raise StateError, "Invalid session."
      end

      # verify slot IDs exist in request
      if params[:rsvp_slots].blank?
        raise StateError, "You must select at least 1 slot."
      end

      # verify invitation exists for this user and session
      invitation = Invitation.where("music_session_id = ? AND receiver_id = ?", music_session.id, user.id)

      if invitation.first.nil? && !music_session.open_rsvps && music_session.creator.id != user.id
        raise JamPermissionError, "Only a session invitee can create an RSVP for this session."
      end

      RsvpRequest.transaction do
        @rsvp = RsvpRequest.new
        @rsvp.user = user
        @rsvp.music_session = music_session
        @rsvp.chosen = true if params[:autoapprove] == true
        
        slot_ids = params[:rsvp_slots]

        instruments = []

        # for each slot requested, do the following:
        # (1) verify slot exists in db
        # (2) verify slot is not already chosen
        # (3) verify user has not already requested this slot
        # (4) create RsvpRequestRsvpSlot
        # (5) create RsvpRequest
        # special case: if the slot is the value 'unstructured',
        # we will also on-demand create a RsvpSlot.
        slot_ids.each do |id|
          if id == "unstructured"
            rsvp_slot = RsvpSlot.new
            rsvp_slot.is_unstructured_rsvp = true
            rsvp_slot.music_session = music_session
            unless rsvp_slot.save
              raise StateError, 'Unable to auto-create RSVP Slot'
            end
          else
            rsvp_slot = RsvpSlot.where(:id => id).first
          end

          # verify slot exists in db
          if rsvp_slot.nil?
            raise StateError, "Invalid slot #{id}."
          end

          # verify user has not already submitted RSVP request for this slot
          rsvp = RsvpRequest.joins(:rsvp_requests_rsvp_slots)
            .where(:user_id => user.id)
            .where(:canceled => false)
            .where(rsvp_requests_rsvp_slots: {rsvp_slot_id: id})

          if !rsvp.blank?
            raise StateError, "You have already submitted an RSVP request for this slot."
          end

          chosen_slot = rsvp_slot.rsvp_requests_rsvp_slots.where("chosen = true").first

          # verify this slot was not already chosen
          if !chosen_slot.nil?
            raise StateError, "The #{rsvp_slot.instrument_id} slot has already been approved by the session organizer."
          else
            rsvp_request_rsvp_slot = RsvpRequestRsvpSlot.new
            rsvp_request_rsvp_slot.rsvp_request = @rsvp
            rsvp_request_rsvp_slot.rsvp_slot = rsvp_slot
            rsvp_request_rsvp_slot.chosen = true if params[:autoapprove] == true
            rsvp_request_rsvp_slot.save

            instruments << rsvp_slot.instrument_id
          end
        end

        @rsvp.save

        unless params[:message].blank?
          session_info_comment = SessionInfoComment.new
          session_info_comment.music_session = music_session
          session_info_comment.user = user
          session_info_comment.comment = params[:message]
          session_info_comment.save
          Notification.send_scheduled_session_comment(music_session, user, params[:message])
        end

        Notification.send_scheduled_session_rsvp(music_session, user, instruments)

        @rsvp
      end
    end

    def self.update(params, user)

      rsvp_request_id = params[:id]

      music_session = MusicSession.find_by_id(params[:session_id])

      # verify music session exists
      if music_session.nil?
        raise StateError, "Invalid session."
      end

      # authorize the user attempting to respond to the RSVP request
      if music_session.creator.id != user.id
        raise JamPermissionError, "Only the session organizer can accept or decline an RSVP request."
      end

      rsvp_request = RsvpRequest.find_by_id(rsvp_request_id)

      if rsvp_request.nil?
        raise StateError, "Invalid RSVP request."
      end

      RsvpRequest.transaction do

        rsvp_request.chosen = true
        rsvp_responses = params[:rsvp_responses]
        if !rsvp_responses.blank?
          instruments = []
          accepted_slot = false

          rsvp_responses.each do |r|
            request_slot_id = r[:request_slot_id]
            request_slot = RsvpRequestRsvpSlot.find_by_id(request_slot_id)
            if request_slot.nil?
              raise StateError, "Invalid request slot #{request_slot_id}."
            end

            rsvp_slot = RsvpSlot.find_by_id(request_slot.rsvp_slot_id)
            if rsvp_slot.nil?
              raise StateError, "Slot does not exist"
            end

            # slot has already been accepted
            if rsvp_slot.chosen && r[:accept]
              # get the instrument and skill level for this slot and see if there are others available
              # and auto-reassign the request_slot to the open rsvp_slot
              open_slots = music_session.open_slots

              # don't worry about matching proficiency if the user RSVPed to "Any Skill Level"
              if rsvp_slot.proficiency_level == 0
                open_slots = open_slots.select { |slot| slot.instrument_id == rsvp_slot.instrument_id }
              else
                open_slots = open_slots.select { |slot| slot.instrument_id == rsvp_slot.instrument_id && slot.proficiency_level == rsvp_slot.proficiency_level }
              end

              # (1) another available slot exists matching this instrument and proficiency
              unless open_slots.blank?
                rsvp_slot = open_slots.first
                request_slot.rsvp_slot_id = rsvp_slot.id  # this links the RsvpRequestRsvpSlot to the available open slot matching this instrument and proficiency level

              # (2) no identical slots available => create a new one on the fly
              else
                new_slot = RsvpSlot.new
                new_slot.instrument_id = rsvp_slot.instrument_id
                new_slot.proficiency_level = rsvp_slot.proficiency_level
                new_slot.music_session_id = rsvp_slot.music_session_id
                new_slot.is_unstructured_rsvp = rsvp_slot.is_unstructured_rsvp
                new_slot.save!

                request_slot.rsvp_slot_id = new_slot.id  # saved below on line 218
              end
            end

            if r[:accept]
              accepted_slot = true
              request_slot.chosen = true
              request_slot.save

              instruments << rsvp_slot.instrument_id

            else
              request_slot.chosen = false
              request_slot.save
            end
          end

          # send notification if at least 1 slot was approved
          if accepted_slot
            Notification.send_scheduled_session_rsvp_approved(music_session, rsvp_request.user, instruments)
          end

        else
          raise StateError, "Invalid request."
        end

        rsvp_request.save
      end
    end

    def self.cancel(params, user)
      if params[:id].blank?
        raise StateError, "RSVP request ID is required."
      end

      if params[:session_id].blank?
        raise StateError, "Session ID is required."
      end

      music_session = MusicSession.find(params[:session_id])
      rsvp_request = RsvpRequest.find(params[:id])

      if music_session.creator.id != user.id && rsvp_request.user_id != user.id
        raise JamPermissionError, "Only the session organizer or RSVP creator can cancel the RSVP."
      end

      RsvpRequest.transaction do

        case params[:cancelled]
        when 'yes'
          rsvp_request.canceled = true
          rsvp_request.cancel_all = false

        when 'all'
          rsvp_request.canceled = true
          rsvp_request.cancel_all = true
        end

        rsvp_request.save

        if rsvp_request.errors.any?
          return rsvp_request
        end

        # mark corresponding slot's chosen field as false
        rsvp_request_slots = RsvpRequestRsvpSlot.where("rsvp_request_id = ?", rsvp_request.id)

        rsvp_request_slots.each do |slot|
          if slot.chosen
            slot.chosen = false
            slot.save
          end
        end

        # send notification
        if music_session.creator.id == user.id
          Notification.send_scheduled_session_rsvp_cancelled_org(music_session, rsvp_request.user)
        else
          Notification.send_scheduled_session_rsvp_cancelled(music_session, user)
        end

        unless params[:message].blank?
          session_info_comment = SessionInfoComment.new
          session_info_comment.music_session = music_session
          session_info_comment.user = user
          session_info_comment.comment = params[:message]
          session_info_comment.save
          Notification.send_scheduled_session_comment(music_session, user, params[:message], true)
        end
      end
    end

    private

    def creator_rsvp_cancel
      if canceled && !canceled_was && user == self.rsvp_slots[0].music_session.creator
        errors.add(:canceled, "can't be canceled by the session organizer")
      end
    end

    def cancel_calendar
      calendar_manager = CalendarManager.new
      if self.canceled
        self.rsvp_slots.each do |slot|
          calendar_manager.cancel_ics_event(slot.music_session, user)
        end
      end
    end
  end

end
