###
# MentionSystem module
#
# This module defines common behavior in mention system
###
module MentionSystem
  ###
  # MentionProcessor class
  #
  # This class defines mention processor behavior in mention system
  ###
  class MentionProcessor
    ###
    # Constructor of the MentionProcessor class
    ###
    def initialize
      @callbacks = { after: [], before: [] }
    end

    ###
    # Adds an after callback
    #
    # @param [Proc] callback - the callback to add
    ###
    def add_after_callback(callback)
      @callbacks[:after].push(callback)
    end

    ###
    # Adds a before callback
    #
    # @param [Proc] callback - the callback to add
    ###
    def add_before_callback(callback)
      @callbacks[:before].push(callback)
    end

    ###
    # Extract handles from mentioner
    #
    # @param [Mentioner] mentioner - the {Mentioner} to extract handles from
    # @return [Array]
    ###
    def extract_handles_from_mentioner(mentioner)
      content = extract_mentioner_content(mentioner)
      handles = content.scan(handle_regexp).map { |handle| handle.gsub("#{mention_prefix}","") }
    end

    ###
    # Extracts the mentioner content
    #
    # @param [Mentioner] mentioner - the {Mentioner} to extract content from
    # @return [String]
    ###
    def extract_mentioner_content(mentioner)
      raise "Must be implemented in subclass"
    end

    ###
    # Finds mentionees by handles
    #
    # @param [Array] handles - the array of {Mentionee} handles to find
    # @return [Array]
    ###
    def find_mentionees_by_handles(*handles)
      raise "Must be implemented in subclass"
    end

    ###
    # Process mentions
    #
    # @param [Mentioner] mentioner - the {Mentioner} to process mentions from
    ###
    def process_mentions(mentioner)
      handles = extract_handles_from_mentioner(mentioner)
      mentionees = find_mentionees_by_handles(handles)

      mentionees.each do |mentionee|
        if process_before_callbacks(mentioner, mentionee)
          if mentioner.mention(mentionee)
            process_after_callbacks(mentioner, mentionee)
          end
        end
      end
    end

    private
      ###
      # Retrieves the handle regexp
      #
      # @return [Regexp]
      ###
      def handle_regexp
        /(?<!\w)#{mention_prefix}\w+/
      end

      ###
      # Returns the mention prefix
      #
      # @return [String]
      ###
      def mention_prefix
        "@"
      end

      ###
      # Process after callbacks
      #
      # @param [Mentioner] mentioner - the mentioner of the callback
      # @param [Mentionee] mentionee - the mentionee of the callback
      ###
      def process_after_callbacks(mentioner, mentionee)
        result = true
        @callbacks[:after].each do |callback|
          unless callback.call(mentioner, mentionee)
            result = false
            break
          end
        end
        result
      end

      ###
      # Process before callbacks
      #
      # @param [Mentioner] mentioner - the mentioner of the callback
      # @param [Mentionee] mentionee - the mentionee of the callback
      ###
      def process_before_callbacks(mentioner, mentionee)
        result = true
        @callbacks[:before].each do |callback|
          unless callback.call(mentioner, mentionee)
            result = false
            break
          end
        end
        result
      end
  end
end