#{
# Creates JSON output from committee-info.txt with the following format:
#  "last_updated": "2016-03-04 04:50:00 UTC",
#  "committee_count": 210,
#  "nextBoardMeetings": {
#    "July": {
#      "epoch": 1752699600,
#      "display": "2025-07-16 21:00:00 UTC",
#      "iso8601": "2025-07-16T21:00:00Z"
#    },...
#  "roster_counts": {
#    "accumulo": 40,
#    ...
#  },
#  "committees": {
#    "abdera": {
#      "display_name": "Abdera",
#      "site": "http://abdera.apache.org/",
#      "description": "Atom Publishing Protocol Implementation",
#      "mail_list": "abdera",
#      "established": "11/2008",
#      "report": [
#        "Next month: missing in February",
#        "February",
#        "May",
#        "August",
#        "November"
#      ],
#      "nextMeetingEpoch": 1752699600,
#      "chair": {
#        "availid": {
#          "name": "Some One"
#        }
#      },
#      "roster": {
#        "availid": {
#          "name": "Some One",
#          "date": "2009-10-21"
#         },
#      ...
#      },
#      "pmc": true
#    },

# If a second parameter is provided, also creates committee-retired info of the form:
#{
#  "last_updated": "2016-03-04 04:50:00 UTC",
#  "retired_count": 123,
#  "retired": {
#    "abdera": {
#      "display_name": "Abdera",
#      "description": "Atom Publishing Protocol Implementation",
#      "retired": "yyyy-mm".
#      "mlists": ["dev", "user", ...] // lists not yet shut down
#    },

require_relative 'public_json_common'
require 'whimsy/asf/board'

# gather committee info
committees = ASF::Committee.load_committee_info

nextQuarter = ASF::Board.nextQuarter # {June: date1, July: date2, ...}

# reformat the data
info = {
  last_updated: ASF::Committee.svn_change,
  committee_count: committees.size,
  pmc_count: 0,
  nextBoardMeetings: nextQuarter.map {|k,v| [k,{epoch: v.to_i, display: v.to_s, iso8601: v.iso8601}]}.to_h,
  roster_counts: nil
}

roster_counts = {}
info[:committees] = committees.map {|committee|
  schedule = committee.schedule.to_s.split(/,\s*/)
  schedule.unshift committee.report if committee.report != committee.schedule
  nextMeeting = ASF::Board.nextReport(nextQuarter, schedule)
  cname = committee.name.gsub(/[^-\w]/, '')
  data = {
    display_name: committee.display_name,
    site: committee.site,
    description: committee.description,
    mail_list: committee.mail_list,
    established: committee.established,
    report: schedule,
    nextMeetingEpoch: nextMeeting.to_i,
    # Convert {:name=>"Public Name", :id=>"availid"} to
    # "chair": { "availid": { "name": "Public Name" } }
    chair: committee.chairs.map {|chair| [chair[:id], {:name => chair[:name]}]}.to_h,
    roster_count: committee.roster.size,
    roster: committee.roster.sort.to_h, # sort entries by uid
    pmc: committee.pmc?
  }
  info[:pmc_count] += 1 if committee.pmc?
  roster_counts[cname] = committee.roster.size
  data[:paragraph] = committee.paragraph if committee.paragraph
  [cname, data]
}.to_h
info[:roster_counts] = roster_counts

info[:officers] =
  ASF::Committee.officers.map { |officer|
    [officer.name.gsub(/[^-\w]/, ''),
      {
        display_name: officer.display_name,
        paragraph: officer.paragraph, # will always be present
        roster: officer.chairs.map {|e| [e[:id], {:name => e[:name]}]}.to_h
      }
    ]
  }.to_h

info[:board] = {
  roster: ASF::Board.directors(true)
}

public_json_output(info)

