#!/usr/bin/env python3

import configparser
import json
from pprint import pprint
from zoomus import ZoomClient
import psycopg2
import psycopg2.extras
import time
import dateutil.parser
import sys
import argparse

config = configparser.ConfigParser()
config.read('zoom.ini')

client = ZoomClient(config['api']['key'], config['api']['secret'])

db_conn = psycopg2.connect(dbname=config['db']['name'], user=config['db']['user'], password=config['db']['passwd'], host="127.0.0.1")
db = db_conn.cursor(cursor_factory=psycopg2.extras.NamedTupleCursor)


def die(msg):
    print(msg, file=sys.stderr)
    sys.exit(1)


def parse_time(iso_time):
    return dateutil.parser.isoparse(iso_time)


def get_meeting(host_id, meeting_id):
    resp = client.meeting.get(host_id=host_id, id=meeting_id)
    resp.raise_for_status()
    details = json.loads(resp.content)
    if debug:
        pprint(details)
    return details


def insert_meeting(uid, meet):
    meeting_id = meet["id"]

    db.execute("""
            INSERT INTO zoom_meetings
            (meeting_id, uuid, host_uid, topic, type)
            VALUES (%s, %s, %s, %s, %s)
            RETURNING mid
        """, (
            meet['id'],
            meet['uuid'],
            uid,
            meet['topic'],
            meet['type'],
        ))
    meeting_row = db.fetchone()
    mid = meeting_row.mid

    print(f"Meeting {meeting_id}: Creating")

    if 'occurrences' in meet:
        # Recurrent meetings have a list of occurrences
        for occ in meet["occurrences"]:
            occ_id = occ['occurrence_id']
            if occ.get('status', "") == 'deleted':
                print(f"Meeting {meeting_id}.{occ_id}: Marked as deleted")
                continue
            print(f"Meeting {meeting_id}.{occ_id}: Scheduling")
            db.execute("""
                    INSERT INTO zoom_schedule
                    (mid, occurrence_id, start_time, duration)
                    VALUES (%s, %s, %s, %s)
                """, (
                    mid,
                    occ_id,
                    parse_time(occ['start_time']),
                    occ['duration'],
                ))
    elif 'start_time' in meet:
        # Other meetings usually have a starting time
        print(f"Meeting {meeting_id}: Scheduling")
        db.execute("""
                INSERT INTO zoom_schedule
                (mid, occurrence_id, start_time, duration)
                VALUES (%s, %s, %s, %s)
            """, (
                mid,
                0,
                parse_time(meet['start_time']),
                meet['duration'],
            ))

    return mid


def add_meeting(uid, meet):
    mtype = meet['type']
    if mtype == 8:
        # Recurring meetings: need to ask for the list of occurrences
        time.sleep(0.2)
        meet = get_meeting(meet['host_id'], meet['id'])

    insert_meeting(uid, meet)


def get_meetings(uid, user_id):
    page_id = 1
    num_pages = 999
    expected_rec = 0
    total_rec = 0

    while page_id <= num_pages:
        print(f"Fetching meetings for user {user_id}: page {page_id} of {num_pages}")

        resp = client.meeting.list(user_id = user_id, type = 'scheduled', page_number = page_id)
        resp.raise_for_status()

        meeting_list = json.loads(resp.content)
        if debug:
            pprint(meeting_list)

        num_pages = meeting_list['page_count']
        expected_rec = meeting_list['total_records']

        for meet in meeting_list['meetings']:
            add_meeting(uid, meet)
            total_rec += 1

        page_id += 1
        time.sleep(0.2)

    assert total_rec == expected_rec, "Unexpected number of records, probably because of race condition"


def fetch_all():
    db.execute('DELETE FROM zoom_meetings')

    db.execute("SELECT * FROM zoom_users")
    users = db.fetchall()
    for u in users:
        get_meetings(u.uid, u.user_id)

    db_conn.commit()


def fetch_user(user_email):
    db.execute("SELECT * FROM zoom_users WHERE email=%s", (user_email,))
    u = db.fetchone()
    if not u:
        die("No such user.")

    db.execute('DELETE FROM zoom_meetings WHERE host_uid=%s', (u.uid,))
    get_meetings(u.uid, u.user_id)
    db_conn.commit()


def fetch_single(user_email, meeting_id):
    db.execute("SELECT * FROM zoom_meetings WHERE meeting_id=%s", (meeting_id,))
    meeting_row = db.fetchone()

    if meeting_row:
        db.execute("SELECT * FROM zoom_users WHERE uid=%s", (meeting_row.host_uid,))
        user_row = db.fetchone()
        assert user_row
        print(f"Meeting owned by {user_row.email}")
        if user_email is not None and user_email != user_row.email:
            die("Mismatched meeting host!")

        print(f"Deleting previous records on meeting {meeting_id}")
        db.execute("DELETE FROM zoom_schedule WHERE mid=%s", (meeting_row.mid,))
        db.execute("DELETE FROM zoom_meetings WHERE mid=%s", (meeting_row.mid,))

    else:
        if not user_email:
            die("Meeting is not known yet, you need to specify --user")

        db.execute("SELECT * FROM zoom_users WHERE email=%s", (user_email,))
        user_row = db.fetchone()
        if user_row is None:
            die(f"E-mail {user_email} not found")

    meet = get_meeting(user_row.user_id, meeting_id)
    insert_meeting(user_row.uid, meet)

    db_conn.commit()


argp = argparse.ArgumentParser(description="Fetch meeting data from Zoom API")
argp.add_argument('--all', action='store_const', const=True, default=False, help="re-fetch all meetings")
argp.add_argument('--user', metavar='EMAIL', help='fetch meetings hosted by a given user')
argp.add_argument('--meeting', type=int, metavar='ID', help='fetch a single meeting (requires --user unless the meeting is already known)')
argp.add_argument('--debug', action='store_const', const=True, default=False, help="enable debugging dumps")

args = argp.parse_args()
debug = args.debug

if args.all:
    fetch_all()
elif args.meeting is not None:
    fetch_single(args.user, args.meeting)
elif args.user is not None:
    fetch_user(args.user)
else:
    print("Nothing to do.")