#!/usr/bin/env python3
"""Script to update the permissions of a shared GDrive folder

This script uses a service account to manage the permissions of a shared folder
in GDrive. The service account was created from this page:
https://console.developers.google.com/projectselector/iam-admin/serviceaccounts

The credentials of the service account have been saved to a private gist whose
URL has been set as an environment variable. The credentials could potentially
be directly saved as environment variables, but this seemed much more easier to
manage.

*NOTE*: Ensure that the GDrive API has been enabled in the project that this
service account is being used from.

*NOTE*: Ensure that the drive has been (manually) shared with the user of the
 service account, before trying to run this script.

"""

import datetime
import os
import sys

import click

sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from rsvp.gdrive_utils import (
    add_birthday,
    add_rsvp,
    cal_event_to_model_id,
    create_root_folder,
    create_service,
    delete_event,
    get_calendar_id,
    list_events,
    photos,
    update_birthday,
    update_calendar_sharing,
    update_permissions,
    update_rsvp,
)
from rsvp.models import Event, GDrivePhoto, User
from rsvp.utils import read_app_config
from rsvp.zulip_utils import zulip_announce_new_photos


@click.group()
@click.pass_context
def cli(ctx):
    """A CLI to manage Google Drive photos related actions"""
    ctx.obj.update(
        {
            "drive_service": create_service("drive"),
            "calendar_service": create_service("calendar"),
            "gdrive_root": os.environ["GOOGLE_DRIVE_MEDIA_DRIVE_ID"],
        }
    )


@click.command()
@click.argument("name", type=str, default="RSVP Media")
@click.pass_context
def create_drive_root_dir(ctx, name):
    service = ctx.obj["drive_service"]
    create_root_folder(service, name)


@click.command()
@click.pass_context
def sync_photos(ctx):
    service = ctx.obj["drive_service"]
    gdrive_root = ctx.obj["gdrive_root"]
    gd_photos = [GDrivePhoto(**photo) for photo in photos(service, gdrive_root)]
    if not gd_photos:
        return
    previous_photos = set(GDrivePhoto.objects.values_list("gdrive_id"))
    previous_paths = set(
        GDrivePhoto.objects.values_list("gdrive_parent", "gdrive_path")
    )
    GDrivePhoto.objects.delete()
    GDrivePhoto.objects.insert(gd_photos, load_bulk=False)
    current_photos = set(GDrivePhoto.objects.values_list("gdrive_id"))
    current_paths = set(GDrivePhoto.objects.values_list("gdrive_parent", "gdrive_path"))
    new_photos = GDrivePhoto.objects.filter(
        gdrive_id__in=(current_photos - previous_photos)
    )
    new_paths = current_paths - previous_paths
    zulip_announce_new_photos(new_paths, new_photos)


@click.command()
@click.pass_context
def sync_drive_permissions(ctx):
    service = ctx.obj["drive_service"]
    gdrive_root = ctx.obj["gdrive_root"]
    emails = User.approved_users().values_list("email")
    emails = [email.lower() for email in emails]
    update_permissions(service, gdrive_root, emails)


@click.command()
@click.pass_context
def sync_calendar_permissions(ctx):
    service = ctx.obj["calendar_service"]
    emails = User.approved_users().values_list("email")
    emails = [email.lower() for email in emails]
    update_calendar_sharing(service, emails)


@click.command()
@click.pass_context
def sync_calendar_birthdays(ctx):
    service = ctx.obj["calendar_service"]
    users = User.approved_users().filter(dob__ne=None, hide_dob__in=[None, False])
    calendarId = get_calendar_id(service)
    birthdays = {
        cal_event_to_model_id(event): event
        for event in list_events(service, calendarId)
        if event["iCalUID"].startswith("birthday:")
    }
    calendar_emails = set(birthdays.keys())
    users_by_email = {user.email: user for user in users}
    user_emails = set(users_by_email.keys())
    to_add_emails = user_emails - calendar_emails
    to_remove_emails = calendar_emails - user_emails
    to_update_emails = user_emails.intersection(calendar_emails)

    for email in to_add_emails:
        add_birthday(service, calendarId, users_by_email[email])

    for email in to_remove_emails:
        delete_event(service, calendarId, birthdays[email])

    for email in to_update_emails:
        update_birthday(service, calendarId, users_by_email[email], birthdays[email])


@click.command()
@click.pass_context
def sync_calendar_rsvp_events(ctx):
    service = ctx.obj["calendar_service"]
    today = datetime.date.today()
    app_config = read_app_config()
    timezone = app_config["TIMEZONE"]
    calendarId = get_calendar_id(service)
    upcoming_rsvps = Event.objects.filter(date__gte=today).order_by("date")

    def in_future(date_string):
        return (
            datetime.datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ").date()
            >= today
        )

    events = {
        cal_event_to_model_id(event): event
        for event in list_events(service, calendarId)
        if event["iCalUID"].startswith("rsvp:")
        and in_future(event["start"]["dateTime"])
    }
    calendar_rsvps = set(events.keys())
    rsvps_by_id = {str(rsvp.id): rsvp for rsvp in upcoming_rsvps}
    rsvp_ids = set(rsvps_by_id.keys())
    to_add_ids = rsvp_ids - calendar_rsvps
    to_remove_ids = calendar_rsvps - rsvp_ids
    to_update_ids = rsvp_ids.intersection(calendar_rsvps)

    for id_ in to_add_ids:
        add_rsvp(service, calendarId, rsvps_by_id[id_], timezone)

    for id_ in to_remove_ids:
        delete_event(service, calendarId, events[id_])

    for id_ in to_update_ids:
        update_rsvp(service, calendarId, rsvps_by_id[id_], events[id_], timezone)


cli.add_command(create_drive_root_dir)
cli.add_command(sync_photos)
cli.add_command(sync_drive_permissions)
cli.add_command(sync_calendar_permissions)
cli.add_command(sync_calendar_birthdays)
cli.add_command(sync_calendar_rsvp_events)

if __name__ == "__main__":
    cli(obj={})