# Check if there is an unexpected entry date
# we only do this if the file has changed to avoid excessive reports
if check_now?
  # Note: symbolize_names=false to avoid symbolising variable keys such as pmc and user names
  # However the current JSON (info) uses symbols for fixed keys - beware!
  previous = JSON.parse(@old_file, :symbolize_names => false)
  previous = previous['committees']
  last_updated = info[:last_updated] # This is a Time instance
  # the joining date should normally be the same as the date when the file was updated:
  updated_day1 = last_updated.strftime('%Y-%m-%d') # day of update
  # and the date must be after the last time the data was checked.
  # Unfortunately the last_updated field is only updated when the content changes -
  # there is currently no record of when the last check was done.
  # For now, just assume that this is done every 15 mins. This may cause spurious reports
  # if the checks are ever suspended for longer and meanwhile changes occur.
  # Note: for those in an earlier timezone the date could be a few hours earlier
  updated_day2 = (last_updated - 3600 * 4).strftime('%Y-%m-%d') # day of previous update

  # for validating UIDs
  uids = ASF::Person.list().map(&:id)

  info[:committees].each { |pmc, entry|
    next if pmc == 'infrastructure' # no dates
    Wunderbar.warn "#{pmc}: no description found" if entry[:pmc] && !entry[:description]
    previouspmc = previous[pmc] # get the original details (if any)
    if previouspmc # we have an existing entry
      entry[:roster].each { |name, value|
        newdate = value[:date]
        if newdate.nil?
          Wunderbar.warn "Un-dated member for #{pmc}: #{name} #{value[:name]} #{newdate}"
          next
        end
        if previouspmc['roster'][name].nil? # new name, check the date is OK
          if newdate <= updated_day1 and newdate >= updated_day2 # in range
            Wunderbar.info "New member for #{pmc}: #{name} #{value[:name]} #{newdate}"
          elsif newdate > updated_day1
            Wunderbar.warn "Future-dated member for #{pmc}: #{name} #{value[:name]} #{newdate}"
          else
            Wunderbar.warn "Past-dated member for #{pmc}: #{name} #{value[:name]} #{newdate}"
          end
        else
          olddate = previouspmc['roster'][name]['date']
          if olddate != newdate
            Wunderbar.warn "Changed date member for #{pmc}: #{name} #{value[:name]} #{olddate} => #{newdate}"
          end
        end
      }
    else
      Wunderbar.info "New PMC detected: #{pmc}"
      # Could check that the joining dates are all the same?
    end
    entry[:roster].each { |id, _|
      Wunderbar.warn "#{pmc}: unknown uid '#{id}'" unless uids.include?(id)
    }
  }

  previous.each { |pmc, _|
    unless info[:committees][pmc]
      Wunderbar.info "Deleted PMC detected: #{pmc}"
    end
  }

end

# do we awant attic info?
if ARGV.length >= 2
  ARGV.shift # we have already used this
  require 'whimsy/asf/mlist'

  metadata = ASF::Committee.load_committee_metadata[:tlps]

  public_lists = Hash.new {|h, k| h[k] = Array.new}
  begin
    ASF::MLIST.list_types(true) do |dom, list, _|
      public_lists[dom.sub(/\.apache\.org$/, '')] << list
    end
  rescue StandardError => e
    Wunderbar.error e.inspect
  end

  # reformat the data
  attic = {last_updated: ASF::Committee.meta_change}

  data = {}

  active = info[:committees].keys

  metadata.each do |key, value|
    retired = value[:retired]
    if retired
      Wunderbar.warn "#{key} has no display name" unless value[:name]
      data[key] = {
        display_name: value[:name],
        retired: retired,
        retired_date: value[:retired_date],
      }
      data[key][:description] = value[:description] if value[:description]
      mlists = public_lists[key]
      if mlists.size > 0
        data[key][:mlists] = mlists
      end
    else
      unless active.include? key
        Wunderbar.warn "Checking committee-info.yml: has '#{key}'' retired? Could not find it in committee-info.txt!"
      end
    end
  end

  attic[:retired_count] = data.size
  attic[:retired] = data

  public_json_output(attic) # uses first ARGV entry
end
