#
# edit exiting / post new report
#
# Note: this code validates that env.user is one of the following:
#  1) an ASF member
#  2) a PMC chair
#  3) a member of the PMC for the report being posted
#

# special case for new special orders
if @attach == '7?'
  @message = "Post Special Order 7X: #{@title}"
elsif @attach == '8?'
  @message = "Post Discussion Item 8X: #{@title}"
end

attach = nil

# Determine if user is authorized
user = ASF::Person.find(env.user)
member_or_officer = (user.asf_chair_or_member?)
real_web_server = env.password
alternate_credentials = (real_web_server and not member_or_officer) ?
  [['--username', 'whimsysvn']] : nil

# prepend user id to message if whimsysvn role account is used
@message = "#{env.user}: #{@message}" if env.user and alternate_credentials

Agenda.update(@agenda, @message, auth: alternate_credentials) do |agenda|
  # quick parse of agenda
  parsed = ASF::Board::Agenda.parse(agenda, true)

  # map @project to @attach to support posting from reporter.apache.org
  if not @attach and @project
    project = ASF::Committee.find(@project)
    raise "project #{@project.inspect} not found" unless project
    unless member_or_officer or project.owners.include? user
      raise "not authorized to post to #{@project}"
    end

    projectName = project.display_name
    parsed.each do |report|
      if report['title'] == projectName
        raise 'report already posted' unless @digest or report['missing']
        attach = @attach = report[:attach]
        @digest ||= report['digest']
      end
    end
  elsif not ENV['RACK_ENV'] == 'test'
    raise 'not authorized to post to the board agenda' unless member_or_officer
  end

  # remove trailing whitespace
  @report.sub! /\s*\Z/, ''

  # convert unicode blank characters to an ASCII space
  @report.gsub!(/[[:blank:]]/, ' ')

  if @attach == '7?'
    # new special order

    # adjust indentation
    indent = @report.scan(/^ +/).min
    @report.gsub!(/^#{indent}/, '') if indent
    @report.gsub!(/^(\S)/, '       \1')

    # add order letter to title
    order = 'A'
    parsed.map {|section| section[:attach]}.
      select {|attach| attach =~ /^7\w/}.length.times {order.succ!}
    title = "    #{order}. #{@title}\n\n"

    # update the commit message that will be used
    @message.sub! '7X', "7#{order}"

    # insert into agenda
    agenda[/\n() 8\. Discussion Items/, 1] = "#{title}#{@report}\n\n"

  elsif @attach == '8?'
    # new discussion item

    # adjust indentation
    indent = @report.scan(/^ +/).min
    @report.gsub!(/^#{indent}/, '') if indent
    @report.gsub!(/^(\S)/, '       \1')

    # add item letter to title
    discussion = agenda[/ 8\. Discussion Items.*\n 9\./m]
    items = discussion.scan(/^    ([A-Z]+)\./).flatten
    item = items.empty? ? 'A' : items.max.succ
    title = "    #{item}. #{@title}\n\n"

    # update the commit message that will be used
    @message.sub! '8X', "8#{item}"

    # insert into agenda
    agenda[/\n() 9\. .*Action Items/, 1] = "#{title}#{@report}\n\n"

  elsif @attach&.start_with? '+'
    pmc_reports = parsed.select {|section| section[:attach] =~ /^[A-Z]/}
    attach = pmc_reports.last[:attach].succ
    pmc = ASF::Committee.find(@attach[1..-1])
    unless pmc.dn
      raise Exception.new("#{@attach[1..-1].inspect} PMC not found")
    end

    # select shepherd
    shepherds = pmc_reports.map {|section| section['shepherd']}.
      select {|shepherd| not shepherd.include? ' '}.
      group_by {|n| n}.map {|n, list| [n, list.length]}
    min = shepherds.map {|name, count| count}.min
    shepherd = shepherds.select {|name, count| count == min}.sample.first

    # insert section into committee-reports
    agenda[/\n() 7\. Special Orders/, 1] =
      "    #{attach}. Apache #{pmc.display_name} Project " +
      "[#{pmc.chair.public_name} / #{shepherd}]\n\n" +
      "       See Attachment #{attach}\n\n" +
      "       [ #{pmc.display_name}.\n" +
      "         approved:\n" +
      "         comments:\n" +
      "         ]\n\n"

    # insert report text as an attachment
    agenda[/^()-+\nEnd of agenda/, 1] =
      "-----------------------------------------\n" +
      "Attachment #{attach}: Report from the Apache #{pmc.display_name} " +
      "Project  [#{pmc.chair.public_name}]\n\n" +
      "#{@report.strip}\n\n"

  else
    item = parsed.find {|item| item[:attach]==@attach}

    if not item
      raise Exception.new("Attachment #{@attach.inspect} not found")
    elsif @digest != item['digest']
      raise Exception.new('Merge conflict')
    end

    spacing = "\n\n"

    if @attach =~ /^4\w/
      pattern = /(\n\n    #{@attach[-1]}\. #{item['title']} \[.*?\]).*?\n\n(    [B-Z]\.| 5\.)/m
      @report.gsub! /^(.)/, '       \1'
    elsif @attach =~ /^[78]\w/
      title = item['fulltitle'] || item['title']
      pattern = /(^\s+#{@attach[-1]}\.\s+#{title})\n.*?\n( {1,6}\w\.)/m
      @report.gsub! /^(.)/, '       \1'
    elsif @attach == '8.'
      title = 'Discussion Items'
      pattern = /^(\s8\. #{title})\n.*\n( 9\.)/m
      @report.gsub! /^(.)/, '    \1'
    else
      pattern = /(---\nAttachment #{@attach}:.*?\[.*?\])\n.*?\n(-{40})/m
      spacing = "\n\n\n"
    end

    spacing = '' if @report.empty?

    # President report has a custom footer - retain it
    if item['title'] == 'President' and agenda[pattern]
      footer = agenda[pattern][/\n\n(\s+Additionally.*?)\s+\w\.\Z/m, 1]
      @report += "\n\n#{footer}" if footer
    end

    if not agenda.sub!(pattern) { "#{$1}\n\n#{@report}#{spacing}#{$2}" }
      raise Exception.new('report merge failed')
    end
  end

  # return updated agenda
  agenda
end

# filter agenda if project is specified or the user is not authorized to see
# the entire document
if @project or alternate_credentials
  agenda = _.delete 'agenda'
 _item agenda.find {|report| report[:attach] == attach}
end
