#!/usr/bin/env python3

import os
import site
import sys
import tempfile
import venv
from collections import defaultdict
from dataclasses import dataclass
from subprocess import run
from textwrap import wrap
from urllib.parse import urljoin


SCHEMAS_FILE = "schema-packages.txt"
DOC_FILE = "schemas.rst"
HEADER = """
=================
Available Schemas
=================

.. This file is autogenerated by the build-schemas-list.py script. Do not edit manually.

These are the topics that you can expect to see on Fedora's message bus,
sorted by the python package that contains their schema.
Install the corresponding python package if you want to make use of the schema
and access additional information on the message you're receiving.

In the Fedora Infrastructure, some of those topics will be prefixed by
``org.fedoraproject.stg.`` in staging and ``org.fedoraproject.prod.`` in production.
"""
DATAGREPPER_URL = "https://apps.fedoraproject.org/datagrepper/"
PREFIXES = ("org.fedoraproject.", "org.release-monitoring.")


def read_packages(schemas_filepath):
    packages = []
    with open(schemas_filepath) as schemas_file:
        for line in schemas_file:
            line = line.strip()
            if not line or line.startswith("#"):
                continue
            packages.append(line)
    packages.sort()
    return packages


@dataclass
class Schema:
    topic: str
    package: str
    doc: str


def create_venv(dirname):
    print("Creating virtualenv...")
    venv.create(dirname, with_pip=True)
    # Activate venv
    sys.prefix = sys.exec_prefix = dirname
    site.addsitepackages(None, [dirname])


def install_packages(dirname, packages):
    # Don't use pip as a library:
    # https://pip.pypa.io/en/stable/user_guide/#using-pip-from-your-program
    pip = os.path.join(dirname, "bin", "pip")
    print("Upgrading pip...")
    run([pip, "-q", "install", "--upgrade", "pip"], check=True)
    for package in packages:
        print(f"Installing {package}...")
        run([pip, "-q", "install", package], check=True)


def get_schemas():
    import pkg_resources

    schemas = defaultdict(list)
    for entry_point in pkg_resources.iter_entry_points("fedora.messages"):
        msg_cls = entry_point.load()
        if not msg_cls.topic:
            target = f"{entry_point.module_name}:{'.'.join(entry_point.attrs)}"
            if target != "fedora_messaging.message:Message":
                print(f"The {target} schema has no declared topic, skipping.")
            continue
        package_name = entry_point.dist.project_name
        doc = " ".join(wrap(msg_cls.__doc__)) if msg_cls.__doc__ else None
        schemas[package_name].append(
            Schema(topic=msg_cls.topic, package=package_name, doc=doc)
        )
    return schemas


def _is_prefixed(topic):
    return any(topic.startswith(prefix) for prefix in PREFIXES)


def _get_category(topic):
    if _is_prefixed(topic):
        index = 3
    else:
        index = 0
    return topic.split(".")[index]


def write_doc(schemas, doc_filepath):
    with open(doc_filepath, "w") as doc_file:
        doc_file.write(HEADER)
        for package_name in sorted(schemas):
            package_schemas = schemas[package_name]
            print(f"\n\n{package_name}", file=doc_file)
            print("=" * len(package_name), end="\n\n", file=doc_file)
            category = _get_category(package_schemas[0].topic)
            history_url = urljoin(DATAGREPPER_URL, f"raw?category={category}")
            print(
                f"You can view the history of `all {category} messages <{history_url}>`__ "
                "in datagrepper.\n\n",
                file=doc_file,
            )
            for schema in package_schemas:
                prod_topic = (
                    schema.topic
                    if _is_prefixed(schema.topic)
                    else f"org.fedoraproject.prod.{schema.topic}"
                )
                history_url = urljoin(DATAGREPPER_URL, f"raw?topic={prod_topic}")
                if schema.doc:
                    print(
                        f"* ``{schema.topic}``: {schema.doc.strip()} (`history <{history_url}>`__)",
                        file=doc_file,
                    )
                else:
                    print(
                        f"* ``{schema.topic}`` (`history <{history_url}>`__)",
                        file=doc_file,
                    )


def main():
    here = os.path.dirname(__file__)
    schemas_file = os.path.normpath(os.path.join(here, SCHEMAS_FILE))
    packages = read_packages(schemas_file)
    with tempfile.TemporaryDirectory() as tmpdirname:
        create_venv(tmpdirname)
        install_packages(tmpdirname, packages)
        schemas = get_schemas()
    doc_file = os.path.normpath(os.path.join(here, DOC_FILE))
    write_doc(schemas, doc_file)
    print(f"Wrote the documentation in {doc_file}")


if __name__ == "__main__":
    main()
