# GithubTag generates the following API links:
# getting an issue
#   https://api.github.com/repos/facebook/react/issues/9218
# getting a pull request
#   https://api.github.com/repos/facebook/react/pulls/14105
# getting a comment belonging to an issue
#   https://api.github.com/repos/facebook/react/issues/comments/287635042
class GithubTag
  class GithubIssueTag
    PARTIAL = "liquids/github_issue".freeze
    API_BASE_ENDPOINT = "https://api.github.com/repos/".freeze
    ISSUE_REGEXP = %r{https://github\.com/[\w\-.]{1,39}/[\w\-.]{1,39}/(issues|pull)/\d+((#issuecomment-|#discussion_|#pullrequestreview-)\w+)?}

    def initialize(link)
      @orig_link = link
      @link = parse_link(link)
      @content = GithubIssue.find_or_fetch(@link)
      @content_json = @content.issue_serialized
      @is_issue = @content_json[:title].present?
      @created_at = @content_json[:created_at]
      @body = @content.processed_html.html_safe # rubocop:disable Rails/OutputSafety
    end

    def render
      ApplicationController.render(
        partial: PARTIAL,
        locals: {
          body: @body,
          created_at: @created_at.rfc3339,
          date: I18n.l(@created_at.utc, format: :github),
          html_url: @content_json[:html_url],
          issue_number: issue_number,
          tagline: tagline,
          title: title,
          user_avatar_url: @content_json[:user][:avatar_url],
          user_html_url: @content_json[:user][:html_url],
          username: @content_json[:user][:login]
        },
      )
    end

    private

    attr_reader :content_json

    def parse_link(link)
      match = link.match(ISSUE_REGEXP)
      raise StandardError, I18n.t("liquid_tags.github_tag.github_issue_tag.invalid_github_issue_pull") unless match

      generate_api_link(link)
    end

    def generate_api_link(input)
      uri = Addressable::URI.parse(input).normalize
      uri.host = nil if uri.host == "github.com"

      # public PRs URLs are "/pull/{id}" but the API requires "/pulls/{id}"
      uri.path = uri.path.gsub(%r{/pull/}, "/pulls/")

      # public comments URLs are "/issues/{id}#issuecomment-{comment_id}"
      # or "/pull/{id}#issuecomment-{comment_id}"
      # but the API requires "/issues/comments/{comment_id}"
      if uri.fragment&.start_with?("issuecomment-")
        uri.path = uri.path.gsub(%r{(issues|pulls)/\d+}, "issues/comments/")
        comment_id = uri.fragment.split("-").last
        uri.join!(comment_id)
      end

      # fragments and query params are not needed in the API call
      uri.fragment = nil
      uri.query = nil

      # remove leading forward slash in the path
      path = uri.path.delete_prefix("/")

      Addressable::URI.parse(API_BASE_ENDPOINT).join(path).to_s
    end

    def title
      content_json[:title] || I18n.t("liquid_tags.github_tag.github_issue_tag.comment_for")
    end

    def issue_number
      @issue_number ||= content_json[:number] || content_json[:issue_url].split("/").last
    end

    def tagline
      @is_issue ? I18n.t("liquid_tags.github_tag.github_issue_tag.posted_on") : I18n.t("liquid_tags.github_tag.github_issue_tag.commented_on") # rubocop:disable Layout/LineLength
    end
  end
end
