# frozen_string_literal: true

require 'set'

module HTML
  class Pipeline
    # HTML filter that replaces @org/team mentions with links. Mentions within
    # <pre>, <code>, <a>, <style>, and <script> elements are ignored.
    #
    # Context options:
    #   :base_url - Used to construct links to team profile pages for each
    #               mention.
    #   :team_pattern - Used to provide a custom regular expression to
    #                       identify team names
    #
    class TeamMentionFilter < Filter
      # Public: Find @org/team mentions in text.  See
      # TeamMentionFilter#team_mention_link_filter.
      #
      #   TeamMentionFilter.mentioned_teams_in(text) do |match, org, team|
      #     "<a href=...>#{team}</a>"
      #   end
      #
      # text - String text to search.
      #
      # Yields the String match, org name, and team name.  The yield's
      # return replaces the match in the original text.
      #
      # Returns a String replaced with the return of the block.
      def self.mentioned_teams_in(text, team_pattern = TeamPattern)
        text.gsub team_pattern do |match|
          org = $1
          team = $2
          yield match, org, team
        end
      end

      # Default pattern used to extract team names from text. The value can be
      # overridden by providing the team_pattern variable in the context. To
      # properly link the mention, should be in the format of /@(1)\/(2)/.
      TeamPattern = /
        (?<=^|\W)                  # beginning of string or non-word char
        @([a-z0-9][a-z0-9-]*)      # @organization
          \/                       # dividing slash
          ([a-z0-9][a-z0-9\-_]*)   # team
          \b
      /ix

      # Don't look for mentions in text nodes that are children of these elements
      IGNORE_PARENTS = %w[pre code a style script].to_set

      def call
        result[:mentioned_teams] ||= []

        doc.search('.//text()').each do |node|
          content = node.to_html
          next unless content.include?('@')
          next if has_ancestor?(node, IGNORE_PARENTS)
          html = mention_link_filter(content, base_url, team_pattern)
          next if html == content
          node.replace(html)
        end
        doc
      end

      def team_pattern
        context[:team_pattern] || TeamPattern
      end

      # Replace @org/team mentions in text with links to the mentioned team's
      # page.
      #
      # text      - String text to replace @mention team names in.
      # base_url  - The base URL used to construct team page URLs.
      # team_pattern  - Regular expression used to identify teams in text
      #
      # Returns a string with @team mentions replaced with links. All links have a
      # 'team-mention' class name attached for styling.
      def mention_link_filter(text, _base_url = '/', team_pattern = TeamPattern)
        self.class.mentioned_teams_in(text, team_pattern) do |match, org, team|
          link = link_to_mentioned_team(org, team)

          link ? match.sub("@#{org}/#{team}", link) : match
        end
      end

      def link_to_mentioned_team(org, team)
        result[:mentioned_teams] |= [team]

        url = base_url.dup
        url << '/' unless url =~ /[\/~]\z/
        
        "<a href='#{url << org}/#{team}' class='team-mention'>" \
          "@#{org}/#{team}" \
          '</a>'
      end
    end
  end
end