#!/usr/bin/env python3
import argparse
import json
import logging
import os
import sys
import unicodedata

sys.path.append(os.path.join(os.path.dirname(__file__), "..", "api"))
import zulip


def write_public_streams() -> None:
    public_streams = set()

    for stream_name in stream_names:
        # Zephyr class names are canonicalized by first applying NFKC
        # normalization and then lower-casing server-side
        canonical_cls = unicodedata.normalize("NFKC", stream_name).lower()
        if canonical_cls in [
            "security",
            "login",
            "network",
            "ops",
            "user_locate",
            "mit",
            "moof",
            "wsmonitor",
            "wg_ctl",
            "winlogger",
            "hm_ctl",
            "hm_stat",
            "zephyr_admin",
            "zephyr_ctl",
        ]:
            # These zephyr classes cannot be subscribed to by us, due
            # to MIT's Zephyr access control settings
            continue

        public_streams.add(canonical_cls)

    with open("/home/zulip/public_streams.tmp", "w") as f:
        f.write(json.dumps(list(public_streams)) + "\n")
    os.rename("/home/zulip/public_streams.tmp", "/home/zulip/public_streams")


if __name__ == "__main__":
    log_file = "/home/zulip/sync_public_streams.log"
    logger = logging.getLogger(__name__)
    log_format = "%(asctime)s: %(message)s"
    logging.basicConfig(format=log_format)
    formatter = logging.Formatter(log_format)
    logger.setLevel(logging.DEBUG)
    file_handler = logging.FileHandler(log_file)
    file_handler.setFormatter(formatter)
    logger.addHandler(file_handler)

    parser = zulip.add_default_arguments(argparse.ArgumentParser())
    options = parser.parse_args()

    zulip_client = zulip.Client(client="ZulipSyncPublicStreamsBot/0.1")
    backoff = zulip.RandomExponentialBackoff()

    while backoff.keep_going():
        try:
            res = zulip_client.register(event_types=["stream"])
            if res["result"] != "success":
                backoff.fail()
                logger.error("Error registering event queue:\n%r", res)
                continue
        except Exception:
            logger.exception("Error registering event queue:")
            continue

        backoff.succeed()
        queue_id = res["queue_id"]
        last_event_id = res["last_event_id"]
        stream_names = {stream["name"] for stream in res["streams"]}
        write_public_streams()

        while backoff.keep_going():
            try:
                res = zulip_client.get_events(queue_id=queue_id, last_event_id=last_event_id)
                if res["result"] != "success":
                    backoff.fail()
                    logger.error("Error getting events:\n%r", res)
                    if res["result"] == "error":
                        # Break out to the outer loop to re-register the event queue.
                        break
                    continue
            except Exception:
                logger.exception("Error getting events:")
                continue

            backoff.succeed()
            for event in res["events"]:
                last_event_id = max(last_event_id, event["id"])
                if event["type"] == "stream":
                    if event["op"] == "create":
                        stream_names.update(stream["name"] for stream in event["streams"])
                        write_public_streams()
                    elif event["op"] == "delete":
                        stream_names.difference_update(
                            stream["name"] for stream in event["streams"]
                        )
                        write_public_streams()