#! /usr/bin/python3

import argparse
from email.message import EmailMessage
from datetime import datetime, timedelta
import smtplib
import sys

import caida_oidc_client
from requests_oauthlib import OAuth2Session

FROM = 'monitor-info@caida.org'
SUBJECT = 'recently connected ark monitors'
MAILSERVER = 'rommie.caida.org'
API_URL = "https://api.arkmon.caida.org"
CLIENT_ID = "arkmon-offline"

TOKEN_FILE = "/etc/ark/.arkmon-offline.token"


def get_monitors(client_id: str, url: str, token_file: str,
                 realm: str = 'CAIDA'):
    token_info = caida_oidc_client.load_token_info(token_file)
    save_tokens = caida_oidc_client.make_save_tokens(token_file)
    refresh_data = caida_oidc_client.jwt_decode(token_info['refresh_token'])
    token_url = refresh_data["iss"] + '/protocol/openid-connect/token'

    # establish a new session, refreshing access token if necessary
    with OAuth2Session(client_id=client_id,
                token=token_info,
                auto_refresh_url=token_url,
                auto_refresh_kwargs={"client_id": client_id},
                token_updater=save_tokens) as session:

        if not session.authorized:
            print("Failed to authorize session, aborting")
            return None

        try:
            response = session.request("GET", f"{url}/monitors/")
        except Exception as e:
            print(f"Failed to fetch {url}/monitors/: {e}")
            return None

        if response.status_code != 200:
            print(f"Got status {response.status_code}, aborting",
                    file=sys.stderr)
            return None

        monitors = response.json()
        if len(monitors) == 0:
            print("Empty monitors list, aborting")
            return None
        return monitors


def send_email(mon_info: dict, recipient: str):
    html_rows = []
    txt_rows = []
    items = sorted(mon_info.items(), key=lambda kv: kv[1]['uptime'],
                   reverse=True)
    for mon_id,info in items:
        uptime = info['uptime']
        td = timedelta(seconds=uptime)
        ago = f'{td.days:2} days {td.seconds / 3600:5.02f} hours'
        hardware_info = info['hwtype']
        view_url = f'https://dory.caida.org/node/view/{mon_id}'
        html_rows.append(f'<a href="{view_url}">{mon_id}</a> '
                         f'uptime {ago} '
                         f'-- {hardware_info}')
        txt_rows.append(f'{mon_id:7} uptime {ago}\n'
                        f'  {hardware_info}')
    msg = EmailMessage()
    msg['Subject'] = SUBJECT
    msg['From'] = FROM
    msg['To'] = recipient
    msg.set_content('\n'.join(txt_rows))
    br = '<br/>\n  '
    msg.add_alternative(f'''
<html>
  <head></head>
  <body>
  {br.join(html_rows)}
  </body>
</html>
    ''', subtype='html')
    s = smtplib.SMTP(MAILSERVER)
    s.send_message(msg)
    s.quit()


#====================================================================

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--token-file', default=TOKEN_FILE)
    parser.add_argument('--recipient', required=True)
    args = parser.parse_args()

    # can we lose monitors that come up at exactly the same time as the
    # script runs? Do we need overlapping time?
    day_sec = timedelta(days=1).total_seconds()
    now_sec = datetime.now().timestamp()

    all_mons = get_monitors(client_id=CLIENT_ID, url=API_URL,
                            token_file=args.token_file)
    if all_mons is None:
        sys.exit()

    # TODO figure out how to detect pre-configured nodes being booted,
    # without access to timestamped data from dory

    # Only *-zz monitors that have come up in the last day are new
    new_mons = {m['node']: m for m in all_mons
                    if m['node'].endswith('-zz')
                        and m['node'] != 'xxx-zz'
                        and m.get('lastping', 0) > (now_sec - day_sec)
                        and 0 < m.get('uptime', 0) < day_sec}

    if new_mons:
        send_email(new_mons, args.recipient)


if __name__ == "__main__":
    main()
