#
# Fetch and maintain a local cache of reporter.apache.org "forgotten" drafts.
# Select projects with drafts for the current agenda and sort by attachment.
#
# Note: reporter will return partial results for non-members.  Once full
# results are retrieved, serve from the cache for the next minute.
#

class Reporter
  @@partial = true
  @@agenda = nil
  @@digest = nil

  def self.drafts(env, update=nil)
    changed = false

    agenda_file = File.basename(Dir["#{FOUNDATION_BOARD}/board_agenda_*.txt"].max)

    if ENV['RACK_ENV'] == 'test'
      return {agenda: agenda_file, drafts: []}
    end

    cache = File.join(ASF::Config.get(:cache), 'reporter-drafts.json')

    # read and prune previous status.  May be used to "fill in the blanks"
    # should a partial set of reports be received
    report_status = (JSON.parse(File.read(cache)) rescue {}).
      select {|project, status| status['agenda'] == agenda_file}.to_h

    # file updates as they are received
    if update

      report_status.merge! update
      File.write cache, report_status.to_json
      changed = true

    # if cache is older than a minute, fetch new data
    elsif not File.exist? cache or File.mtime(cache) < Time.now-60 or @@partial
      # source of truth
      uri = URI.parse('https://reporter.apache.org/api/drafts/forgotten')

      # fetch and merge in latest report statuses
      Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
        request = Net::HTTP::Get.new(uri)
        request.basic_auth env.user, env.password
        response = http.request(request)
        if response.code == '200'
          report_status.merge! JSON.parse(response.body)['report_status']
        else
          Wunderbar.error "Failed to fetch #{uri}: #{response.code}"
        end

        if not File.exist? cache or JSON.parse(File.read cache) != report_status
          changed = true
        end

        File.write cache, report_status.to_json

        @@partial = (not ASF::Person.find(env.user).asf_member?)
      end
    end

    # extract projects with drafts for this agenda
    lastMeeting = ASF::Board.lastMeeting.to_i
    drafts = report_status.select do |project, status|
      next false unless status['agenda'] == agenda_file
      last_draft = status['last_draft']
      next false if last_draft and status['draft_timestamp'] <= lastMeeting
      last_draft and not last_draft.empty?
    end

    # return agenda and drafts indexed by attachment
    results = {
      agenda: agenda_file,
      drafts: drafts.map {|project, status|
        committee = ASF::Committee.find(project)
        [status['attach'], {
          project: project,
          title: (committee ? committee.display_name : project),
          timestamp: status['draft_timestamp'],
          author: status['last_author'],
          text: status['last_draft']
        }]
      }.to_h
    }

    if changed
      digest = Digest::MD5.hexdigest(JSON.dump(results[:drafts]))
      Events.post type: 'reporter', agenda: agenda_file, digest: digest
    end

    # filter drafts based on user visibility
    user = env.respond_to?(:user) && ASF::Person.find(env.user)
    unless !user or user.asf_chair_or_member?
      projects = user.committees.map(&:name)
      results[:drafts].keep_if do |attach, draft|
        projects.include? draft[:project]
      end
    end

    @@digest = Digest::MD5.hexdigest(JSON.dump(results[:drafts]))
    @@agenda = agenda_file
    results[:digest] = @@digest

    results
  end

  # return digest information
  def self.digest
    {agenda: @@agenda, digest: @@digest}
  end
end
