<commit_before># frozen_string_literal: true

require "how_is/version"
require "how_is/sources/github"
require "how_is/sources/github_helpers"
require "date"

module HowIs::Sources
  class Github
    class Issues
      include HowIs::Sources::GithubHelpers

      def initialize(repository, start_date, end_date)
        @repository = repository
        @user, @repo = repository.split("/", 2)
        @start_date = start_date
        @end_date = end_date
      end

      def url(values = {})
        defaults = {
          "is" => singular_type,
          "created" => "#{@start_date}..#{@end_date}",
        }
        values = defaults.merge(values)
        raw_query = values.map { |k, v|
          [k, v].join(":")
        }.join(" ")

        query = CGI.escape(raw_query)

        "https://github.com/#{@repository}/#{url_suffix}?q=#{query}"
      end

      def average_age
        average_age_for(data)
      end

      def oldest
        oldest_for(data) || {}
      end

      def newest
        newest_for(data) || {}
      end

      def summary
        number_open = to_a.length
        pretty_number =
          pluralize(pretty_type, number_open, zero_is_no: true)

        "There #{are_or_is(number_open)} <a href=\"#{url}\">#{pretty_number} open</a>."
      end

      def to_html
        summary_ = "<p>#{summary}</p>"

        return summary_ if to_a.empty?

        template_data = {
          summary: summary_,
          average_age: average_age,
          type: type,
          pretty_type: pretty_type,

          oldest_link: oldest["url"],
          oldest_date: pretty_date(oldest["createdAt"]),

          newest_link: newest["url"],
          newest_date: pretty_date(newest["createdAt"]),
        }

        Kernel.format(HowIs.template("issues_or_pulls_partial.html_template"), template_data)
      end

      # TODO: Clean up Issues Per Label stuff, or replace it with different functionality.

      def issues_per_label
        ipl = with_label_links(num_with_label(data), @repository)
        number_with_no_label = num_with_no_label(data)

        if number_with_no_label > 0
          ipl["(No label)"] = {
            "name" => "(No label)",
            "total" => number_with_no_label,
          }
        end

        ipl
      end

      HTML_GRAPH_ROW = <<-EOF
  <tr>
    <td style="width: %{label_width}">%{label_text}</td>
    <td><span class="fill" style="width: %{percentage}%%">%{link_text}</span></td>
  </tr>
      EOF

      def issues_per_label_html
        ipl = issues_per_label

        return "<p>There are no open issues to graph.</p>" if ipl.empty?

        biggest = ipl.map { |_label, info| info["total"] }.max
        get_percentage = ->(number_of_issues) { number_of_issues * 100 / biggest }

        longest_label_length = ipl.map(&:first).map(&:length).max
        label_width = "#{longest_label_length}ch"

        parts = ipl.map { |label, info|
          # TODO: Remove this hack to get around unlabeled issues not having a link.
          label_text = label
          label_url  = label_url_for(info["name"])
          label_text = '<a href="' + label_url + '">' + label_text + '</a>'

          Kernel.format(HTML_GRAPH_ROW, {
            label_width: label_width,
            label_text: label_text,
            label_link: info["url"],
            percentage: get_percentage.call(info["total"]),
            link_text: info["total"].to_s,
          })
        }

        "<table class=\"horizontal-bar-graph\">\n" +
        parts.join("\n") +
        "\n</table>"
      end

      def to_a
        obj_to_array_of_hashes(data)
      end

      private

      def url_suffix
        "issues"
      end

      def singular_type
        "issue"
      end

      def type
        singular_type + "s"
      end

      def pretty_type
        "issue"
      end

      def data
        return @data if instance_variable_defined?(:@data)

        @data = []
        last_cursor = fetch_last_cursor
        return @data if last_cursor.nil?

        after, data = fetch_issues(after, data, last_cursor)

        @data = data.select(&method(:issue_is_relevant?))
      end

      def issue_is_relevant?(issue)
        if !issue["closedAt"].nil? && date_le(issue["closedAt"], @start_date)
          false
        else
          date_ge(issue["createdAt"], @start_date) && date_le(issue["createdAt"], @end_date)
        end
      end

      def graphql(query_string)
        query = Okay::GraphQL.query(query_string)
        headers = {bearer_token: HowIs::Sources::Github::ACCESS_TOKEN}
        query.submit!(:github, headers).or_raise!.from_json
      end

      def fetch_last_cursor
        raw_data = graphql <<~QUERY
          repository(owner: #{@user.inspect}, name: #{@repo.inspect}) {
            #{type}(last: 1, orderBy:{field: CREATED_AT, direction: ASC}) {
              edges {
                cursor
              }
            }
          }
        QUERY

        edges = raw_data.dig("data", "repository", type, "edges")
        if edges.nil? || edges.empty?
          nil
        else
          edges.last["cursor"]
        end
      end

      def fetch_issues(after, data, last_cursor)
        data ||= []
        chunk_size = 100
        after_str = ", after: #{after.inspect}" unless after.nil?

        raw_data = graphql <<~QUERY
          repository(owner: #{@user.inspect}, name: #{@repo.inspect}) {
            #{type}(first: #{chunk_size}#{after_str}, orderBy:{field: CREATED_AT, direction: ASC}) {
              edges {
                cursor<commit_msg>i think this should work but i hit the ratelimit.<commit_after># frozen_string_literal: true

require "how_is/version"
require "how_is/sources/github"
require "how_is/sources/github_helpers"
require "date"

module HowIs::Sources
  class Github
    class Issues
      include HowIs::Sources::GithubHelpers

      def initialize(repository, start_date, end_date)
        @repository = repository
        @user, @repo = repository.split("/", 2)
        @start_date = start_date
        @end_date = end_date
      end

      def url(values = {})
        defaults = {
          "is" => singular_type,
          "created" => "#{@start_date}..#{@end_date}",
        }
        values = defaults.merge(values)
        raw_query = values.map { |k, v|
          [k, v].join(":")
        }.join(" ")

        query = CGI.escape(raw_query)

        "https://github.com/#{@repository}/#{url_suffix}?q=#{query}"
      end

      def average_age
        average_age_for(data)
      end

      def oldest
        oldest_for(data) || {}
      end

      def newest
        newest_for(data) || {}
      end

      def summary
        number_open = to_a.length
        pretty_number =
          pluralize(pretty_type, number_open, zero_is_no: true)

        "There #{are_or_is(number_open)} <a href=\"#{url}\">#{pretty_number} open</a>."
      end

      def to_html
        summary_ = "<p>#{summary}</p>"

        return summary_ if to_a.empty?

        template_data = {
          summary: summary_,
          average_age: average_age,
          type: type,
          pretty_type: pretty_type,

          oldest_link: oldest["url"],
          oldest_date: pretty_date(oldest["createdAt"]),

          newest_link: newest["url"],
          newest_date: pretty_date(newest["createdAt"]),
        }

        Kernel.format(HowIs.template("issues_or_pulls_partial.html_template"), template_data)
      end

      # TODO: Clean up Issues Per Label stuff, or replace it with different functionality.

      def issues_per_label
        ipl = with_label_links(num_with_label(data), @repository)
        number_with_no_label = num_with_no_label(data)

        if number_with_no_label > 0
          ipl["(No label)"] = {
            "name" => "(No label)",
            "total" => number_with_no_label,
          }
        end

        ipl
      end

      HTML_GRAPH_ROW = <<-EOF
  <tr>
    <td style="width: %{label_width}">%{label_text}</td>
    <td><span class="fill" style="width: %{percentage}%%">%{link_text}</span></td>
  </tr>
      EOF

      def issues_per_label_html
        ipl = issues_per_label

        return "<p>There are no open issues to graph.</p>" if ipl.empty?

        biggest = ipl.map { |_label, info| info["total"] }.max
        get_percentage = ->(number_of_issues) { number_of_issues * 100 / biggest }

        longest_label_length = ipl.map(&:first).map(&:length).max
        label_width = "#{longest_label_length}ch"

        parts = ipl.map { |label, info|
          # TODO: Remove this hack to get around unlabeled issues not having a link.
          label_text = label
          label_url  = label_url_for(info["name"])
          label_text = '<a href="' + label_url + '">' + label_text + '</a>'

          Kernel.format(HTML_GRAPH_ROW, {
            label_width: label_width,
            label_text: label_text,
            label_link: info["url"],
            percentage: get_percentage.call(info["total"]),
            link_text: info["total"].to_s,
          })
        }

        "<table class=\"horizontal-bar-graph\">\n" +
        parts.join("\n") +
        "\n</table>"
      end

      def to_a
        obj_to_array_of_hashes(data)
      end

      private

      def url_suffix
        "issues"
      end

      def singular_type
        "issue"
      end

      def type
        singular_type + "s"
      end

      def pretty_type
        "issue"
      end

      def data
        return @data if instance_variable_defined?(:@data)

        @data = []
        return @data if last_cursor.nil?

        loop do
          after, data = fetch_issues(after, data)
          p after
          p last_cursor
          puts
          break if after == last_cursor
        end
        puts
        puts

        @data = data.select(&method(:issue_is_relevant?))
      end

      def issue_is_relevant?(issue)
        if !issue["closedAt"].nil? && date_le(issue["closedAt"], @start_date)
          false
        else
          date_ge(issue["createdAt"], @start_date) && date_le(issue["createdAt"], @end_date)
        end
      end

      def graphql(query_string)
        query = Okay::GraphQL.query(query_string)
        headers = {bearer_token: <PASSWORD>::Github::ACCESS_TOKEN}
        query.submit!(:github, headers).or_raise!.from_json
      end

      def last_cursor
        return @last_cursor if instance_variable_defined?(:@last_cursor)

        raw_data = graphql <<~QUERY
          repository(owner: #{@user.inspect}, name: #{@repo.inspect}) {
            #{type}(last: 1, orderBy:{field: CREATED_AT, direction: ASC}) {
              edges {
                cursor
              }
            }
          }
        QUERY

        edges = raw_data.dig("data", "repository", type, "edges")
        @last_cursor =
          if edges.nil? || edges.empty?
            nil
          else
            edges.last["cursor"]
          end
      end

      def fetch_issues(after, data)
        data ||= []
        chunk_size = 100
        after_str = ", after: #{after.inspect}" unless after.nil?

        raw_data = graphql <<~QUERY
          repository(owner: #{@user.inspect}, name: #{@repo.inspect}) {
            #{type}(first: #{chunk_size}#{after_str}, orderBy:{field: CREATED_AT, direction: ASC}) {
              edges {
                cursor
{'fraction_non_alphanumeric': 0.12349610757254069, 'fraction_numerical': 0.0015923566878980893, 'mean_word_length': 2.0114544485881725, 'pattern_counts': {'":': 2, '<': 52, '<?xml version=': 0, '>': 52, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2473557', 'n_tokens_mistral': 3557, 'n_tokens_neox': 3442, 'n_words': 725}