#!/usr/bin/env ruby
PAGETITLE = "ASF Mailing List Subscription Helper" # Wvisible:mail subscribe
$LOAD_PATH.unshift '/srv/whimsy/lib'
require 'wunderbar'
require 'wunderbar/bootstrap'
require 'whimsy/asf'
require 'whimsy/asf/mlist'

FORMAT_NUMBER = 3 # json format number

user = ASF::Person.new($USER)
# authz handled by httpd

# get the possible names of the current, graduated and retired podlings
current = []
retired = []
graduated = [] # if no longer a PMC then this is the same as retired
ASF::Podling.list.each {|p|
  names = p['resourceAliases'] # array, may be empty
  names.push p['resource'] # single string, always present
  status = p['status']
  if status == 'current'
    current.concat(names)
  elsif status == 'retired'
    retired.concat(names)
  elsif status == 'graduated'
    graduated.concat(names)
  end
}

pmcs = ASF::Committee.pmcs.map(&:mail_list)

# hack to get access to pmcs etc
define_method(:gettype) do |dom|
  return :ASF if dom == 'apache.org'
  host = dom.sub('.apache.org', '') # get the host name
  return :TLP if pmcs.include? host
  return :PPMC if current.include? host
  return :TLP if dom.end_with? '.apache.org'
  return nil if retired.include? host or graduated.include? host
  return :ASF
end

ldap_pmcs = [] # No need to get the info for ASF members
ldap_pmcs = user.committees.map(&:mail_list) unless user.asf_member?
# Also allow podling private lists to be subscribed by podling owners
ldap_pmcs += user.podlings.map(&:mail_list) unless user.asf_member?
addrs = user.all_mail

listtype = {} # key: list@dom, value: list type: ASF|TLP|PPMC

lists = ASF::Mail.cansub(user.asf_member?, ASF.pmc_chairs.include?(user), ldap_pmcs, false)
  .map { |dom, lname, _list|
    listid = lname + '@' + dom
    type = gettype(dom)
    listtype[listid] = type if type
    [dom, lname]
  }.sort.map {|d, l| "#{l}@#{d}"}


