# Copyright 2024 The Pigweed Authors
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not
# use this file except in compliance with the License. You may obtain a copy of
# the License at
#
#     https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations under
# the License.
"""Generates the modules index table on //docs/sphinx/modules.rst."""

import json
import os
import sys

from sphinx.application import Sphinx


try:  # Bazel location for the data
    from python.runfiles import runfiles  # type: ignore

    script_dir = os.path.dirname(os.path.abspath(__file__))
    modules_file = f"{script_dir}/../PIGWEED_MODULES"
    metadata_file = f"{script_dir}/../module_metadata.json"
except ImportError:  # GN location for the data
    modules_file = f'{os.environ["PW_ROOT"]}/PIGWEED_MODULES'
    metadata_file = f'{os.environ["PW_ROOT"]}/docs/module_metadata.json'
with open(modules_file, 'r') as f:
    # The complete, authoritative list of modules.
    complete_pigweed_modules_list = f.read().splitlines()
with open(metadata_file, 'r') as f:
    # Module metadata such as supported languages and status.
    metadata = json.load(f)


def build_status_badge(status):
    """Styles the module status as a clickable badge."""
    # This default value should never get used but it's a valid
    # neutral styling in case something goes wrong and it leaks through.
    badge_type = 'info'
    if status == 'stable':
        badge_type = 'primary'
    elif status == 'unstable':
        badge_type = 'secondary'
    elif status == 'experimental':
        badge_type = 'warning'
    elif status == 'deprecated':
        badge_type = 'danger'
    else:
        msg = f'[modules_index.py] error: invalid module status ("{status}")'
        sys.exit(msg)
    # Use bdg-ref-* to make the badge link to the glossary definition for
    # "stable", "experimental", etc.
    # https://sphinx-design.readthedocs.io/en/latest/badges_buttons.html
    return f':bdg-{badge_type}:`{status}`'


def build_row(module_name: str):
    """Builds a row of data for the table. Each module gets a row."""
    ref = f':ref:`module-{module_name}`'
    if module_name not in metadata:
        return f'   "{ref}", "", "", ""\n'
    tagline = metadata[module_name].get('tagline', '')
    status = build_status_badge(metadata[module_name]['status'])
    if 'languages' in metadata[module_name]:
        languages = ', '.join(metadata[module_name]['languages'])
    else:
        languages = ''
    return f'   "{ref}", "{status}", "{tagline}", "{languages}"\n'


def generate_modules_index(_, docname: str, source: list[str]) -> None:
    """Inserts the metadata table into //docs/sphinx/modules.rst."""
    if (
        docname != 'modules'
    ):  # Only run this logic on //docs/sphinx/modules.rst.
        return
    skip = ['docker']  # Items in //PIGWEED_MODULES that should be skipped.
    # Transform //docs/sphinx/module_metadata.json into a csv-table reST directive.
    # https://docutils.sourceforge.io/docs/ref/rst/directives.html#csv-table-1
    content = '\n\n.. csv-table::\n'
    content += '   :header: "Name", "Status", "Description", "Languages"\n\n'
    # Loop through the complete, authoritative list (as opposed to the metadata)
    # to guarantee that every module is listed on the modules index page.
    for module in complete_pigweed_modules_list:
        if module in skip:
            continue
        content += build_row(module)
    # Modify the reST of //docs/sphinx/modules.rst in-place. The auto-generated table
    # is just appended to the reST source text.
    source[0] += content


def setup(app: Sphinx) -> dict[str, bool]:
    """Hooks this extension into Sphinx."""
    app.connect('source-read', generate_modules_index)
    return {
        'parallel_read_safe': True,
        'parallel_write_safe': True,
    }