_html do
  # better system output styling (errors in red)
  _style :system
  _script src: '/assets/bootstrap-select.js'
  _link rel: 'stylesheet', href: '/assets/bootstrap-select.css'
  _body? do
    _whimsy_body(
      title: PAGETITLE,
      subtitle: 'Mailing List Subscriptions',
      related: {
        'https://www.apache.org/foundation/mailinglists.html' => 'Apache Mailing List Info Page (How-to Subscribe Manually)',
        'https://lists.apache.org' => 'Apache Mailing List Archives',
        '/committers/moderationhelper.cgi' => 'Mailing List Moderation Helper',
        '/roster/committer/__self__' => 'Your Committer Details (and subscriptions)'
      },
      helpblock: -> {
        _p 'The below form allows Apache committers to automatically subscribe to, or unsubscribe from, most ASF mailing lists.'
        _p do
          _span.text_info 'Note:'
          _ 'Only email address(es) associated with your Apache ID are listed here.  To'
          _span.strong 'change your associated email addresses'
          _ ', login to '
          _a 'your Whimsy personal details page', href: "https://whimsy.apache.org/roster/committer/__self__"
          _ 'where you can change your Forwarding Address(es) and alternate email addresses you may use.'
        end
        _p 'ASF members can use this form to subscribe to most private lists. PMC chairs can subscribe to board lists. (P)PMC members can subscribe to their private@ list.'
        _p 'The subscription request will be queued and should be processed within about an hour.'
        _p do
          _ 'To subscribe to other private lists, send an email to the list-subscribe@ address and wait for the request to be manually approved.'
          _ 'This might take a day or two.'
        end
        _p do
          _ 'To view all your existing subscriptions (and email addresses), see your'
          _a 'committer details', href: '/roster/committer/__self__'
          _ '.'
        end
      }
    ) do

      _form method: 'post' do
        _input type: 'hidden', name: 'request', value: 'sub'
        _fieldset do
          _legend 'Subscribe To A List'

          _label 'Select a mailing list first, then select the email address to subscribe to that list.'
          _ '(The dropdown only shows lists to which you can automatically subscribe)'
          _br
          _label 'List name:'
          _select name: 'list', data_live_search: 'true' do
            _optgroup label: 'Foundation lists' do
              lists.find_all { |list| listtype[list] == :ASF }.each do |list|
                _option list
              end
            end

            _optgroup label: 'Top-Level Projects' do
              lists.find_all { |list| listtype[list] == :TLP }.each do |list|
                _option list
              end
            end

            _optgroup label: 'Podlings' do
              lists.find_all { |list| listtype[list] == :PPMC }.each do |list|
                _option list
              end
            end
          end

          _label 'Email:'
          _select name: 'addr' do
            addrs.each do |addr|
              _option addr
            end
          end

          _input type: 'submit', value: 'Submit Request'
        end
      end
      _p do
        _br
        _b 'WARNING'
        _ 'Some providers are known to block our emails as SPAM.'
        _br
        _ 'Please see the following for details: '
        _a 'email provider issues', href: 'emailissues', target: '_blank'
        _ ' (opens in new page)'
      end
      _p
      _hr
      _p

      _form method: 'post' do
        _input type: 'hidden', name: 'request', value: 'unsub'
        _fieldset do
          _legend 'Unsubscribe From A List'

          _label 'Select the mailing list first, then select the email address to unsubscribe.'
          _ '(The dropdown only shows lists to which you are subscribed)'
          _br
          # collect subscriptions
          subscriptions = ASF::MLIST.subscriptions(user.all_mail)[:subscriptions].
            select { |listid, _| ASF::Mail.unsubbable? listid}. # can we unsubscribe from this list?
            group_by {|listid, _mail| listid}. # allow for multiple subs to single list
            transform_values {|v| v.map(&:last)} # pick out the emails

          # fill in any missing types
          subscriptions.each_key do |list|
            listtype[list] ||= gettype(list.split('@')[-1]) || :ASF
          end

          _label 'List name:'
          _select.ulist! name: 'list', data_live_search: 'true' do
            _optgroup label: 'Foundation lists' do
              subscriptions.select { |list, _| listtype[list] == :ASF }.each do |list, emails|
                _option list, data_emails: emails.join(' ')
              end
            end

            _optgroup label: 'Top-Level Projects' do
              subscriptions.select { |list, _| listtype[list] == :TLP }.each do |list, emails|
                _option list, data_emails: emails.join(' ')
              end
            end

            _optgroup label: 'Podlings' do
              subscriptions.select { |list, _| listtype[list] == :PPMC }.each do |list, emails|
                _option list, data_emails: emails.join(' ')
              end
            end
          end

          _label 'Email:'
          _select.uaddr! name: 'addr' do
            addrs.each do |addr|
              _option addr
            end
          end

          _input type: 'submit', value: 'Submit Request'
        end
      end

      _p

      if _.post?
        _hr

        unless addrs.include? @addr and lists.include? @list
          _h2_.text_danger {_span.label.label_danger 'Invalid Input'}
          _p 'Both email and list to subscribe to are required!'
          break
        end

        # subreq/unsubreq now accept name@dom
        # Keep the key for the file name
        listkey = ASF::Mail.listdom2listkey(@list)

        # Each user can only subscribe once to each list in each timeslot
        fn = "#{$USER}-#{listkey}.json"

        vars = {
          version: FORMAT_NUMBER,
          availid: $USER,
          addr: @addr,
          listkey: @list,
          # f3
          member_p: user.asf_member?,
          chair_p: ASF.pmc_chairs.include?(user),
        }
        request = JSON.pretty_generate(vars) + "\n"

        _div.well do
          if @request == 'unsub'
            _p 'Submitting unsubscribe request:'
          else
            _p 'Submitting subscribe request:'
          end

          _pre request
        end

        SUBREQ = ASF::SVN.svnpath!('subreq')
        SUBREQ.sub! '/subreq', '/unsubreq' if @request == 'unsub'

        rc = 999

        Dir.mktmpdir do |tmpdir|

          # commit using user's credentials if possible, otherwise use whisysvn
          if not $PASSWORD # should not happen
            raise ArgumentError.new "Missing credentials"
          elsif user.asf_member?
            credentials = {user: $USER, password: $PASSWORD}
          else # committer has insufficient karma, use the proxy
            credentials = {user: 'whimsysvn'}
          end

          ASF::SVN.svn_('checkout', [SUBREQ, tmpdir], _, credentials)

          Dir.chdir tmpdir do

            if File.exist? fn
              File.write(fn, request + "\n")
              ASF::SVN.svn('status', '.')
            else
              File.write(fn, request + "\n")
              ASF::SVN.svn('add', fn)
            end

            if @request == 'unsub'
              message = "#{@list} -= #{$USER}"
            else
              message = "#{@list} += #{$USER}"
            end

            options = credentials.merge({msg: message})
            rc = ASF::SVN.svn_('commit', fn, _, options)
          end
        end

        if rc == 0
          _div.alert.alert_success role: 'alert' do
            _p do
              _span.strong 'Request successfully submitted.'
              if @request == 'unsub'
                _ 'You will be unsubscribed within the hour.'
              else
                _ 'You will be subscribed within the hour.'
              end
            end
          end
        else
          _div.alert.alert_danger role: 'alert' do
            _p do
              _span.strong 'Request Failed, see above for details'
            end
          end
        end
      end
    end

    _script %{
      $('select').selectpicker({});

      function select_emails() {
        var emails = $('#ulist option:selected').attr('data-emails').split(' ');
        var oldval = $('#addr').val();
        var newval = null;
        $('#uaddr option').each(function() {
          if (emails.indexOf($(this).text()) == -1) {
            this.disabled = true;
            if (this.textContent == oldval) oldval = null;
          } else {
            this.disabled = false;
            newval = newval || this.textContent;
          };
        });

        if (newval && !oldval) {
          $('#uaddr').val(newval);
          $('button[data-id=uaddr] .filter-option').text(newval);
        }

        $('#uaddr').selectpicker('render');
      }

      select_emails();

      $('#ulist').change(function() {
        select_emails();
      });
    }
  end
end
